Minimum Replica Count Zero Floor: Cold Start After Idle Scale-Down¶
Status: Published
Experiment completed with real data on 2026-05-04.
1. Question¶
When a Container Apps revision has minReplicas: 0, the app scales to zero after idle and incurs a cold start on the next request. In the Consumption plan, what is the typical cold start duration for a standard Python or Node.js container, and how does this vary with container image size, resource allocation, and the presence of init containers?
2. Why this matters¶
Scale-to-zero is the primary cost optimization for Container Apps Consumption plan. However, the cold start penalty affects user-perceived latency for the first request after an idle period. Teams often set minReplicas: 0 without understanding the cold start cost. Understanding the breakdown of cold start time (image pull, container initialization, app startup) is essential for making informed decisions about minReplicas configuration and for setting realistic timeout expectations for clients.
3. Customer symptom¶
"The first request after a period of no traffic takes 30 seconds — is that normal?" or "Clients are timing out on the first request after deployment" or "We need to know how long cold start takes to set our client timeout correctly."
4. Hypothesis¶
- H1: Cold start duration for a Container Apps revision consists of: (a) scale-from-zero trigger time (KEDA detecting the request), (b) image pull time (or cache hit), (c) container initialization (entrypoint to ready), (d) readiness probe pass delay.
- H2: A Python 3.11 slim container (200MB) with no init containers cold starts in approximately 5-15 seconds on Consumption plan in Korea Central, depending on image cache status.
- H3: A large container image (2GB, e.g., ML model container) significantly increases cold start time due to image pull, even with layer caching.
- H4: Adding an init container that performs slow initialization (e.g., downloads model weights) adds directly to the cold start time.
5. Environment¶
| Parameter | Value |
|---|---|
| Service | Azure Container Apps |
| SKU / Plan | Consumption |
| Region | Korea Central |
| Runtime | Python 3.11 (containerapps-helloworld) |
| Environment | env-batch-lab |
| App name | aca-diag-batch |
| Date tested | 2026-05-04 |
6. Variables¶
Experiment type: Performance / Platform behavior
Controlled:
- Container App:
aca-diag-batchwithminReplicas: 0,maxReplicas: 3 - Image:
mcr.microsoft.com/azuredocs/containerapps-helloworld:latest - Region: Korea Central (Consumption plan)
Observed:
- Time from request initiation to HTTP 200 response (client-side measurement via
curl) - Replica count before and after request
- Warm request latency for subsequent requests
Scenarios:
- S1: All revisions at 0 replicas (scaled to zero) → first request cold start
- S2: Second and third request immediately after cold start (warm)
7. Instrumentation¶
- Client-side
curltimer withdate +%s%3N(millisecond precision) az containerapp revision listto confirm replica count before/after- Consecutive requests to measure warm vs. cold latency delta
8. Procedure¶
- Confirm all active revisions at 0 replicas:
az containerapp revision list --query "[?properties.active].{name,replicas:properties.replicas}". - Confirm
minReplicas: 0withaz containerapp update --min-replicas 0. - Send first request and measure time:
START=$(date +%s%3N); curl -s -o /dev/null -w "%{http_code}" <url>; END=$(date +%s%3N); echo $((END-START))ms. - Immediately send second and third requests. Record latencies.
- Check replica count after requests to confirm scale-up.
9. Expected signal¶
- First request: 5–15 seconds cold start latency.
- Second request: <200ms (warm, replica already running).
- Replica count jumps from 0 → 1 after first request.
10. Results¶
Pre-experiment state: all replicas at 0¶
```json [ {"name": "aca-diag-batch--0000001", "replicas": 0}, {"name": "aca-diag-batch--0000002", "replicas": 0}, {"name": "aca-diag-batch--0000003", "replicas": 0} ]
```text
Cold start and warm request measurements¶
```text First request (cold start): HTTP 200 — latency: 21,627 ms (21.6 seconds) Second request (warm): HTTP 200 — latency: 124 ms Third request (warm): HTTP 200 — latency: 72 ms
```text
Replica count after scale-up¶
```json [ {"name": "aca-diag-batch--0000001", "replicas": 1}, {"name": "aca-diag-batch--0000002", "replicas": 0}, {"name": "aca-diag-batch--0000003", "replicas": 0} ]
``text
Only revision--0000001` (the traffic-weighted revision) scaled up to 1. Non-traffic revisions remained at 0.
11. Interpretation¶
- Measured: Cold start latency was 21.6 seconds — above the H2 estimate of 5–15 seconds. The Microsoft Container Apps Hello World image is small (~30MB), so the dominant factor is likely the KEDA scale-from-zero trigger delay and platform-level provisioning overhead, not image pull time.
- Measured: Warm latency is 72–124ms, a ~300× reduction from cold start latency. Once the replica is provisioned, the app serves requests normally.
- Observed: Only the revision with active traffic weight scales up on demand. Zero-traffic revisions remain at 0 replicas indefinitely, even when the environment receives requests.
- Inferred: The 21.6s cold start includes: KEDA trigger detection, node scheduling, image pull (or cache hit), container startup, and readiness probe pass. Exact breakdown requires
ContainerAppSystemLogstimestamp analysis. - Inferred: H2 is partially confirmed — a small image cold starts without dramatic pull overhead — but the total time (21.6s) exceeds the 5–15s estimate. Korea Central Consumption plan timing may differ from other regions.
12. What this proves¶
- Cold start from scale-to-zero takes approximately 21 seconds for a small image (30MB) on Korea Central Consumption plan. Measured.
- Warm request latency is 72–124ms — cold start is a ~300× latency increase. Measured.
- Only the traffic-weighted revision scales up on demand; zero-traffic revisions stay at 0 replicas. Observed.
- Client timeout must exceed 21 seconds to survive a cold start event. Measured.
13. What this does NOT prove¶
- This experiment used a single small image. Cold start for large images (1–2GB) was not measured; image pull time would add significant overhead.
- Init container impact was not tested. A 10-second init container would add directly to the 21.6s total.
- Multiple simultaneous cold start requests were not tested. Queue buildup during scale-from-zero may cause some requests to time out.
- Behavior with
minReplicas: 1was not measured here (expected: no cold start, <200ms first request).
14. Support takeaway¶
When customers report 20–30 second first-request latency after a period of inactivity:
- Check
minReplicason all active revisions — if 0, cold start is expected behavior. - Advise that cold start on Korea Central Consumption plan is approximately 20 seconds for small images; larger images will be slower.
- Mitigation options: (a) set
minReplicas: 1to eliminate cold start at ~$13/month cost for a 0.25 vCPU / 0.5 GiB replica; (b) implement a periodic "keep-warm" ping (not officially supported but widely used); (c) configure client timeout ≥ 30 seconds. - If
minReplicas: 0is required for cost reasons, set client HTTP timeout to at least 30 seconds and consider retry logic for the first request.
15. Reproduction notes¶
```bash RG="rg-lab-aca-batch" APP="aca-diag-batch" URL="https://aca-diag-batch.agreeablehill-e6bfb5a7.koreacentral.azurecontainerapps.io"
Ensure scale-to-zero¶
az containerapp update -n $APP -g $RG --min-replicas 0 --max-replicas 3
Verify replicas at 0 (wait 5+ minutes after last request)¶
az containerapp revision list -n $APP -g $RG \ --query "[?properties.active].{name:name,replicas:properties.replicas}" -o json
Measure cold start¶
START=$(date +%s%3N) HTTP=$(curl -s -o /dev/null -w "%{http_code}" --max-time 60 "${URL}/") END=$(date +%s%3N) echo "Cold start: HTTP ${HTTP}, latency: $((END-START))ms"
Measure warm requests¶
for i in 1 2; do START=$(date +%s%3N) curl -s -o /dev/null -w "%{http_code}" --max-time 15 "${URL}/" END=$(date +%s%3N) echo "Warm request $i: $((END-START))ms" done
``text
- Replicas reach 0 after approximately 5 minutes of no traffic on Consumption plan.
- Use--max-time 60` on cold start curl to avoid client-side timeout before the replica is ready.
- Image pull time depends on whether layers are cached at the node level.