Dockerfile for Python: Flask, Django and FastAPI example
A working Dockerfile for a Python web app, with Gunicorn, Uvicorn or a plain script.
This Dockerfile installs your packages from requirements.txt and starts your app with the server you choose. Fill in the details below, save it in your project folder, then build and run it.
Create your config file
Type your own values. The file below updates as you type, and nothing you type leaves your browser.
FROM python:3.13-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
RUN useradd --create-home appuser
USER appuser
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]How this setup works
Step-by-step setup
- Save the file above as Dockerfile in your project folder, next to requirements.txt.
- Make sure requirements.txt lists your packages, including the server you chose (gunicorn or uvicorn).
- Build the image: docker build -t myapp .
- Run it: docker run -p 8000:8000 myapp.
- Open http://localhost:8000 in your browser.
What the important lines mean
- python:3.13-slim is a small Debian image with Python 3.13.
- PYTHONUNBUFFERED=1 sends print output straight to docker logs.
- --no-cache-dir keeps pip's download cache out of the image so it stays small.
- 0.0.0.0:8000 lets the server accept connections from outside the container.
Common errors and how to fix them
- ModuleNotFoundError: No module named 'gunicorn'
- The server is not listed in requirements.txt. Add gunicorn (or uvicorn) to that file and build again.
- ERROR: Could not open requirements file
- requirements.txt is missing or not in the same folder as the Dockerfile. Create it with pip freeze > requirements.txt.
- Failed to find attribute 'app' in 'app'
- The module setting is wrong. Use the file name, a colon, then the variable that holds your app, like main:app.
- The container runs but the browser cannot connect
- The server must bind to 0.0.0.0 and you must publish the port with -p 8000:8000.
- PermissionError when the app writes a file
- The non-root user cannot write to /app. Write to a mounted volume or /tmp, or untick the non-root option.
Frequently asked questions
- Which server should I choose?
- Use Gunicorn for Flask and Django, and Uvicorn for FastAPI. Use the plain script option only for small tools and scripts.
- Why slim and not Alpine?
- Many Python packages ship ready-made builds for Debian-based images. On Alpine they often have to compile, which is slow and can fail.
- Do I need a .dockerignore file?
- It helps. List .git, __pycache__ and your virtual environment folder so they are not copied into the image.
Free tools that help
- Common ports listSearch a list of common TCP and UDP port numbers: SSH 22, HTTP 80, HTTPS 443, MySQL 3306, PostgreSQL 5432 and more, with dev server ports.
Fix a problem with this setup
- Docker port already allocatedFix "port is already allocated" in Docker and Docker Compose: find the container or program that holds the port and free it.
- Docker container cannot reach localhostFix "connection refused" when an app in Docker calls localhost: use host.docker.internal, a Compose service name or host networking.
More container configs
- Docker Compose for MailpitRun a fake mail server on your computer, so test emails from your app show up in a web page instead of being sent.
- Kubernetes Deployment and ServiceA Deployment and Service YAML for your container image, with replicas, port, resource limits and health checks.
- Docker Compose for MySQL and phpMyAdminRun MySQL and the phpMyAdmin web page on your computer with one docker compose command.
- Docker Compose for PostgreSQL and pgAdminRun PostgreSQL and the pgAdmin web page on your computer with one docker compose command.
- docker-compose for MongoDBRun a local MongoDB database with a password that keeps its data.
- docker-compose for PostgreSQLRun a local PostgreSQL database that keeps its data.