Reference Guide Detailed deployment notes with production context and concrete examples.

Flask Docker Container Not Starting (Fix Guide)

If your Flask Docker container is not starting, restarting repeatedly, or exiting right after launch, this guide shows you how to identify the failure point and fix it step-by-step. The goal is to get the container running, verify the Flask app is reachable, and prevent the same startup issue in production.

Quick Fix / Quick Setup

Start with the container state, logs, and startup command.

bash
docker ps -a

docker logs --tail=200 <container_name>

docker inspect <container_name> --format '{{.State.Status}} {{.State.ExitCode}} {{.State.Error}}'

docker run --rm -it <image_name> sh

# inside the container, test the startup command manually
python -m flask --app app routes || true
python -m flask --app app run --host=0.0.0.0 --port=5000 || true

# or, if using gunicorn
gunicorn -b 0.0.0.0:5000 wsgi:app

# validate compose or dockerfile inputs
docker compose config

docker exec -it <running_container> env | sort

Most startup failures come from one of these:

  • incorrect CMD or ENTRYPOINT
  • missing Python dependencies
  • wrong Flask or gunicorn import path
  • missing environment variables
  • app binding to 127.0.0.1 instead of 0.0.0.0

What’s Happening

A Docker container stays running only while its main process stays alive. If your Flask app crashes during import, startup, or dependency initialization, the main process exits and the container stops or restarts. In other cases, the container may be running but the app is unreachable because it is bound to the wrong interface or port.

Step-by-Step Guide

  1. Check the container state and exit code
    Run:
    bash
    docker ps -a
    docker inspect <container_name> --format '{{.State.Status}} {{.State.ExitCode}} {{.State.OOMKilled}} {{.State.Error}}'
    

    What to look for:
    • Exited with non-zero exit code: startup failure
    • Restarting: process is failing and restart policy is retrying
    • OOMKilled=true: memory pressure killed the process
  2. Read the container logs first
    Run:
    bash
    docker logs --tail=200 <container_name>
    

    Common failures:
    • ModuleNotFoundError
    • ImportError
    • KeyError
    • syntax errors
    • database connection failures
    • gunicorn errors such as:
    text
    Failed to find attribute 'app' in 'wsgi'
    
  3. Verify the startup command
    Check your Dockerfile CMD or ENTRYPOINT. For production, use gunicorn instead of the Flask development server.
    Example Dockerfile:
    dockerfile
    FROM python:3.12-slim
    
    WORKDIR /app
    
    COPY requirements.txt .
    RUN pip install --no-cache-dir -r requirements.txt
    
    COPY . .
    
    CMD ["gunicorn", "-b", "0.0.0.0:5000", "wsgi:app"]
    

    If you use Compose, also inspect the service command: because it can override the image default.
  4. Open a shell in the image and test startup manually
    Run:
    bash
    docker run --rm -it <image_name> sh
    

    Then test:
    bash
    python -c "from wsgi import app; print(app)"
    gunicorn -b 0.0.0.0:5000 wsgi:app
    

    This isolates image-level problems from Compose or orchestration issues.
  5. Confirm the Flask app import path
    Gunicorn must point to the correct module and app object.
    Common patterns:
    bash
    gunicorn -b 0.0.0.0:5000 wsgi:app
    gunicorn -b 0.0.0.0:5000 app:app
    gunicorn -b 0.0.0.0:5000 'app:create_app()'
    

    Test imports directly:
    bash
    python -c "from wsgi import app; print(app)"
    python -c "from app import app; print(app)"
    

    If your app uses a factory, verify the callable exists and returns a valid Flask app.
  6. Verify dependencies are installed
    Inside the container:
    bash
    pip show flask gunicorn
    pip freeze
    

    If required packages are missing:
    • update requirements.txt or pyproject.toml
    • rebuild the image without cache
    bash
    docker build --no-cache -t <image_name> .
    
  7. Check the working directory and copied files
    Inside the container:
    bash
    pwd
    ls -la
    find . -maxdepth 2 -type f | sort
    

    Confirm these exist where expected:
    • application source files
    • wsgi.py
    • templates
    • static assets
    • config files

    If files are missing, review:
    • WORKDIR
    • COPY instructions
    • .dockerignore
  8. Verify environment variables
    Missing runtime variables often crash the app during import or configuration.
    Run:
    bash
    env | sort
    

    Confirm required values such as:
    • SECRET_KEY
    • DATABASE_URL
    • REDIS_URL
    • app-specific settings

    Compose example:
    yaml
    services:
      web:
        image: myflaskapp:latest
        environment:
          SECRET_KEY: change-me
          DATABASE_URL: postgresql://user:pass@db:5432/app
    

    If variables are not loading correctly, see Flask Environment Variables Not Loading in Production.
  9. Confirm the app binds to all interfaces
    If the app listens on 127.0.0.1, Docker port publishing will not expose it correctly.
    Correct:
    bash
    gunicorn -b 0.0.0.0:5000 wsgi:app
    

    For temporary testing only:
    bash
    python -m flask --app app run --host=0.0.0.0 --port=5000
    
  10. Check exposed and published ports
    If using Docker directly:
    bash
    docker run -p 5000:5000 <image_name>
    

    If using Compose:
    yaml
    services:
      web:
        ports:
          - "5000:5000"
    

    Validate that:
    • the app listens on the same container port you publish
    • upstream reverse proxy config points to the same port
  11. Validate Docker Compose configuration
    Run:
    bash
    docker compose config
    

    Review the merged output for:
    • incorrect command
    • wrong env_file
    • invalid volume mounts
    • missing networks
    • broken health checks

    Also inspect service state:
    bash
    docker compose ps
    docker compose logs -f <service_name>
    
  12. Test for broken volume mounts
    A bind mount can hide files copied into the image.
    Problem example:
    yaml
    services:
      web:
        volumes:
          - .:/app
    

    If your local directory does not contain the same files as the image, startup can fail. Temporarily remove the volume and start again.
  13. Check permissions and runtime user
    If the container uses a non-root user, verify it can read application files and write required paths.
    Example:
    dockerfile
    RUN useradd -m appuser
    USER appuser
    

    Validate ownership and permissions:
    bash
    ls -la
    

    If needed:
    dockerfile
    RUN chown -R appuser:appuser /app
    
  14. Check database and dependency readiness
    The app may exit if PostgreSQL, MySQL, Redis, or another service is unavailable at startup.
    Test connectivity from inside the container where possible, or verify hostnames and ports in environment variables. If the app fails only after the container starts, you may need:
    • retry logic
    • startup backoff
    • proper service readiness checks

    If startup succeeds but requests fail later with server errors, see Flask 500 Internal Server Error in Production.
  15. Inspect health checks
    A bad health check can mark a healthy container as unhealthy or trigger restarts.
    Inspect health output:
    bash
    docker inspect <container_name> --format '{{json .State.Health}}'
    

    Compose example:
    yaml
    services:
      web:
        healthcheck:
          test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
          interval: 30s
          timeout: 5s
          retries: 3
    

    Confirm the endpoint exists and returns a successful HTTP response.
  16. Rebuild cleanly after fixes
    After changing dependencies, Dockerfile instructions, or app files, rebuild without cache:
    bash
    docker compose build --no-cache
    docker compose up -d
    

    Or with raw Docker:
    bash
    docker build --no-cache -t <image_name> .
    docker run -d -p 5000:5000 <image_name>
    

    Then verify the container remains in Up state.

Common Causes

  • Incorrect CMD or ENTRYPOINT → Docker starts the wrong process or no long-running process → set a valid gunicorn or app startup command.
  • Wrong gunicorn app path → gunicorn cannot import the Flask app object → fix wsgi:app or factory syntax.
  • Missing Python dependencies → imports fail at startup → update dependency files and rebuild the image.
  • Application code not copied into the image → required files are missing at runtime → fix COPY instructions and .dockerignore.
  • Bind mount hides application files → local volume overlays built image contents → remove or correct the mount path.
  • Required environment variables missing → app crashes during configuration or import → define variables in Compose, env files, or runtime flags.
  • App binds to 127.0.0.1 → service is not reachable through Docker networking → bind to 0.0.0.0.
  • Wrong port mapping → host traffic goes to the wrong container port → align published ports with the actual app bind port.
  • Database or Redis unavailable during startup → app exits when dependency checks fail → test connectivity and add retry handling.
  • Permissions or non-root user issues → app cannot read files or write needed paths → correct ownership and runtime user settings.
  • Health check command is wrong → Docker or an orchestrator marks the container unhealthy → fix the health check endpoint and command.
  • Out-of-memory kill → container stops under memory pressure → reduce memory usage or increase limits.

Debugging Section

Use these commands in order.

bash
docker ps -a
docker logs --tail=200 <container_name>
docker inspect <container_name> --format '{{.State.Status}} {{.State.ExitCode}} {{.State.OOMKilled}} {{.State.Error}}'
docker inspect <container_name>
docker image inspect <image_name>
docker run --rm -it --entrypoint sh <image_name>
docker compose ps
docker compose logs -f <service_name>
docker compose config
docker exec -it <container_name> env | sort
docker exec -it <container_name> sh -lc 'pwd && ls -la'
docker exec -it <container_name> sh -lc 'python -c "from wsgi import app; print(app)"'
docker exec -it <container_name> sh -lc 'pip freeze'
docker exec -it <container_name> sh -lc 'ss -lntp || netstat -lntp'

What to look for:

  • ExitCode is non-zero
  • OOMKilled is true
  • startup command does not match your actual app module
  • environment variables are missing
  • bind mounts hide source files
  • process is not listening on the expected port
  • health check output shows repeated failures

Checklist

  • Container state is Up, not Exited or Restarting
  • docker logs shows the Flask app or gunicorn started without import errors
  • Startup command points to the correct module and app object
  • Required environment variables are present inside the container
  • Application files exist in the expected working directory
  • The app listens on 0.0.0.0 and the correct container port
  • Port mapping or reverse proxy configuration matches the container port
  • Database or dependent services are reachable at startup
  • Health check passes if one is configured

FAQ

Why does the container exit with code 0?

The main process completed successfully and Docker had nothing left to run. This often happens when a shell script starts a background process and then exits.

Why does the container restart repeatedly?

A restart policy is active and the main process keeps failing. Check container logs and the original exit code.

Can a successful image build still fail at runtime?

Yes. Build success only means the image was created. Runtime failures still happen from missing environment variables, incorrect commands, unavailable services, or bind mounts hiding files.

Why is the container running but the app is unreachable?

The app may be listening on 127.0.0.1, using the wrong port, or the reverse proxy may be targeting the wrong upstream.

How do I test whether gunicorn can import my app?

Run this inside the container:

bash
python -c "from wsgi import app; print(app)"

Or start gunicorn directly with the same command used in your Dockerfile or Compose file.

Can Docker Compose volumes break a working image?

Yes. A bind mount can overwrite the application directory and hide files copied during image build.

Should I use flask run in production?

No. Use gunicorn or another production WSGI server.

Final Takeaway

When a Flask Docker container does not start, treat it as a runtime process failure. Check logs, exit code, startup command, imports, environment variables, ports, mounts, and dependency readiness in that order. In most cases, the fix is a corrected gunicorn command, valid app import path, complete environment configuration, or a clean rebuild followed by validation against the production checklist at Flask Production Checklist (Everything You Must Do).