How to Fix: Azure Function App Cold Start Timeout

AzureColdStartTimeoutHigh severity

The Scenario

You deploy a new version of a Node.js Azure Function App on a Friday afternoon. The first request after a period of inactivity (e.g., 20 minutes with zero traffic) takes over 30 seconds to respond and returns a 503 or 504. Subsequent requests are fast. This only happens on the Consumption or Premium plan, not on a Dedicated App Service Plan.

Symptoms

Root Cause

Azure Functions on Consumption/Premium plans unload the host process after a period of inactivity (default ~20 minutes). On the next request, the platform must:

  1. Allocate a new VM/container.
  2. Download the function app package (often a large zip file stored in a storage account).
  3. Unzip and load the runtime, dependencies, and your code.

If step 2 is slow (large package, slow storage account, or network contention), the cold start exceeds the default function timeout (5 minutes for Consumption, 30 minutes for Premium) or the client’s HTTP timeout (typically 230 seconds for Azure Front Door/API Management). The combination of a large node_modules folder and a slow storage account is the most common trigger.

Resolution (Step-by-Step)

  1. Reduce deployment package size.

    # Exclude unnecessary files from the deployment package
    # Create a .funcignore file in your function app root:
    echo "*.git" >> .funcignore
    echo ".gitignore" >> .funcignore
    echo "node_modules/.cache" >> .funcignore
    echo "test" >> .funcignore
    echo "*.map" >> .funcignore
  2. Enable WEBSITE_RUN_FROM_PACKAGE with a remote URL (faster download).

    # Instead of uploading the zip to the function app, upload to Azure Blob Storage
    # and set the app setting to the SAS URL.
    az functionapp config appsettings set --name <func-app-name> \
      --resource-group <rg> \
      --settings "WEBSITE_RUN_FROM_PACKAGE=https://<storage-account>.blob.core.windows.net/<container>/<function-app>.zip?<SAS-token>"
  3. Optimize dependency loading (Node.js specific).

    # Use npm's --production flag to skip dev dependencies in the build step
    npm install --production
    # Consider using a bundler like esbuild to tree-shake and minify code
    # This can reduce the package size by 60-80%.
  4. Switch to Premium plan with pre-warmed instances (if budget allows).

    az functionapp plan create --name <premium-plan> --resource-group <rg> \
      --sku EP1 --is-linux true
    az functionapp update --name <func-app-name> --resource-group <rg> \
      --plan <premium-plan>
    # Set minimum number of always-ready instances
    az functionapp config appsettings set --name <func-app-name> \
      --resource-group <rg> \
      --settings "WEBSITE_PREWARMED_INSTANCE_COUNT=2"
  5. Increase the function timeout (last resort).

    # In host.json
    {
      "functionTimeout": "00:10:00"
    }

Why This Sometimes Doesn’t Work

If the package is still >200MB after optimization, the download from Blob Storage can still be slow due to network latency between the storage account and the function app region. The fix is to ensure the storage account is in the same region as the function app. Also, if you’re using a Shared Access Signature (SAS) token that expires, the cold start will fail with an authentication error, not a timeout — check the function app logs for 403 errors instead of 504.

Verification

# Trigger a cold start by scaling to zero (if possible) or waiting 20+ minutes
# Then invoke the function
curl -w "Total time: %{time_total}s\n" -o /dev/null -s https://<func-app-name>.azurewebsites.net/api/HttpTrigger1

# Expected output: Total time: < 5 seconds (ideally < 2s)
# Check Application Insights for the specific invocation duration.

Common Follow-up Questions

Q: Can I use Azure Front Door to keep the function warm? A: Yes, but Front Door’s health probes use a different endpoint (e.g., /api/health) and may not prevent cold starts for your main function. A better approach is to use a timer-triggered function that calls your HTTP function every 5 minutes.

Q: Does the Elastic Premium plan completely eliminate cold starts? A: No, but it reduces them significantly. With WEBSITE_PREWARMED_INSTANCE_COUNT set to 1 or 2, the platform keeps that many instances always running. A cold start still happens if traffic exceeds the pre-warmed count.

Q: Is there a way to see the exact cold start duration in logs? A: Yes. In Application Insights, query for requests | where cloud_RoleName == "<func-app-name>" | order by timestamp desc | take 10. The first request after a long gap will show a duration that includes the platform cold start time.

Prevent This in the Future

Proactive monitoring of function app invocation durations and package sizes can catch a growing cold start problem before it causes production outages. Teams use Better Stack to set up synthetic monitors that hit the function on a schedule, tracking response times and alerting when cold starts exceed a threshold. This catches regressions introduced by a bloated dependency update or a misconfigured deployment pipeline. Set up uptime monitoring and alerts with Better Stack