Configure a custom container for Azure App Service
This article shows you how to configure a custom container to run on Azure App Service.
This guide provides key concepts and instructions for containerization of Windows apps in App Service. New Azure App Service users should first follow the custom container quickstart and tutorial.
This guide provides key concepts and instructions for containerization of Linux apps in App Service. If you're new to Azure App Service, 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
Service Principal is no longer supported for Windows container image pull authentication. We recommend that you use Managed Identity for both Windows and Linux containers
Supported parent images
For your custom Windows image, choose the right parent image (base image) for the framework you want:
- To deploy .NET Framework apps, use a parent image based on the Windows Server 2019 Core Long-Term Servicing Channel (LTSC) release.
- To deploy .NET Core apps, use a parent image based on the Windows Server 2019 Nano Annual Channel (AC) 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
To change an existing custom container from the current Docker image to a new image, use the following command:
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>
For <username> and <password>, supply the sign-in credentials for your private registry account.
Use managed identity to pull image from Azure Container Registry
Use the following steps to configure your web app to pull from Azure Container Registry (ACR) using managed identity. The steps use system-assigned managed identity. You can use user-assigned managed identity instead.
Enable the system-assigned managed identity for the web app by using the az webapp identity assign command:
az webapp identity assign --resource-group <group-name> --name <app-name> --query principalId --output tsv
Replace <app-name> with the name you used in the previous step. The output of the command, filtered by the
--query
and--output
arguments, is the service principal ID of the assigned identity.Get the resource ID of your Azure Container Registry:
az acr show --resource-group <group-name> --name <registry-name> --query id --output tsv
Replace <registry-name> with the name of your registry. The output of the command, filtered by the
--query
and--output
arguments, is the resource ID of the Azure 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 assign
command - <registry-resource-id> with the ID of your container registry from the
az acr show
command
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-configurations
argument 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
acrUserManagedIdentityID
property to specify its client ID:az identity show --resource-group <group-name> --name <identity-name> --query clientId --output tsv
Replace 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>"}'
You're all set! 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 private endpoint. After you configure your network and DNS resolution, enable the routing of the image pull through the virtual network by configuring the vnetImagePullEnabled
site setting:
az resource update --resource-group <group-name> --name <app-name> --resource-type "Microsoft.Web/sites" --set properties.vnetImagePullEnabled [true|false]
I 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 being pulled and started, App Service continues to serve requests from the old container. Only when the new container is started and ready to receive requests does App Service start sending requests to it.
How container images are stored
The first time you run a custom Docker image in App Service, App Service does a docker pull
and pulls all image layers. These layers are stored on disk, like if you were using Docker on-premises. Each time the app restarts, App Service does a docker pull
. 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, such as scaling up and down the pricing tiers, App Service must pull down all layers again. The same is true if you scale out to add more instances. There are also rare cases where 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 the Cloud Shell. In Bash:
az webapp config appsettings set --resource-group <group-name> --name <app-name> --settings WEBSITES_PORT=8000
In PowerShell:
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 need to be supplied externally. You can pass them in by using the Cloud Shell. In Bash:
az webapp config appsettings set --resource-group <group-name> --name <app-name> --settings DB_HOST="myownserver.mysql.database.azure.com"
In PowerShell:
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 injected into the process as environment variables automatically. You can verify container environment variables with the URL https://<app-name>.scm.azurewebsites.net/Env
.
If your app uses images from a private registry or from Docker Hub, 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 IIS or .NET Framework (4.0 or later) based containers, credentials are injected into System.ConfigurationManager
as .NET app settings and connection strings automatically by App Service. For all other language or framework, they're provided as environment variables for the process, with one of the following prefixes:
APPSETTING_
SQLCONTR_
MYSQLCONTR_
SQLAZURECOSTR_
POSTGRESQLCONTR_
CUSTOMCONNSTR_
This method works both for single-container apps 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. The C:\home directory is provided to enable your custom container to 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. Any existing files already present on the persistent storage when the container starts 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. This 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, enabling or disabling the persistent storage doesn't affect the 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 the Cloud Shell. In Bash:
az webapp config appsettings set --resource-group <group-name> --name <app-name> --settings WEBSITES_ENABLE_APP_SERVICE_STORAGE=false
In PowerShell:
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. The /home directory is provided to enable your custom container to access persistent storage. Saving data 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 \home directory persist. All instances of a scaled-out app can access them. Any existing files already present on the persistent storage when the container starts overwrite any contents in the \home directory of the container.
The only exception is the \home\LogFiles directory. This directory stores the container and application logs. This 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, enabling or disabling the persistent storage doesn't affect the application logging behavior.
We recommend that you write data to /home or a mounted Azure storage path. Data written 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 the Cloud Shell. In Bash:
az webapp config appsettings set --resource-group <group-name> --name <app-name> --settings WEBSITES_ENABLE_APP_SERVICE_STORAGE=true
In PowerShell:
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 data centers. 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, automatically generated keys are 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
, 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, navigate to https://<app-name>.scm.azurewebsites.net/
and choose the SSH option. This option establishes 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 is connected 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 is restarted. Such changes aren't part of the Docker image. To persist your changes, such as 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.
There are several ways to access Docker logs:
In the Azure portal
Docker logs are displayed in the Azure portal, in the Container Settings page of your app. The logs are truncated. To download all the logs, select Download.
From Kudu
To see the individual log files, navigate 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 using an FTP client.
In the SSH terminal, you can't access the C:\home\LogFiles folder by default 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 is currently in use by using an FTP client, you might get an error because of a file lock.
With the Kudu API
Navigate 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. The href
property lets you download the log file directly.
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 by using the Cloud Shell. In Bash:
az webapp config appsettings set --resource-group <group-name> --name <app-name> --settings WEBSITE_MEMORY_LIMIT_MB=2000
In PowerShell:
Set-AzWebApp -ResourceGroupName <group-name> -Name <app-name> -AppSettings @{"WEBSITE_MEMORY_LIMIT_MB"=2000}
The value is defined in 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 must not exceed 8 GB. For more information on how much memory is available, see the Premium v3 service plan section of App Service pricing.
Customize the number of compute cores
By default, a Windows container runs with all available cores for your chosen pricing tier. You might want to reduce the number of cores that your staging slot uses. To reduce the number of cores used by a container, set the WEBSITE_CPU_CORES_LIMIT
app setting to the preferred number of cores. You can set it by using the Cloud Shell. In Bash:
az webapp config appsettings set --resource-group <group-name> --name <app-name> --slot staging --settings WEBSITE_CPU_CORES_LIMIT=1
In PowerShell:
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 from the Azure portal or use the Kudu portal (https://<app-name>.scm.azurewebsites.net/webssh/host
). Enter the following commands 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 hyperthreading processors. Information on how many cores are available, see the Premium v3 service plan section of 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 the actions when HTTP pings fail, set the CONTAINER_AVAILABILITY_CHECK_MODE
app setting. You can set it by using the Cloud Shell. In Bash:
az webapp config appsettings set --resource-group <group-name> --name <app-name> --settings CONTAINER_AVAILABILITY_CHECK_MODE="ReportOnly"
In PowerShell:
Set-AzWebApp -ResourceGroupName <group-name> -Name <app-name> -AppSettings @{"CONTAINER_AVAILABILITY_CHECK_MODE"="ReportOnly"}
The following table shows the possible values:
Value | Descriptions |
---|---|
Repair | Restart the container after three consecutive availability checks. |
ReportOnly | The default value. Don't restart the container but report in the Docker logs for the container after three consecutive availability checks. |
Off | Don't check for availability. |
Support for Group Managed Service Accounts
Group Managed Service Accounts (gMSAs) are currently not supported in Windows containers in App Service.
Enable SSH
Secure Shell (SSH) is commonly used to run administrative commands remotely from a command-line terminal. In order to enable the Azure portal SSH console feature with custom containers, the follow these steps:
Create a standard
sshd_config
file 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-sftp
Note
This file configures OpenSSH and must include the following items in order to comply with the Azure portal SSH feature:
Port
must be set to 2222.Ciphers
must include at least one item in this list:aes128-cbc,3des-cbc,aes256-cbc
.MACs
must include at least one item in this list:hmac-sha1,hmac-sha1-96
.
Create an entrypoint script with the name entrypoint.sh or 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/stack:
Add to the Dockerfile the following instructions according to the base image distribution. These instructions copy the new files, install 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 it's used by App Service to let you access the SSH session with the container. This configuration doesn't allow external connections to the container. Port 2222 of the container is accessible only within the bridge network of a private virtual network and isn't accessible to an attacker on the internet.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: Enabling SSH on Linux Web App for Containers
Access diagnostic logs
You can access the console logs 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 <app-name> and <resource-group-name> with the names 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 you don't see console logs immediately, check again in 30 seconds.
To stop log streaming at any time, type Ctrl+C.
You can also inspect the log files in a browser at https://<app-name>.scm.azurewebsites.net/api/logs/docker
.
Configure multi-container apps
Note
Sidecar containers succeed multi-container apps in App Service. To get started, see Tutorial: Configure a sidecar container for custom container in Azure App Service.
Use persistent storage in Docker Compose
Multi-container apps like WordPress need persistent storage to function properly. To enable it, 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 is mapped 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 / Authorization
- Managed Identities
- CORS
- Virtual network integration isn't supported for Docker Compose scenarios.
- Docker Compose on Azure App Services currently has a limit of 4,000 characters at this time.
Docker Compose options
The following lists show supported and unsupported Docker Compose configuration options:
Supported options
- command
- entrypoint
- environment
- image
- ports
- restart
- services
- volumes (mapping to Azure Storage is unsupported)
Unsupported options
- build (not allowed)
- depends_on (ignored)
- networks (ignored)
- secrets (ignored)
- ports other than 80 and 8080 (ignored)
- default environment variables like
$variable and ${variable}
unlike in docker
Syntax Limitations
- "version x.x" always needs to be the first YAML statement in the file
- ports section must use quoted numbers
- image > volume section must be quoted and can't have permissions definitions
- volumes section must not have an empty curly brace after the volume name
Note
Any other options not explicitly called out are ignored in Public Preview.
robots933456 in logs
You may 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 that App Service uses to check if the container is capable of serving requests. A 404 response simply indicates that the path doesn't exist, but it lets App Service know that the container is healthy and ready to respond to requests.
Related content
Or, see more resources: