Halloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run ScanHalloween DealsAmazon USOctober picks are moving - compare before checkoutAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk11 min

How to Containerize a Python Web App with Docker

A practical, production-minded guide to packaging a Python web app in Docker, from the first local image to a non-root multi-stage build with Compose and health checks.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The reliable way to containerize a Python web app is to build an image that contains your application, pinned dependencies, and Python runtime, then run it with a server bound to 0.0.0.0. Start with a small, trusted Python base image, install dependencies in a cache-friendly layer, and use a multi-stage build plus a non-root runtime for production. The commands below cover a FastAPI example, but the same layout works for Flask and other Python web frameworks.

What containerizing a Python app actually does

A Docker image packages application code, its dependencies, configuration defaults, and the runtime needed to start the process. Running that image creates an isolated container, so the same artifact can behave consistently on a developer laptop, a CI runner, or a production host.

As an Amazon Associate I earn from qualifying purchases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Containerization does not automatically make an application highly available, secure, or stateful. You still need to choose a production server, provide secrets at runtime, persist database data outside the container, and configure health checks and resource limits. Docker supplies the packaging and process boundary; your deployment platform supplies scheduling, networking, storage, and restart policy.

Prepare the project before writing a Dockerfile

Use a predictable project layout

Keep the Docker build context focused on one application. A small FastAPI project might contain:

  • app.py or a package such as app/ containing the web application.
  • A dependency declaration: a pinned requirements.txt, or pyproject.toml together with its lock file.
  • A Dockerfile and, for local multi-service work, compose.yaml.
  • A .dockerignore file.
  • Tests and configuration templates that are safe to include in the build context.

Pin dependency versions in the file used by the build. A lock file or fully pinned requirements file makes two builds reproducible; an unconstrained install can silently produce a different image after a package release.

Create an application that listens on the container interface

For an ASGI application, expose an object that a server can import, such as app in app.py. The server must bind to 0.0.0.0, not only 127.0.0.1, because loopback inside a container is not reachable through Docker’s published port.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose a lightweight health endpoint, for example /health, that returns a quick success response without depending on a slow page render. A health endpoint is useful to Docker, Compose, and an orchestrator when deciding whether to restart or receive traffic.

Exclude local files with .dockerignore

The build context is sent to the Docker daemon or builder. Excluding unnecessary files makes builds faster and prevents accidental disclosure of local material. Include entries such as:

.git
.venv/
venv/
__pycache__/
*.py[cod]
.pytest_cache/
.mypy_cache/
.env
.env.*
tests/
coverage.xml
htmlcov/

Do not copy a local virtual environment into an image. Its binaries may target a different operating system or architecture, and it defeats the purpose of installing dependencies in a controlled image layer. Keep production secrets, private keys, and developer credentials out of both the build context and the image.

Write a first Dockerfile for local development

Use a current, supported Python image from a trusted source and record the tag in version control. The following example uses python:3.12-slim to illustrate the structure; select the Python release that your application and dependencies support.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
# syntax=docker/dockerfile:1
FROM python:3.12-slim

WORKDIR /app

# Copy dependency metadata before source code for better cache reuse.
COPY requirements.txt .
RUN python -m pip install --no-cache-dir --upgrade pip 
    && python -m pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000
CMD ["python", "-m", "uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]

Each instruction has a reason:

  • FROM selects the operating-system libraries and Python runtime. A slim image reduces unused packages, but it may omit compilers or diagnostic tools your dependencies need.
  • WORKDIR establishes the directory used by subsequent instructions and by the running process.
  • The dependency file is copied separately so a source-code edit does not invalidate the dependency-install layer.
  • EXPOSE documents the port the process uses. It does not publish that port to your host.
  • The JSON array is exec-form CMD. Docker can then deliver stop and termination signals directly to the Python server.

If your project uses a pyproject.toml and lock file, copy those files first and replace the pip command with the package manager and locked-install command your project standardizes on. Do not copy the entire source tree before installing dependencies; doing so throws away the most valuable cache layer whenever one application file changes.

Build, run, and verify the image

  1. From the directory containing the Dockerfile, build an image:
    docker build -t my-python-app .
  2. Start a disposable container and map host port 8000 to container port 8000:
    docker run --rm -p 8000:8000 my-python-app
  3. Open http://localhost:8000 or request the health endpoint:
    curl http://localhost:8000/health
  4. Stop the foreground container with Ctrl+C. The --rm option removes the stopped container, while the image remains available for another run.

The two port numbers in -p 8000:8000 are ordered as host port followed by container port. You can choose another host port, such as -p 8080:8000, without changing the server’s internal listen port. If the page is unreachable, inspect the logs with docker logs <container-name-or-id> and confirm that the server command reports a bind address of 0.0.0.0.

Choose the right Python server

The image and framework do not determine the server command by themselves. Match the server to the application’s interface and to the way you plan to scale it.

Application type Typical command When it fits Important decision
FastAPI or another ASGI app python -m uvicorn app:app --host 0.0.0.0 --port 8000 Simple deployments, local development, or environments that manage process replication outside the container. Set worker count and concurrency according to the container’s CPU and memory limits.
Flask or another WSGI app gunicorn app:app --bind 0.0.0.0:8000 Production WSGI serving with managed worker processes. Choose a worker class and count appropriate to the framework and workload.
ASGI app behind a process manager A Gunicorn command configured with an ASGI worker class Deployments that standardize process supervision and worker lifecycle. Confirm the worker class supports your framework and async features.

Gunicorn’s documented container pattern uses JSON-form arguments, binds to 0.0.0.0:8000, and demonstrates sizing workers from available CPU. Treat that as a starting point rather than a universal formula: memory per worker, blocking calls, request duration, and the platform’s CPU quota all affect the safe count. One container with several workers is not the same as several independently scheduled containers, so decide where scaling and failover belong.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use a multi-stage Dockerfile for production

A single-stage image can retain compilers, downloaded archives, package caches, and other build-only material. Multi-stage builds put those operations in a builder stage and copy only the runtime artifacts into the final stage. That normally lowers the final image’s size and attack surface and makes the production contents easier to inspect.

# syntax=docker/dockerfile:1
FROM python:3.12-slim AS builder
WORKDIR /build
COPY requirements.txt .
RUN python -m pip install --upgrade pip 
    && pip wheel --no-cache-dir --wheel-dir /wheels -r requirements.txt

FROM python:3.12-slim AS runtime
WORKDIR /app

COPY --from=builder /wheels /wheels
RUN pip install --no-cache-dir /wheels/* 
    && rm -rf /wheels

COPY . .
EXPOSE 8000
CMD ["gunicorn", "app:app", "--bind", "0.0.0.0:8000"]

The Python tag is an example and must follow your support policy. Pin it to a maintained release and rebuild when security updates are published. If a dependency contains native extensions, the builder may need operating-system compiler or header packages; keep those packages in the builder stage rather than installing them in the runtime stage.

Run the runtime as a non-root user

Processes in a container should not need root privileges. Add an application user in the runtime stage, make the application directory readable by that user, and switch with USER:

FROM python:3.12-slim AS runtime
WORKDIR /app

RUN addgroup --system appgroup && adduser --system --ingroup appgroup appuser
COPY --from=builder /wheels /wheels
RUN pip install --no-cache-dir /wheels/* && rm -rf /wheels
COPY --chown=appuser:appgroup . .

USER appuser
EXPOSE 8000
CMD ["gunicorn", "app:app", "--bind", "0.0.0.0:8000"]

Check that the application writes only to directories the user owns. Logs should normally go to standard output and standard error rather than to a file inside the container. If uploads or generated data must persist, mount storage explicitly instead of relying on the writable layer.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Keep the build reproducible

  • Pin Python and dependency versions, and commit the lock or requirements file.
  • Use a fixed build context and review changes to the Dockerfile and ignore file.
  • Rebuild regularly so the base image receives security fixes; a pinned tag is reproducible, not permanently secure.
  • Generate a software bill of materials or run the image scanner supplied by your platform if your release process requires vulnerability reporting.

Pass configuration and secrets at runtime

Configuration that changes between development, staging, and production belongs outside the image. Read settings such as database URLs, allowed hosts, log level, and worker count from environment variables or a secret manager at startup. Compose can load local development values, while a production platform should inject secrets through its protected configuration mechanism.

Never place a real .env file in the build context, commit credentials in source, or use ARG for a secret that must survive into the running application. Build arguments can appear in image history and build logs. If a value is needed only while building, use the builder’s secret facility and ensure it is not copied into the final stage.

Add a health check that tests the application

A process can remain running while the application is unable to serve requests. Add a health endpoint and, when your platform uses Docker health status, a check that reaches it. A Python-only check avoids assuming that curl exists in a slim image:

HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 
  CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3)"

Set the interval and startup grace period to match real application startup, especially when migrations or model loading occur. A health check should be cheap and deterministic; do not make it depend on an external service unless the deployment decision truly requires that dependency.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use Compose for a web app and local services

Compose is useful when development needs more than one container. It can define the web service, a database, environment variables, a named volume, published ports, and resource limits in one file. This example assumes your application reads DATABASE_URL and that the database service is named db:

services:
  web:
    build:
      context: .
      target: runtime
    command: python -m uvicorn app:app --host 0.0.0.0 --port 8000 --reload
    ports:
      - "8000:8000"
    environment:
      DATABASE_URL: postgresql://app:app_password@db:5432/appdb
    volumes:
      - .:/app
    depends_on:
      - db
    mem_limit: 768m
    cpus: 1.0

  db:
    image: postgres:16
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: app_password
      POSTGRES_DB: appdb
    volumes:
      - db-data:/var/lib/postgresql/data

volumes:
  db-data:

The bind mount gives a development container live access to source files, and the reload command watches those files. Do not carry that setup into production: live reload consumes resources, the source mount overrides files baked into the image, and the database password shown here is only a local example. Use a named volume for development database persistence, and back up production data using the database service’s storage system.

If you maintain a dedicated development stage, Compose can select it with build.target. Keep shells, compilers, test tools, and reload dependencies in that stage; keep the production target limited to the runtime packages and application files. Compose resource keys are interpreted by the implementation and deployment mode you use, so verify that your chosen platform enforces CPU and memory limits rather than assuming local Compose settings apply everywhere.

Rank #4
Natmisz:Dive Log Book Scuba Diving, Scuba Log Book, Scuba Diving Equipment Accessories, Gifts For Scuba Divers Men, Leather Logbook Binder, A6 Size Binders 50 Pages
  • This dive logbook is made from genuine cowhide leather, and just like fingerprints, each piece is one-of-a-kind. Each logbook flaunts its own unique personality with varying colors, textures, and markings. Over time, the leather develops its own charming "battle scars," making it even more beautiful and uniquely yours. It's like your favorite pair of jeans – the more you use it, the cooler it gets
  • Its compact A6 size (5.1in x 7.5in) makes it super easy to carry around, letting you jot down your dive logs anytime, anywhere. Whether you toss it in your dive bag or take it on the go, it’s a breeze to bring along. Perfectly suited for A6 dive logs and A6 binders. Think of it as your trusty sidekick – always ready for the next adventure
  • This dive logbook isn't just a pretty face – it's got some serious chops too. The genuine leather cover laughs in the face of splashes, and the 120g high-quality paper ensures your notes won't dissolve into a soggy mess. Perfect for those damp and drizzly dives
  • 3 Card Slots: Perfect for stashing your dive cards, transit passes, and other important cards. 1 Zipper Pocket: Keep your valuables secure and safe. 4 Receipt Pockets: Perfect for holding tickets, passports, and other essential documents We’ve included 2 removable, expandable PVC pages: Transparent PVC Page: Holds up to 3 cards, making them easy to see and use. PVC Storage Bag: Specially designed waterproof bag for those items that just hate getting wet.
  • Our dive logbook includes all the essential fields for divers to record every dive, such as date, dive site, conditions, equipment, dive stats, and dive verification, among others. With 50 pages designed to capture the key details of each dive adventure, it's perfect for PADI divers and fits the A6 size dive log requirements
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Common failures and precise fixes

“Connection refused” on localhost

Check all three addresses: the application must bind to 0.0.0.0, the container must listen on the port in the command, and the run command must publish that same container port. EXPOSE alone does not publish anything. Use docker ps to confirm the port mapping and docker logs to inspect the startup command.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Dependencies reinstall on every build

Move COPY requirements.txt or the lock files above COPY . .. Also check whether a changing file such as a generated version file is being copied before the install layer. BuildKit can reuse unchanged layers, and a cache mount can speed package downloads without leaving its contents in the final image.

Import errors after switching to a slim image

The package may require native libraries or compilation headers. Install those only in the builder stage, build wheels there, and copy the wheels into the runtime stage. If the package also needs a shared library at runtime, install that library in the runtime stage and document why it is required.

The container exits immediately

Inspect the exit status and logs. A container lives only as long as its foreground process. Check the module path in app:app, confirm that the dependency file installed the selected server, and avoid shell-form commands that hide the real process behind an extra shell.

Files are owned by root

Use COPY --chown and switch to the non-root user before the command runs. Review temporary, cache, upload, and log directories; either make them writable by the application user or redirect that data to an external volume or standard output.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Health status is unhealthy

Run the health command manually inside the container, verify that the endpoint does not require authentication, and increase start-period if normal startup takes longer than the initial grace period. A check aimed at 127.0.0.1 tests the process inside the same container; a platform-level probe may instead need the service name or published endpoint.

Choose an approach using these trade-offs

Approach Strength Cost or risk Best fit
Single-stage Dockerfile Fewest instructions and quick to understand. Build tools and caches can remain in the production image. Early experiments or tightly controlled internal images.
Multi-stage Dockerfile Separates compilation from runtime and reduces attack surface. Requires explicit artifact copying and occasional dependency troubleshooting. Production images and any dependency with native build steps.
Uvicorn directly Simple ASGI command and transparent process behavior. Worker management and scaling may be left to the platform. Local development or platforms that replicate containers.
Gunicorn-managed workers Established worker lifecycle for WSGI and managed deployments. Worker class, count, and memory use need deliberate tuning. Production services that standardize on a process manager.
One Compose project Convenient web, database, environment, volume, and limit definitions. Compose is not automatically a production scheduler or backup system. Local development and repeatable integration environments.

Production readiness checklist

  • The Python base image comes from a trusted source, uses a supported tag, and is rebuilt for security updates.
  • Dependencies are pinned and installed before frequently changing source files.
  • A multi-stage build keeps compilers, package caches, and test tooling out of the runtime image.
  • The process binds to 0.0.0.0 and the published host port maps to the actual container port.
  • The image runs as a non-root user and writes persistent data only to explicitly managed storage.
  • Secrets are injected at runtime, never baked into layers, source, or build arguments.
  • Logs go to standard output and errors, where the container platform can collect them.
  • A health endpoint and suitable Docker or platform probe are present.
  • CPU and memory limits are set where the deployment system supports them, and worker count is tested within those limits.
  • The image is tested by building from a clean checkout and starting the same command used in deployment.

Go deeper after your first successful build

Once you can build, run, and update the image, Docker’s official educational resources list Docker Deep Dive by Nigel Poulton as a more extensive guide; paperback and Kindle editions are available through the author’s listed retailers. Treat it as supplementary reading rather than a substitute for documenting your own image, runtime, and deployment policies.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.