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

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

Containerizing a Python web app packages its code, pinned dependencies, configuration, and Python runtime into a portable Docker image. You can build that image once, run it on a laptop or CI runner, and deploy the same artifact to a server with predictable behavior. The reliable workflow is to prepare a clean project directory, install dependencies in a cache-friendly layer, bind the web server to 0.0.0.0, and use a small multi-stage image with non-root execution for production.

What containerizing a Python web app means

A container image is an immutable filesystem and metadata bundle containing an application and everything it needs to start. Docker describes containerizing an application as packaging it with its dependencies, configuration, and runtime into one portable unit. Starting the image creates an isolated process with its own filesystem, network namespace, and resource limits while sharing the host’s kernel.

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

That packaging solves a common deployment problem: a developer’s laptop, a CI runner, and a production server may have different Python versions, operating-system libraries, or installed packages. A pinned dependency file and an explicit image tag make those inputs visible and reproducible. Containers do not automatically make an application secure, highly available, or stateful; those concerns still require runtime configuration, storage, monitoring, and an appropriate deployment platform.

Prepare the project before writing a Dockerfile

Keep the build context deliberate

Place the application source, dependency declaration, Dockerfile, Compose file (if used), and .dockerignore in a predictable project directory. A typical layout is:

my-python-app/
├── app.py
├── requirements.txt
├── Dockerfile
├── compose.yaml
└── .dockerignore

If the project uses Poetry, uv, or another package manager, copy its project file and lock file instead of inventing a second dependency list. The important property is that versions are pinned or otherwise locked according to the project’s support policy.

Use a minimal dependency file

For a small FastAPI example, requirements.txt might contain pinned FastAPI and Uvicorn releases selected by the project. Flask applications would list Flask and the production WSGI server they use. Do not install development-only linters, test runners, or documentation tools into the production environment unless the runtime actually needs them.

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

Exclude local and secret files

Create .dockerignore before the first build so Docker does not send unnecessary data to the build engine:

.git
.gitignore
.venv/
venv/
__pycache__/
*.py[cod]
.pytest_cache/
.mypy_cache/
.coverage
.env
.env.*
Dockerfile*
compose*.yml
compose*.yaml
tests/
dist/
build/

Adjust the entries if your build needs a file listed here. A local .env file can contain credentials; excluding it prevents accidental inclusion in image layers, but you still need to rotate a credential that has already been committed or baked into an image.

Write a development Dockerfile

Choose and record a trusted base image

Start from a maintained Python image from a trusted registry. A slim variant usually contains fewer operating-system packages than a full image, but a package that compiles native extensions may need additional build tools. Record the exact Python tag you selected and update it deliberately; floating tags make later rebuilds harder to reproduce.

Install dependencies before application code

Docker reuses a layer only when the instructions and their inputs are unchanged. Copying the dependency file first means a source-code edit does not invalidate the expensive dependency-install layer. This is the central cache optimization for a Python image.

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 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"]

The app:app value means “import the app module and use its app object.” Change it to your module and application object, such as main:api. EXPOSE documents the container port; it does not publish that port on the host.

Build and run the image

  1. From the directory containing the Dockerfile, run docker build -t my-python-app .. The final dot is the build context.
  2. Start a disposable container with docker run --rm -p 8000:8000 my-python-app. The first port is on the host; the second is inside the container.
  3. Open http://localhost:8000 or the application’s health endpoint. For FastAPI, the interactive documentation is commonly available at /docs when enabled.
  4. Stop the process with Ctrl-C. The --rm option removes the stopped container while leaving the image.

The server must listen on 0.0.0.0, not only on 127.0.0.1. The latter accepts connections from inside the container but is unreachable through Docker’s published port.

Use a multi-stage image for production

A single-stage build can leave compilers, header files, downloaded archives, and package-manager caches in the final image. Docker’s multi-stage guidance recommends separating the build environment from the runtime environment. The runtime stage copies only the artifacts needed to run the application, reducing image size and the number of packages that could be exploited.

# 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 shown is an example. Select a supported version compatible with your application and pin it in the Dockerfile. The builder creates wheels; the runtime installs those wheels without retaining the wheel directory. If a dependency requires system libraries at runtime, install only those libraries in the runtime stage, while keeping compilers and development headers in the builder.

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

Run the process as a non-root user

Processes in a container start as root unless you change the user. Create an unprivileged account, give it ownership of the application files, and switch to it before the command runs:

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"]

Choose a writable directory explicitly if the application creates temporary files. A non-root process may expose permission assumptions that were hidden during local development; fix those assumptions rather than switching back to root.

Add a health check when the platform uses it

Expose a lightweight endpoint such as /health that verifies the process is alive and, if appropriate, that critical dependencies are reachable. A Docker health check can probe it:

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

Use a probe that matches the application contract. A health endpoint that performs an expensive database query on every interval can create load or mark a healthy process unhealthy when an optional dependency is unavailable.

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

Choose the server command for your framework

Application type Typical command What to decide
FastAPI or another ASGI app python -m uvicorn app:app --host 0.0.0.0 --port 8000 Whether one Uvicorn process is sufficient for the container’s CPU and memory limits.
WSGI app such as Flask gunicorn app:app --bind 0.0.0.0:8000 Worker class, worker count, timeout, and graceful-shutdown behavior.
ASGI app managed by Gunicorn gunicorn app:app --bind 0.0.0.0:8000 with the project’s ASGI worker configuration That the installed Gunicorn and worker implementation match the framework.

Uvicorn is a direct ASGI server and is the command used in Docker’s FastAPI example. Gunicorn manages workers and is commonly used for WSGI deployments; its documented examples use JSON-form commands and bind to 0.0.0.0:8000. Size worker counts against the CPU and memory assigned to the container rather than copying a universal formula. More workers consume more memory, and an orchestrator may already provide process-level scaling.

Use exec-form JSON CMD, not a shell string. Exec form lets the server receive stop and termination signals directly, which improves graceful shutdown during a rolling deployment.

Configure the container at runtime

Do not bake passwords, API keys, certificates, or environment-specific URLs into the image. Pass non-secret configuration as environment variables and use the deployment platform’s secret mechanism for sensitive values. An image can be pulled, cached, and inspected by more people than the running service.

docker run --rm 
  -p 8000:8000 
  -e APP_ENV=production 
  -e DATABASE_URL="$DATABASE_URL" 
  my-python-app

Read and validate variables at startup. Fail with a clear message when a required value is missing, and provide safe defaults only for values that are genuinely non-sensitive. Never log a full connection string or token while diagnosing startup failures.

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

Use Compose for local development and supporting services

Compose describes several containers and their relationships in one file. It is useful when the web service needs a database, a named volume, environment variables, or local resource limits. Keep live-reload tools and interactive shells in a development stage; use the minimal runtime stage for production.

services:
  web:
    build:
      context: .
      target: builder
    ports:
      - '8000:8000'
    environment:
      APP_ENV: development
      DATABASE_URL: postgresql://app:password@db:5432/app
    volumes:
      - .:/app
    depends_on:
      - db
    command: ["python", "-m", "uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 512M

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

volumes:
  db-data:

The bind mount makes source edits visible to the development container; it is not a production deployment strategy. The named volume keeps database data outside the disposable database container. Replace example credentials with a local-only mechanism and never reuse them in production. A database service becoming “started” does not necessarily mean it is ready to accept connections, so application retry logic or an explicit readiness check may still be required.

Make builds faster and reproducible

Preserve cache boundaries

Copy lock files and dependency declarations before source code. Avoid changing an early RUN instruction for unrelated reasons, because every later layer must then be rebuilt. Docker BuildKit can use cache mounts for package downloads; the exact mount syntax depends on the Dockerfile frontend and build environment, so test it in the CI builder you actually use.

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

Pin both dependency and image inputs

A lock file or fully pinned requirements.txt controls Python packages. An explicit Python image tag controls the interpreter and operating-system base. Rebuild regularly to receive security updates, but review and test those updates rather than silently changing production artifacts.

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

Keep the runtime contents intentional

  • Use a small trusted base image appropriate for your compatibility needs.
  • Install build-only packages only in the builder stage.
  • Remove package caches and temporary wheel directories.
  • Copy only the source and assets required at runtime.
  • Do not include VCS metadata, local virtual environments, tests, or secrets unless the application explicitly needs them.
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” from the host

Check all three links: the process is running, it listens on 0.0.0.0, and the host mapping matches the container port. For a server on port 8000, use -p 8000:8000. If you changed the internal port, change both the command and the right-hand side of the mapping.

Module or application object not found

Confirm the working directory contains the module, then correct the command’s import path. main:api means a file named main.py with an object named api; it is not interchangeable with app:app. Package layouts may require a dotted path such as src.main:app and a matching Python path.

Dependency installation fails during build

Read the first compiler or system-library error, not only the final pip summary. Add the required compiler and headers to the builder stage, then install the corresponding runtime shared libraries in the runtime stage. If a wheel exists for the selected Python version and architecture, using it may avoid compilation.

Changes are missing while developing

Rebuild when changing dependencies or the Dockerfile. For source edits, use a development bind mount and a reload-capable server. A stale image is not fixed by repeatedly starting another container from the same tag.

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

The container exits immediately

Inspect logs with docker logs <container>. A container remains alive only while its main process runs; a shell that finishes, a failed import, or a server bound to an invalid port will stop it. Keep the server as the foreground process and use exec-form CMD.

Files cannot be written after switching users

Find the path the application writes, create it during the image build, and assign ownership with --chown or chown. Prefer ephemeral directories such as /tmp for temporary data and external storage for data that must survive a replacement container.

Production readiness checklist

  • Dependency versions and the Python base-image tag are explicit and reviewed.
  • The image uses a trusted, maintained, reasonably small base.
  • Build tools and caches are excluded from the runtime stage.
  • The process runs as a non-root user.
  • Secrets enter at runtime through environment or a secret manager.
  • The server binds to the container interface and receives termination signals directly.
  • A health endpoint and, where useful, a Docker health check are defined.
  • Logs go to standard output and error so the platform can collect them.
  • Database and user-uploaded data use external persistence rather than the writable container layer.
  • CI builds the image, starts it, exercises a health endpoint, and scans the resulting artifact according to the organization’s security process.

For one web process, a Dockerfile may be enough. Add Compose when local development needs a database, volume, or coordinated environment. In production, an orchestrator can provide replicas, resource limits, rolling updates, and readiness probes; the image should remain focused on starting one well-behaved application process.

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.

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.