Python Deployment: Production Servers and Dockerization

Deploying Python applications in production requires moving beyond the built-in development servers to a robust stack capable of handling concurrency, process management, and isolation. This article focuses on **Gunicorn/Uvicorn** configurations and **Multi-stage Docker** builds.

I. The Production Stack: WSGI and ASGI

Python web applications follow either the WSGI (Synchronous) or ASGI (Asynchronous) specification.

A. WSGI with Gunicorn (Synchronous)

For Django or Flask applications, Gunicorn (Green Unicorn) is the standard.

* **Worker Model:** Uses a "Pre-fork" worker model. The master process forks several worker processes to handle requests.

* **Configuration:**

```bash

gunicorn --workers 4 --bind 0.0.0.0:8000 myapp.wsgi:application

```

* **Optimal Worker Count:** A common heuristic is $(2 \times \text{CPUs}) + 1$.

B. ASGI with Uvicorn (Asynchronous)

For FastAPI or Starlette, Uvicorn provides an implementation of the ASGI spec based on `uvloop` and `httptools`.

* **Running Uvicorn:**

```bash

uvicorn myapp.main:app --host 0.0.0.0 --port 8000 --workers 4

```

* **The Gunicorn-Uvicorn Combo:** For the best of both worlds (process management + async performance), use Gunicorn as the process manager with Uvicorn workers:

```bash

gunicorn -w 4 -k uvicorn.workers.UvicornWorker myapp.main:app

```

II. Multi-Stage Docker Builds

To minimize image size and improve security, Python applications should use multi-stage Dockerfiles.

A. The Multi-Stage Strategy

1. **Builder Stage:** Install build-time dependencies (e.g., `gcc`, `libc-dev`, `python3-dev`) and compile wheels.

2. **Final Stage:** Copy only the necessary runtime files and the installed packages.

B. Example Dockerfile

```dockerfile

Stage 1: Build

FROM python:3.11-slim as builder

WORKDIR /build

RUN apt-get update && apt-get install -y gcc libc-dev

COPY requirements.txt .

RUN pip install --user --no-cache-dir -r requirements.txt

Stage 2: Runtime

FROM python:3.11-slim

WORKDIR /app

Copy installed packages from builder

COPY --from=builder /root/.local /root/.local

COPY . .

Update PATH to include .local/bin

ENV PATH=/root/.local/bin:$PATH

EXPOSE 8000

CMD ["gunicorn", "--bind", "0.0.0.0:8000", "myapp.wsgi:application"]

```

III. Production Hardening

1. **Non-Root User:** Never run the application as `root`. Create a dedicated user in the Dockerfile.

2. **Signal Handling:** Ensure the application correctly handles `SIGTERM` for graceful shutdowns.

3. **Logging:** Log to `stdout/stderr` so the container orchestrator (Kubernetes/ECS) can capture logs.

4. **Resource Limits:** Define CPU and Memory limits in the orchestrator to prevent a single container from starving the host.

IV. Conclusion: Deployment as Code

Production Python deployment is a balance between performance (Uvicorn), stability (Gunicorn), and efficiency (Multi-stage Docker). By automating this stack through CI/CD pipelines, you ensure that every deployment is repeatable, secure, and performant.

For further optimization, see [SiteReliabilityEngineering](SiteReliabilityEngineering) and [SecretsManagement](SecretsManagement).