ZIP Deploy and Run From Package¶
Use ZIP Deploy when you already have a prepared deployment artifact and want to push it directly to App Service without introducing a full CI/CD system. For production workloads, pair ZIP Deploy with immutable packages and explicit validation.
Main Content¶
ZIP Deploy Flow¶
flowchart TD
A[Build app artifact locally or in CI] --> B[Create ZIP package without parent folder]
B --> C[az webapp deploy --type zip]
C --> D{WEBSITE_RUN_FROM_PACKAGE enabled?}
D -- Yes --> E[Package mounted read-only]
D -- No --> F[Files extracted into wwwroot]
E --> G[Restart app]
F --> G
G --> H[Run smoke test] When ZIP Deploy Fits Best¶
- You already have a tested ZIP artifact from another pipeline.
- You want a direct CLI-driven deployment method.
- You need deterministic package promotion without rebuilding in App Service.
- You want to combine artifact deployment with deployment slots.
Package layout matters
The ZIP file must contain the application files at the archive root. Do not wrap everything in an extra top-level directory, or App Service can fail to detect and run the app correctly.
ZIP Deploy vs az webapp up¶
| Command | Best For | What It Does | Trade-Off |
|---|---|---|---|
az webapp deploy --type zip | Existing app and prebuilt artifact | Pushes a prepared ZIP package to the app by using the publish API | You manage packaging, app creation, and runtime settings yourself |
az webapp up | Quick starts and prototypes | Creates resources and deploys source code in one workflow | Less explicit, less repeatable for mature production release pipelines |
Use az webapp up for first-time experimentation or tutorials. Use ZIP Deploy when the app already exists and you want explicit control over the artifact, restart timing, and release flow.
Enable WEBSITE_RUN_FROM_PACKAGE¶
WEBSITE_RUN_FROM_PACKAGE=1 tells App Service to run from a mounted package instead of relying on mutable file copies in wwwroot. This improves consistency and reduces partial-copy or file-lock issues during deployment.
Linux runtime-specific support
For Linux code apps, Run From Package is supported for Node.js and .NET, but not for Python apps or Java apps on Linux. Use standard ZIP deploy for unsupported Linux runtimes.
az webapp config appsettings set \
--resource-group $RG \
--name $APP_NAME \
--settings WEBSITE_RUN_FROM_PACKAGE=1 SCM_DO_BUILD_DURING_DEPLOYMENT=false \
--output json
| Command/Parameter | Purpose |
|---|---|
az webapp config appsettings set | Updates App Service application settings on the target app. |
--resource-group $RG | Selects the resource group that contains the web app. |
--name $APP_NAME | Selects the web app to configure. |
--settings | Supplies one or more app settings to write in the same operation. |
WEBSITE_RUN_FROM_PACKAGE=1 | Tells App Service to mount the deployed ZIP package as read-only site content. |
SCM_DO_BUILD_DURING_DEPLOYMENT=false | Disables Kudu build automation because the artifact is already built. |
--output json | Returns JSON output that is easy to inspect or reuse in scripts. |
Read-only content root
When you run from package, application code should not try to write into the deployment directory. Store uploads, generated files, caches, and session state in external services or persistent storage instead.
Portal view: Environment variables blade¶

The az webapp config appsettings set command above writes directly into this blade, which is the same surface the Portal exposes for manual edits. Notice there is no WEBSITE_RUN_FROM_PACKAGE row yet - this is the pre-state before enabling run-from-package, and after the CLI runs it appears here with Source App Service just like the existing entries. Treat this blade as a read-only audit view in CI-driven deployments rather than the primary editing surface, because manual Portal edits drift from the configuration that ZIP Deploy expects. The Deployment slot setting column matters when you promote to production - if a slot-specific value is required for staging-only configuration, mark it sticky from the CLI or this column instead of relying on the default empty state.
Complete ZIP Deploy Example¶
zip -r ./artifacts/webapp.zip .
az webapp deploy \
--resource-group $RG \
--name $APP_NAME \
--src-path ./artifacts/webapp.zip \
--type zip \
--clean true \
--restart true \
--output json
curl --silent --show-error --fail \
"https://$APP_NAME.azurewebsites.net/health"
| Command/Parameter | Purpose |
|---|---|
zip | Creates a ZIP archive that can be uploaded to App Service. |
-r | Recursively includes files and directories in the archive. |
./artifacts/webapp.zip | Sets the output ZIP file path for the deployment artifact. |
. | Uses the current directory as the archive source. |
az webapp deploy | Uploads the ZIP package to the App Service deployment endpoint. |
--resource-group $RG | Targets the resource group that owns the app. |
--name $APP_NAME | Targets the specific web app to deploy. |
--src-path ./artifacts/webapp.zip | Points the deployment command to the local ZIP artifact. |
--type zip | Declares that the uploaded artifact format is ZIP. |
--clean true | Removes existing deployed files before applying the new package. |
--restart true | Restarts the app so the new deployment is loaded immediately. |
--output json | Returns structured deployment output for validation or automation. |
curl | Sends an HTTP request to verify the deployed app responds successfully. |
--silent | Hides normal progress output. |
--show-error | Prints an error message if the request fails. |
--fail | Makes the command exit nonzero on HTTP error responses. |
"https://$APP_NAME.azurewebsites.net/health" | Calls the health endpoint on the production site. |
Deploy to a Slot Instead of Production¶
az webapp deploy \
--resource-group $RG \
--name $APP_NAME \
--slot staging \
--src-path ./artifacts/webapp.zip \
--type zip \
--restart true \
--output json
| Command/Parameter | Purpose |
|---|---|
az webapp deploy | Uploads the ZIP package to App Service. |
--resource-group $RG | Targets the resource group that contains the app. |
--name $APP_NAME | Selects the parent web app. |
--slot staging | Deploys to the staging slot instead of production. |
--src-path ./artifacts/webapp.zip | Uses the prepared ZIP artifact as the deployment source. |
--type zip | Declares the artifact type as ZIP. |
--restart true | Restarts the slot after deployment. |
--output json | Returns structured command output for verification. |
Preferred production pattern
ZIP Deploy becomes much safer when you deploy to a staging slot first, validate health and smoke tests, and then promote by swap. See Slots and Swap.
Advanced Topics¶
ZIP Deploy Design Guidance¶
- Prefer prebuilt artifacts from CI over ad hoc local builds.
- Use
WEBSITE_RUN_FROM_PACKAGEfor more predictable production behavior where the runtime supports it. - Keep the package small and remove build-only files where possible.
- If the app requires build automation, document why
SCM_DO_BUILD_DURING_DEPLOYMENT=trueis acceptable.
Verification Commands¶
az webapp show \
--resource-group $RG \
--name $APP_NAME \
--query "{state:state,host:defaultHostName}" \
--output json
az webapp config appsettings list \
--resource-group $RG \
--name $APP_NAME \
--query "[?name=='WEBSITE_RUN_FROM_PACKAGE' || name=='SCM_DO_BUILD_DURING_DEPLOYMENT']" \
--output table
| Command/Parameter | Purpose |
|---|---|
az webapp show | Retrieves core metadata about the web app. |
--resource-group $RG | Targets the resource group that owns the app. |
--name $APP_NAME | Selects the web app to inspect. |
--query "{state:state,host:defaultHostName}" | Narrows the output to app state and default hostname. |
--output json | Formats the result as JSON. |
az webapp config appsettings list | Lists current app settings for the web app. |
--query "[?name=='WEBSITE_RUN_FROM_PACKAGE' || name=='SCM_DO_BUILD_DURING_DEPLOYMENT']" | Filters the settings list to the deployment-related keys only. |
--output table | Formats the filtered settings in a readable table. |