Configure a Linux Python app for Azure App Service

This article describes how Azure App Service runs Python apps, and how you can customize the behavior of App Service when needed. Python apps must be deployed with all the required pip modules.

The App Service deployment engine automatically activates a virtual environment and runs pip install -r requirements.txt for you when you deploy a Git repository, or a Zip package with build processes switched on.

This guide provides key concepts and instructions for Python developers who use a built-in Linux container in App Service. If you've never used Azure App Service, you should follow the Python quickstart and Python with PostgreSQL tutorial first.


Linux is currently the recommended option for running Python apps in App Service. For information on the Windows option, see Python on the Windows flavor of App Service.

Show Python version

To show the current Python version, run the following command in the Cloud Shell:

az webapp config show --resource-group <resource-group-name> --name <app-name> --query linuxFxVersion

To show all supported Python versions, run the following command in the Cloud Shell:

az webapp list-runtimes --linux | grep PYTHON

You can run an unsupported version of Python by building your own container image instead. For more information, see use a custom Docker image.

Set Python version

Run the following command in the Cloud Shell to set the Python version to 3.7:

az webapp config set --resource-group <resource-group-name> --name <app-name> --linux-fx-version "PYTHON|3.7"

Customize build automation

If you deploy your app using Git or zip packages with build automation turned on, the App Service build automation steps through the following sequence:

  1. Run custom script if specified by PRE_BUILD_SCRIPT_PATH.
  2. Run pip install -r requirements.txt.
  3. If is found in the root of the repository, run collectstatic. However, if DISABLE_COLLECTSTATIC is set to true, this step is skipped.
  4. Run custom script if specified by POST_BUILD_SCRIPT_PATH.

PRE_BUILD_COMMAND, POST_BUILD_COMMAND, and DISABLE_COLLECTSTATIC are environment variables that are empty by default. To run pre-build commands, define PRE_BUILD_COMMAND. To run post-build commands, define POST_BUILD_COMMAND. To disable running collectstatic when building Django apps, set DISABLE_COLLECTSTATIC=true.

The following example specifies the two variables to a series of commands, separated by commas.

az webapp config appsettings set --name <app-name> --resource-group <resource-group-name> --settings PRE_BUILD_COMMAND="echo foo, scripts/"
az webapp config appsettings set --name <app-name> --resource-group <resource-group-name> --settings POST_BUILD_COMMAND="echo foo, scripts/"

For additional environment variables to customize build automation, see Oryx configuration.

For more information on how App Service runs and builds Python apps in Linux, see Oryx documentation: How Python apps are detected and built.

Container characteristics

Python apps deployed to App Service on Linux run within a Docker container that's defined in the App Service Python GitHub repository. You can find the image configurations inside the version-specific directories.

This container has the following characteristics:

  • Apps are run using the Gunicorn WSGI HTTP Server, using the additional arguments --bind= --timeout 600.

  • By default, the base image includes the Flask web framework, but the container supports other frameworks that are WSGI-compliant and compatible with Python 3.7, such as Django.

  • To install additional packages, such as Django, create a requirements.txt file in the root of your project using pip freeze > requirements.txt. Then, publish your project to App Service using Git deployment, which automatically runs pip install -r requirements.txt in the container to install your app's dependencies.

Container startup process

During startup, the App Service on Linux container runs the following steps:

  1. Use a custom startup command, if provided.
  2. Check for the existence of a Django app, and launch Gunicorn for it if detected.
  3. Check for the existence of a Flask app, and launch Gunicorn for it if detected.
  4. If no other app is found, start a default app that's built into the container.

The following sections provide additional details for each option.

Django app

For Django apps, App Service looks for a file named within your app code, and then runs Gunicorn using the following command:

# <module> is the path to the folder that contains
gunicorn --bind= --timeout 600 <module>.wsgi

If you want more specific control over the startup command, use a custom startup command and replace <module> with the name of the module that contains

Flask app

For Flask, App Service looks for a file named or and starts Gunicorn as follows:

# If
gunicorn --bind= --timeout 600 application:app
# If
gunicorn --bind= --timeout 600 app:app

If your main app module is contained in a different file, use a different name for the app object, or you want to provide additional arguments to Gunicorn, use a custom startup command.

Default behavior

If the App Service doesn't find a custom command, a Django app, or a Flask app, then it runs a default read-only app, located in the opt/defaultsite folder. The default app appears as follows:

Default App Service on Linux web page

Customize startup command

You can control the container's startup behavior by providing a custom Gunicorn startup command. To do this, running the following command in the Cloud Shell:

az webapp config set --resource-group <resource-group-name> --name <app-name> --startup-file "<custom-command>"

For example, if you have a Flask app whose main module is and the Flask app object in that file is named myapp, then <custom-command> is as follows:

gunicorn --bind= --timeout 600 hello:myapp

If your main module is in a subfolder, such as website, specify that folder with the --chdir argument:

gunicorn --bind= --timeout 600 --chdir website hello:myapp

You can also add any additional arguments for Gunicorn to <custom-command>, such as --workers=4. For more information, see Running Gunicorn (

To use a non-Gunicorn server, such as aiohttp, you can replace <custom-command> with something like this:

python3.7 -m aiohttp.web -H localhost -P 8080 package.module:init_func


App Service ignores any errors that occur when processing a custom command file, then continues its startup process by looking for Django and Flask apps. If you don't see the behavior you expect, check that your startup file is deployed to App Service and that it doesn't contain any errors.

Access environment variables

In App Service, you can set app settings outside of your app code. Then you can access them using the standard os.environ pattern. For example, to access an app setting called WEBSITE_SITE_NAME, use the following code:


Detect HTTPS session

In App Service, SSL termination happens at the network load balancers, so all HTTPS requests reach your app as unencrypted HTTP requests. If your app logic needs to check if the user requests are encrypted or not, inspect the X-Forwarded-Proto header.

if 'X-Forwarded-Proto' in request.headers and request.headers['X-Forwarded-Proto'] == 'https':
# Do something when HTTPS is used

Popular web frameworks let you access the X-Forwarded-* information in your standard app pattern. In CodeIgniter, the is_https() checks the value of X_FORWARDED_PROTO by default.

Access diagnostic logs

You can access the console logs generated from inside the container. First, turn on container logging by running the following command in the Cloud Shell:

az webapp log config --name <app-name> --resource-group myResourceGroup --docker-container-logging filesystem

Once container logging is turned on, run the following command to see the log stream:

az webapp log tail --name <app-name> --resource-group myResourceGroup

If you don't see console logs immediately, check again in 30 seconds.


You can also inspect the log files from the browser at https://<app-name>

To stop log streaming at any time, type Ctrl+C.

Open SSH session in browser

To make open a direct SSH session with your container, your app should be running.

Paste the following URL into your browser and replace <app-name> with your app name:


If you're not yet authenticated, you're required to authenticate with your Azure subscription to connect. Once authenticated, you see an in-browser shell, where you can run commands inside your container.

SSH connection


Any changes you make outside the /home directory are stored in the container itself and don't persist beyond an app restart.

To open a remote SSH session from your local machine, see Open SSH session from remote shell.


  • You see the default app after deploying your own app code. The default app appears because you either haven't deployed your app code to App Service, or App Service failed to find your app code and ran the default app instead.
  • Restart the App Service, wait 15-20 seconds, and check the app again.
  • Be sure you're using App Service for Linux rather than a Windows-based instance. From the Azure CLI, run the command az webapp show --resource-group <resource_group_name> --name <app_service_name> --query kind, replacing <resource_group_name> and <app_service_name> accordingly. You should see app,linux as output; otherwise, recreate the App Service and choose Linux.
  • Use SSH or the Kudu console to connect directly to the App Service and verify that your files exist under site/wwwroot. If your files don't exist, review your deployment process and redeploy the app.
  • If your files exist, then App Service wasn't able to identify your specific startup file. Check that your app is structured as App Service expects for Django or Flask, or use a custom startup command.
  • You see the message "Service Unavailable" in the browser. The browser has timed out waiting for a response from App Service, which indicates that App Service started the Gunicorn server, but the arguments that specify the app code are incorrect.
  • Refresh the browser, especially if you're using the lowest pricing tiers in your App Service Plan. The app may take longer to start up when using free tiers, for example, and becomes responsive after you refresh the browser.
  • Check that your app is structured as App Service expects for Django or Flask, or use a custom startup command.
  • Access the log stream.

Next steps