Azure App Service Container Fails to Start — Gunicorn/Django Timeout and Invalid Host Header Error

Damion Barrett 0 Reputation points
2025-10-21T01:04:06.1733333+00:00

I am running a Django (Python 3.11) web app on Azure App Service for Containers using a custom image pushed to Azure Container Registry (gracealoneacr).

The container fails to start with the following errors in the logs:

  • django.core.exceptions.DisallowedHost: Invalid HTTP_HOST header: 'gracealone-api.azurewebsites.net'
  • Application timed out waiting for HTTP pings on port 80
  • Site container terminated during startup

I already verified that:

  • STATIC_ROOT and other Django settings are configured
  • Gunicorn runs fine locally with gunicorn core.wsgi:application --bind 0.0.0.0:8000
  • The App Service startup command is set to the above gunicorn command
  • The resource group is gracealone-rg, container image gracealone-api:v11, and registry gracealoneacr

I suspect the app isn’t reading environment variables like WEBSITES_PORT or the port isn’t being exposed correctly on Azure’s side.

Can you confirm the correct environment configuration for a Django container on Azure App Service for Containers and how to fix the startup timeout and DisallowedHost error?

Any help from Azure engineers on diagnosing container startup failure or webapp configuration mismatch would be appreciated.

Azure Container Registry
Azure Container Registry
An Azure service that provides a registry of Docker and Open Container Initiative images.
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. RAMAMURTHY MAKARAPU 1,130 Reputation points Microsoft External Staff Moderator
    2025-10-22T00:59:59.7533333+00:00

    Hi @Damion Barrett ,

    Thank you for submitting your question on Microsoft Q&A.

    This looks like two separate but related issues:

    1. Django is rejecting the Host header > DisallowedHost
    2. App Service’s health pings can’t reach your app > “timed out waiting for HTTP pings on port 80”

    Below is a concise, battle‑tested checklist to get a Django container running reliably on Azure App Service for Containers with Gunicorn, and to fix both errors.

    Why the error occurs .

    App Service for Containers assumes your container listens on port 80. If your app listens on another port (e.g., 8000), you must tell App Service via WEBSITES_PORT to probe that port; otherwise, health pings hit the wrong port and the site fails to start.

    App Service performs warm‑up pings (default path "/robots933456.txt") until the container responds, or until the startup time limit (default ~230s) is reached. Both the warm‑up path and success status codes can be customized.

    Django’s ALLOWED_HOSTS must include your app’s hostname (gracealone-api.azurewebsites.net) or a suitable pattern; otherwise, Django throws DisallowedHost

    Fix -1 is allow the Azure hostname in Django

    Add your Azure host to ALLOWED_HOSTS. A safe, flexible pattern is to read from an env var:

    # settings.py
    import os
    
    ALLOWED_HOSTS = os.getenv(
        "ALLOWED_HOSTS",
        "gracealone-api.azurewebsites.net,.azurewebsites.net,localhost,127.0.0.1"
    ).split(",")
    
    

    Django rejects requests whose Host header is not in ALLOWED_HOSTS. Using an env var lets you keep the same image across environments.

    Fix - 2 is Make App Service probe the same port your app listens on

    You’ve confirmed Gunicorn is bound to 0.0.0.0:8000. Choose one of these approaches (both work):

    Option A (keep app on 8000)

    • Set WEBSITES_PORT=8000 in App Service → Configuration → Application settings.
    • EXPOSE 8000 in your Dockerfile.
    • Keep your Gunicorn bind as 0.0.0.0:8000.
    # Dockerfile
    EXPOSE 8000
    # (your CMD/ENTRYPOINT launching gunicorn on 0.0.0.0:8000)
    
    

    App Service expects 80 unless WEBSITES_PORT is set; also ensure the Dockerfile exposes the same port.

    Option B (move app to port 80)

    • Change your Gunicorn bind to 0.0.0.0:80.
    • EXPOSE 80 in your Dockerfile.
    • You can omit WEBSITES_PORT (default is 80).

    Binding to 80 matches the platform’s default probing behavior. Warm‑up pings target the configured port and path until the container is ready.

    Note on PORT vs WEBSITES_PORT

    • On custom containers, use WEBSITES_PORT.
    • On blessed images (platform-provided stacks), PORT is used by the underlying runner. If in doubt, set both

    Fix - 3: Avoid App Service startup timeouts

    If your app does migrations or loads resources at startup, increase the container startup time limit:

    az webapp config appsettings set \
      --resource-group gracealone-rg \
      --name gracealone-api \
      --settings WEBSITES_CONTAINER_START_TIME_LIMIT=600
    

    WEBSITES_CONTAINER_START_TIME_LIMIT defaults to ~230s and can be increased up to 1800s.

    Optionally, set a health path your app can return 200 OK quickly:

    az webapp config appsettings set \
      --resource-group gracealone-rg \
      --name gracealone-api \
      --settings WEBSITE_WARMUP_PATH=/healthz WEBSITE_WARMUP_STATUSES=200
    
    

    By default, any status from "/robots933456.txt" counts as ready; you can require 200/202 on a custom path for stricter checks.

    Recommended startup/compose patterns

    1. Let your image start itself (preferred)

    Define ENTRYPOINT/CMD in your Dockerfile and do not override “Startup Command” in App Service. This avoids mismatch between portal settings and your image:

    # Example ENTRYPOINT/CMD with port coming from env
    ENV GUNICORN_WORKERS=3
    # Bind to $PORT if present else fallback to 8000
    CMD ["bash","-lc","gunicorn core.wsgi:application --workers ${GUNICORN_WORKERS} --bind 0.0.0.0:${PORT:-8000}"]
    
    

    Overriding container commands is possible but easy to misconfigure; baking the start command into the image reduces surprises.

    1. If you must use “Startup Command”

    Make sure it matches the port strategy above and uses the correct env var:

    gunicorn core.wsgi:application --workers 3 --bind 0.0.0.0:8000
    # and set WEBSITES_PORT=8000
    
    

    The portal override replaces your image’s entry point ensure it aligns with the exposed/bound port.

    Reference :

    https://v4.hkg1.meaqua.org/en-us/azure/app-service/reference-app-settings?tabs=kudu%2Cdotnet

    https://stackoverflow.com/questions/40667519/why-is-django-throwing-error-disallowedhost-at

    Kindly let us know if the above comment helps or you need further assistance on this issue.

    Please "upvote" if the information helped you. This will help us and others in the community as well.

    0 comments No comments

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.