Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
This article shows you how to configure a custom container to run on Azure App Service.
Learn about key concepts and get instructions for containerization of Windows apps in App Service. New users should first follow the custom container quickstart and tutorial.
Learn about key concepts and get instructions for containerization of Linux apps in App Service. New users should first follow the custom container quickstart and tutorial. For sidecar containers, see Tutorial: Configure a sidecar container for custom container in Azure App Service.
Note
Using a service principal for Windows container image pull authentication is no longer supported. We recommend that you use managed identity for both Windows and Linux containers.
Supported parent images
Select the right parent image (base image) for the framework you want for your custom Windows image:
- To deploy .NET Framework apps, use a parent image based on the Windows Server 2019 Core Long-Term Servicing Channel release.
- To deploy .NET Core apps, use a parent image based on the Windows Server 2019 Nano Annual Channel release.
It takes some time to download a parent image during app startup. You can reduce startup time by using one of the following parent images that are already cached in Azure App Service:
- mcr.microsoft.com/windows/servercore:ltsc2022
- mcr.microsoft.com/windows/servercore:ltsc2019
- mcr.microsoft.com/dotnet/framework/aspnet:
4.8-windowsservercore-ltsc2022 - mcr.microsoft.com/dotnet/framework/aspnet:
4.8-windowsservercore-ltsc2019 - mcr.microsoft.com/dotnet/runtime:
6.0-nanoserver-ltsc2022 - mcr.microsoft.com/dotnet/runtime:
6.0-nanoserver-1809 - mcr.microsoft.com/dotnet/aspnet:
6.0-nanoserver-ltsc2022 - mcr.microsoft.com/dotnet/aspnet:
6.0-nanoserver-1809
Change the Docker image of a custom container
Use the following command to change the current Docker image to a new image in an existing custom container:
az webapp config container set --name <app-name> --resource-group <group-name> --docker-custom-image-name <docker-hub-repo>/<image>
Use an image from a private registry
To use an image from a private registry, such as Azure Container Registry, run the following command:
az webapp config container set --name <app-name> --resource-group <group-name> --docker-custom-image-name <image-name> --docker-registry-server-url <private-repo-url> --docker-registry-server-user <username> --docker-registry-server-password <password>
Supply the sign-in credentials for your private registry account in the \<username> and \<password> fields.
Use managed identity to pull an image from Azure Container Registry
Use the following steps to configure your web app to pull from Azure Container Registry by using managed identity. The steps use system-assigned managed identity, but you can also use user-assigned managed identity.
Enable the system-assigned managed identity for the web app by using the
az webapp identity assigncommand:az webapp identity assign --resource-group <group-name> --name <app-name> --query principalId --output tsvReplace <app-name> with the name you used in the previous step. The output of the command, filtered by the
--queryand--outputarguments, is the service principal ID of the assigned identity.Get the resource ID of your container registry:
az acr show --resource-group <group-name> --name <registry-name> --query id --output tsvReplace <registry-name> with the name of your registry. The output of the command, filtered by the
--queryand--outputarguments, is the resource ID of the container registry.Grant the managed identity permission to access the container registry:
az role assignment create --assignee <principal-id> --scope <registry-resource-id> --role "AcrPull"Replace the following values:
- <principal-id> with the service principal ID from the
az webapp identity assigncommand - <registry-resource-id> with the ID of your container registry from the
az acr showcommand
For more information about these permissions, see What is Azure role-based access control?.
- <principal-id> with the service principal ID from the
Configure your app to use the managed identity to pull from Azure Container Registry.
az webapp config set --resource-group <group-name> --name <app-name> --generic-configurations '{"acrUseManagedIdentityCreds": true}'Replace the following values:
- <app-name> with the name of your web app.
Tip
If you use PowerShell console to run the commands, escape the strings in the
--generic-configurationsargument in this step and the next step. For example:--generic-configurations '{\"acrUseManagedIdentityCreds\": true'.(Optional) If your app uses a user-assigned managed identity, make sure the identity is configured on the web app and then set the
acrUserManagedIdentityIDproperty to specify its client ID:az identity show --resource-group <group-name> --name <identity-name> --query clientId --output tsvReplace the
<identity-name>of your user-assigned managed identity and use the output<client-id>to configure the user-assigned managed identity ID.az webapp config set --resource-group <group-name> --name <app-name> --generic-configurations '{"acrUserManagedIdentityID": "<client-id>"}'
The web app now uses managed identity to pull from Azure Container Registry.
Use an image from a network-protected registry
To connect and pull from a registry inside a virtual network or on-premises, your app must integrate with a virtual network. You also need virtual network integration for Azure Container Registry with a private endpoint. After you configure your network and DNS resolution, enable the routing of the image pull through the virtual network. Configure the vnetImagePullEnabled site setting:
az resource update --resource-group <group-name> --name <app-name> --resource-type "Microsoft.Web/sites" --set properties.vnetImagePullEnabled [true|false]
Troubleshoot what to do if you don't see the updated container
If you change your Docker container settings to point to a new container, it might take a few minutes before the app serves HTTP requests from the new container. While the new container is pulled and started, App Service continues to serve requests from the old container. App Service only sends requests to the new container after it starts and is ready to receive requests.
Learn how container images are stored
The first time you run a custom Docker image in App Service, App Service performs the docker pull command and pulls all image layers. The layers are stored on disk, the same as when you use Docker on-premises. Each time the app restarts, App Service performs the docker pull command. It pulls only changed layers. If there are no changes, App Service uses existing layers on the local disk.
If the app changes compute instances for any reason (like changing pricing tiers), App Service must again pull all layers. The same is true if you scale out to add more instances. Also, in rare cases, the app instances might change without a scale operation.
Configure port number
By default, App Service assumes your custom container listens on port 80. If your container listens to a different port, set the WEBSITES_PORT app setting in your App Service app. You can set it by using Azure Cloud Shell. In Bash, use the following command:
az webapp config appsettings set --resource-group <group-name> --name <app-name> --settings WEBSITES_PORT=8000
In PowerShell, use the following command:
Set-AzWebApp -ResourceGroupName <group-name> -Name <app-name> -AppSettings @{"WEBSITES_PORT"="8000"}
App Service currently allows your container to expose only one port for HTTP requests.
Configure environment variables
Your custom container might use environment variables that you need to supply externally. You can pass them in by using Cloud Shell. In Bash, use the following command:
az webapp config appsettings set --resource-group <group-name> --name <app-name> --settings DB_HOST="myownserver.mysql.database.azure.com"
In PowerShell, use the following command:
Set-AzWebApp -ResourceGroupName <group-name> -Name <app-name> -AppSettings @{"DB_HOST"="myownserver.mysql.database.azure.com"}
When your app runs, the App Service app settings are automatically injected into the process as environment variables. You can verify container environment variables with the URL https://<app-name>.scm.azurewebsites.net/Env.
When you SSH into a container with custom Docker images, you might see only a few environment variables if you try to use commands like env or printenv. To see all environment variables within the container, like ones you pass in to your application for runtime usage, add this line to your entrypoint script:
eval $(printenv | sed -n "s/^\([^=]\+\)=\(.*\)$/export \1=\2/p" | sed 's/"/\\\"/g' | sed '/=/s//="/' | sed 's/$/"/' >> /etc/profile)
See a full example.
If your app uses images from a private registry or from Docker Hub, the credentials for accessing the repository are saved in environment variables: DOCKER_REGISTRY_SERVER_URL, DOCKER_REGISTRY_SERVER_USERNAME, and DOCKER_REGISTRY_SERVER_PASSWORD. Because of security risks, none of these reserved variable names are exposed to the application.
For Internet Information Services (IIS) or .NET Framework (4.0 or later) containers, credentials are automatically injected into System.ConfigurationManager as .NET app settings and connection strings by App Service. For all other languages or frameworks, they're provided as environment variables for the process, with one of the following prefixes:
APPSETTING_SQLCONTR_MYSQLCONTR_SQLAZURECOSTR_POSTGRESQLCONTR_CUSTOMCONNSTR_
You can use this method for both single-container or multi-container apps, where the environment variables are specified in the docker-compose.yml file.
Use persistent shared storage
You can use the C:\home directory in your custom container file system to persist files across restarts and share them across instances. When you use the C:\home directory, your custom container can access persistent storage.
When persistent storage is disabled, writes to the C:\home directory aren't persisted across app restarts or across multiple instances. When persistent storage is enabled, all writes to the C:\home directory persist. All instances of a scaled-out app can access them. When the container starts, if any files are present on the persistent storage, they overwrite any contents in the C:\home directory of the container.
The only exception is the C:\home\LogFiles directory. This directory stores the container and application logs. The folder always persists upon app restarts if application logging is enabled with the File System option, whether or not persistent storage is enabled. In other words, when you enable or disable persistent storage, it doesn't affect application logging behavior.
By default, persistent storage is enabled on Windows custom containers. To disable it, set the WEBSITES_ENABLE_APP_SERVICE_STORAGE app setting value to false by using Cloud Shell. In Bash, use the following command:
az webapp config appsettings set --resource-group <group-name> --name <app-name> --settings WEBSITES_ENABLE_APP_SERVICE_STORAGE=false
In PowerShell, use the following command:
Set-AzWebApp -ResourceGroupName <group-name> -Name <app-name> -AppSettings @{"WEBSITES_ENABLE_APP_SERVICE_STORAGE"=false}
You can use the /home directory in your custom container file system to persist files across restarts and share them across instances. When you use the C:\home directory, your custom container can access persistent storage. Keep in mind that data that you save within /home contributes to the storage space quota included with your App Service plan.
When persistent storage is disabled, writes to the C:\home directory aren't persisted across app restarts or across multiple instances. When persistent storage is enabled, all writes to the C:\home directory persist. All instances of a scaled-out app can access them. When the container starts, if any files are present on the persistent storage, they overwrite any contents in the C:\home directory of the container.
The only exception is the C:\home\LogFiles directory. This directory stores the container and application logs. The folder always persists upon app restarts if application logging is enabled with the File System option, whether or not persistent storage is enabled. In other words, when you enable or disable persistent storage, it doesn't affect application logging behavior.
We recommend that you write data to /home or a mounted Azure storage path. Data that you write outside these paths isn't persistent during restarts. The data is saved to platform-managed host disk space separate from the App Service plans file storage quota.
By default, persistent storage is disabled on Linux custom containers. To enable it, set the WEBSITES_ENABLE_APP_SERVICE_STORAGE app setting value to true by using Cloud Shell. In Bash, use the following command:
az webapp config appsettings set --resource-group <group-name> --name <app-name> --settings WEBSITES_ENABLE_APP_SERVICE_STORAGE=true
In PowerShell, use the following command:
Set-AzWebApp -ResourceGroupName <group-name> -Name <app-name> -AppSettings @{"WEBSITES_ENABLE_APP_SERVICE_STORAGE"=true}
Note
You can also configure your own persistent storage.
Detect HTTPS session
App Service terminates TLS at the front ends. That means that TLS requests never get to your app. You don't need to, and shouldn't, implement any support for TLS into your app.
The front ends are located inside Azure datacenters. If you use TLS with your app, your traffic across the internet is always safely encrypted.
Customize ASP.NET machine key injection
During the container start, keys are automatically generated and injected into the container as the machine keys for ASP.NET cryptographic routines. You can find these keys in your container by looking for the following environment variables: MACHINEKEY_Decryption, MACHINEKEY_DecryptionKey, MACHINEKEY_ValidationKey, and MACHINEKEY_Validation.
The new keys at each restart might reset ASP.NET forms authentication and view state, if your app depends on them. To prevent the automatic regeneration of keys, set them manually as App Service app settings.
Connect to the container
To connect to your Windows container directly for diagnostic tasks, go to https://<app-name>.scm.azurewebsites.net/ and select the SSH option. This option establishes a direct SSH session in which you can run commands inside your container.
- It functions separately from the graphical browser above it, which only shows the files in your shared storage.
- In a scaled-out app, the SSH session connects to one of the container instances. You can select a different instance from the Instance dropdown list in the top Kudu menu.
- Except for changes in the shared storage, any change you make to the container from within the SSH session doesn't persist when your app restarts. These changes aren't part of the Docker image. To persist changes like registry settings and software installation, make them part of the Dockerfile.
Access diagnostic logs
App Service logs actions by the Docker host and activities from within the container. Logs from the Docker host (platform logs) are enabled by default. You need to manually enable application logs or web server logs from within the container. For more information, see Enable application logging and Enable web server logging.
You can access Docker logs in several ways:
Azure portal
Docker logs are displayed in the Azure portal in the Container Settings pane of your app. The logs are truncated. To download all the logs, select Download.
Kudu
To see the individual log files, go to https://<app-name>.scm.azurewebsites.net/DebugConsole and select the LogFiles folder. To download the entire LogFiles directory, select the Download icon to the left of the directory name. You can also access this folder by using an FTP client.
By default, you can't access the C:\home\LogFiles folder in the SSH terminal because persistent shared storage isn't enabled. To enable this behavior in the console terminal, enable persistent shared storage.
If you try to download the Docker log that's currently in use by using an FTP client, you might get an error because of a file lock.
Kudu API
Go directly to https://<app-name>.scm.azurewebsites.net/api/logs/docker to see metadata for the Docker logs. You might see more than one log file listed. You can use the href property to directly download the log file.
To download all the logs together in one ZIP file, access https://<app-name>.scm.azurewebsites.net/api/logs/docker/zip.
Customize container memory
By default, all Windows containers deployed in Azure App Service have a memory limit configured. The following table lists the default settings per App Service plan SKU.
| App Service plan SKU | Default memory limit per app (in MB) |
|---|---|
| P1v3 | 1024 |
| P1Mv3 | 1024 |
| P2v3 | 1536 |
| P2Mv3 | 1536 |
| P3v3 | 2048 |
| P3Mv3 | 2048 |
| P4Mv3 | 2560 |
| P5Mv3 | 3072 |
You can change this value by providing the WEBSITE_MEMORY_LIMIT_MB app setting in Cloud Shell. In Bash, use the following command:
az webapp config appsettings set --resource-group <group-name> --name <app-name> --settings WEBSITE_MEMORY_LIMIT_MB=2000
In PowerShell, use the following command:
Set-AzWebApp -ResourceGroupName <group-name> -Name <app-name> -AppSettings @{"WEBSITE_MEMORY_LIMIT_MB"=2000}
The value is defined in megabytes (MB) and must be less and equal to the total physical memory of the host. For example, in an App Service plan with 8 GB of RAM, the cumulative total of WEBSITE_MEMORY_LIMIT_MB for all the apps can't exceed 8 GB. For more information on how much memory is available, see the Premium v3 service plan in App Service pricing.
Customize the number of compute cores
By default, a Windows container runs with all available cores for your pricing tier. You might want to reduce the number of cores that your staging slot uses. To reduce the number of cores that a container uses, set the WEBSITE_CPU_CORES_LIMIT app setting to the preferred number of cores. You can set it by using Cloud Shell. In Bash, use the following command:
az webapp config appsettings set --resource-group <group-name> --name <app-name> --slot staging --settings WEBSITE_CPU_CORES_LIMIT=1
In PowerShell, use the following command:
Set-AzWebApp -ResourceGroupName <group-name> -Name <app-name> -AppSettings @{"WEBSITE_CPU_CORES_LIMIT"=1}
Tip
Updating the app setting triggers automatic restart, which causes minimal downtime. For a production app, consider swapping it into a staging slot. Change the app setting in the staging slot, and then swap it back into production.
To verify your adjusted number, open an SSH session by using the Azure portal or the Kudu portal (https://<app-name>.scm.azurewebsites.net/webssh/host). Enter the following commands by using PowerShell. Each command returns a number.
Get-ComputerInfo | ft CsNumberOfLogicalProcessors # Total number of enabled logical processors. Disabled processors are excluded.
Get-ComputerInfo | ft CsNumberOfProcessors # Number of physical processors.
The processors might be multicore or hyper-threading processors. To find out how many cores are available, see the Premium v3 service plan in App Service pricing.
Customize health ping behavior
App Service considers a container to be successfully started when the container starts and responds to an HTTP ping. The health ping request contains the header User-Agent= "App Service Hyper-V Container Availability Check". If the container starts but doesn't respond to pings after a certain amount of time, App Service logs an event in the Docker log.
If your application is resource intensive, the container might not respond to the HTTP ping in time. To control what happens when HTTP pings fail, set the CONTAINER_AVAILABILITY_CHECK_MODE app setting. You can set it by using Cloud Shell. In Bash, use the following command:
az webapp config appsettings set --resource-group <group-name> --name <app-name> --settings CONTAINER_AVAILABILITY_CHECK_MODE="ReportOnly"
In PowerShell, use the following command:
Set-AzWebApp -ResourceGroupName <group-name> -Name <app-name> -AppSettings @{"CONTAINER_AVAILABILITY_CHECK_MODE"="ReportOnly"}
The following table shows the possible values:
| Value | Description |
|---|---|
Repair |
Restart the container after three consecutive availability checks. |
ReportOnly |
The default value. Report the container in the Docker logs after three consecutive availability checks, but don't restart it. |
Off |
Don't check for availability. |
Support for group managed service accounts
Group managed service accounts aren't supported in Windows containers in App Service.
Enable SSH
You can use Secure Shell (SSH) to remotely run administrative commands from a command-line terminal. To enable the Azure portal SSH console feature with custom containers, follow these steps:
Create a standard
sshd_configfile with the following example contents and place it on the application project root directory:Port 2222 ListenAddress 0.0.0.0 LoginGraceTime 180 X11Forwarding yes Ciphers aes128-cbc,3des-cbc,aes256-cbc,aes128-ctr,aes192-ctr,aes256-ctr MACs hmac-sha1,hmac-sha1-96 StrictModes yes SyslogFacility DAEMON PasswordAuthentication yes PermitEmptyPasswords no PermitRootLogin yes Subsystem sftp internal-sftpNote
This file configures OpenSSH and must include the following items to comply with the Azure portal SSH feature:
- The
Portvalue must be set to2222. - The
Ciphersvalues must include at least one item in this list:aes128-cbc,3des-cbc, oraes256-cbc. - The
MACsvalues must include at least one item in this list:hmac-sha1orhmac-sha1-96.
- The
Create an entrypoint script named
entrypoint.shor change any existing entrypoint file. Add the command to start the SSH service, along with the application startup command. The following example demonstrates starting a Python application. Replace the last command according to the project language or stack:Add the following instructions to the Dockerfile according to the base image distribution. These instructions copy the new files, install the OpenSSH server, set proper permissions and configure the custom entrypoint, and expose the ports required by the application and SSH server, respectively:
COPY entrypoint.sh ./ # Start and enable SSH RUN apt-get update \ && apt-get install -y --no-install-recommends dialog \ && apt-get install -y --no-install-recommends openssh-server \ && echo "root:Docker!" | chpasswd \ && chmod u+x ./entrypoint.sh COPY sshd_config /etc/ssh/ EXPOSE 8000 2222 ENTRYPOINT [ "./entrypoint.sh" ]Note
The root password must be exactly
Docker!because App Service uses it to grant you access to the SSH session with the container. This configuration doesn't allow external connections to the container. The container's port2222is accessible only within the bridge network of a private virtual network. An attacker on the internet can't access it.Rebuild and push the Docker image to the registry, and then test the Web App SSH feature in the Azure portal.
For more troubleshooting information, see the Azure App Service blog: Enable SSH on a Linux web app for containers.
Access diagnostic logs
You can access the console logs that are generated from inside the container.
To turn on container logging, run the following command:
az webapp log config --name <app-name> --resource-group <resource-group-name> --docker-container-logging filesystem
Replace the <app-name> and <resource-group-name> values with names that are appropriate for your web app.
After you turn on container logging, run the following command to see the log stream:
az webapp log tail --name <app-name> --resource-group <resource-group-name>
If console logs don't appear immediately, check again in 30 seconds.
To stop log streaming at any time, use the keyboard shortcut Ctrl+C.
Configure multi-container apps
Note
The Docker Compose feature will be retired on March 31, 2027. Sidecar containers succeed multi-container apps in App Service. For new services, see Tutorial: Configure a sidecar container for custom container in Azure App Service. For existing multi-container apps in App Service, see Migrate your Docker Compose applications to the sidecar feature.
Use persistent storage in Docker Compose
Multi-container apps like WordPress need persistent storage to function properly. To enable persistent storage, your Docker Compose configuration must point to a storage location outside your container. Storage locations inside your container don't persist changes beyond app restart.
To enable persistent storage, set the WEBSITES_ENABLE_APP_SERVICE_STORAGE app setting. Use the az webapp config appsettings set command in Cloud Shell.
az webapp config appsettings set --resource-group <group-name> --name <app-name> --settings WEBSITES_ENABLE_APP_SERVICE_STORAGE=TRUE
In your docker-compose.yml file, map the volumes option to ${WEBAPP_STORAGE_HOME}.
WEBAPP_STORAGE_HOME is an environment variable in App Service that maps to persistent storage for your app. For example:
wordpress:
image: <image name:tag>
volumes:
- "${WEBAPP_STORAGE_HOME}/site/wwwroot:/var/www/html"
- "${WEBAPP_STORAGE_HOME}/phpmyadmin:/var/www/phpmyadmin"
- "${WEBAPP_STORAGE_HOME}/LogFiles:/var/log"
Preview limitations
Multi-container is currently in preview. The following App Service platform features aren't supported:
- Authentication or authorization.
- Managed identities.
- Cross-origin resource sharing (CORS).
- Virtual network integration with Docker Compose scenarios.
Docker Compose on Azure App Service currently has a limit of 4,000 characters.
Docker Compose options
The following sections show supported and unsupported Docker Compose configuration options.
Supported options
commandentrypointenvironmentimageportsrestartservicesvolumes(Mapping to Azure Storage is unsupported)
Unsupported options
build(not allowed)depends_on(ignored)networks(ignored)secrets(ignored)- Ports other than
80and8080(ignored) - Default environment variables like
$variableand${variable}(unlike in Docker)
Syntax limitations
- The first YAML statement in the file always needs to be
version x.x. - The ports section must use quoted numbers.
- The
image > volumesection must be quoted and can't have permissions definitions. - The volumes section can't include an empty curly brace after the volume name.
Note
Any other options not explicitly mentioned are ignored in preview.
Ignore the robots933456 message in logs
You might see the following message in the container logs:
2019-04-08T14:07:56.641002476Z "-" - - [08/Apr/2019:14:07:56 +0000] "GET /robots933456.txt HTTP/1.1" 404 415 "-" "-"
You can safely ignore this message. /robots933456.txt is a dummy URL path. App Service uses it to check if the container is capable of serving requests. A "404" error response indicates that the path doesn't exist, and it signals to App Service that the container is healthy and ready to respond to requests.