Back to Blog
Guide8 min read2026-05-01

FastAPI Production Deployment: Docker and Cloud Guide

A complete guide to deploying FastAPI applications to production with Docker, Uvicorn, async database connections, and a managed cloud PaaS.

FastAPI Production Deployment: Docker and Cloud Guide

FastAPI has rapidly become one of the most popular Python web frameworks, offering automatic OpenAPI documentation, type-safe request validation, and excellent async performance. Deploying a FastAPI application to production correctly requires containerization with Docker, a production ASGI server, and proper environment configuration.

This guide covers everything you need to get a FastAPI app running reliably in production on [PandaStack](https://pandastack.io).

Why FastAPI Needs Uvicorn in Production

FastAPI is an ASGI framework — it needs an ASGI server to handle HTTP requests. In development, FastAPI's built-in uvicorn runner works fine. In production, you should run Uvicorn under Gunicorn with the Uvicorn worker class, which adds process management and multi-worker support:

gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000

This gives you Gunicorn's robust process management with Uvicorn's async request handling.

Project Structure

myapi/
├── app/
│   ├── __init__.py
│   ├── main.py
│   ├── routers/
│   │   └── users.py
│   ├── models.py
│   └── database.py
├── requirements.txt
├── Dockerfile
└── pandastack.json

Application Entry Point

# app/main.py
from fastapi import FastAPI
from contextlib import asynccontextmanager
from .database import database
from .routers import users

@asynccontextmanager
async def lifespan(app: FastAPI):
    await database.connect()
    yield
    await database.disconnect()

app = FastAPI(
    title="My API",
    lifespan=lifespan,
    docs_url="/docs" if not os.getenv("PRODUCTION") else None,
)

app.include_router(users.router, prefix="/api/v1")

@app.get("/health")
async def health():
    return {"status": "ok"}

The lifespan context manager connects your database pool on startup and disconnects cleanly on shutdown — the correct pattern for async FastAPI apps.

Production Dockerfile

FROM python:3.12-slim

WORKDIR /app

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

RUN apt-get update && apt-get install -y --no-install-recommends     build-essential libpq-dev     && rm -rf /var/lib/apt/lists/*

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

COPY . .

RUN addgroup --system appgroup && adduser --system --ingroup appgroup appuser
USER appuser

EXPOSE 8000

CMD ["gunicorn", "app.main:app", "-w", "4", "-k", "uvicorn.workers.UvicornWorker", "--bind", "0.0.0.0:8000"]

Requirements

fastapi>=0.110.0
uvicorn[standard]>=0.29.0
gunicorn>=21.2.0
asyncpg>=0.29.0
databases[asyncpg]>=0.9.0
pydantic-settings>=2.0.0

Async Database with asyncpg

FastAPI's async performance is only realized if your database calls are also async. Use databases with asyncpg for PostgreSQL:

# app/database.py
import os
import databases

DATABASE_URL = os.environ["DATABASE_URL"]
database = databases.Database(DATABASE_URL)

Provision a managed PostgreSQL instance from the Databases section of [dashboard.pandastack.io](https://dashboard.pandastack.io) and copy the connection string.

Configuring pandastack.json

{
  "type": "container",
  "healthCheckPath": "/health"
}

PandaStack polls the healthCheckPath after each deployment. Only after your container returns HTTP 200 from this endpoint does PandaStack route live traffic to it. This prevents users from hitting your app before the database connection pool is ready.

Environment Variables

Set all secrets and configuration from [dashboard.pandastack.io](https://dashboard.pandastack.io):

DATABASE_URL=postgresql+asyncpg://user:pass@host:5432/mydb
SECRET_KEY=your-jwt-secret-key
PRODUCTION=true
ALLOWED_ORIGINS=https://yourdomain.com,https://www.yourdomain.com

Read these in your FastAPI app using Pydantic Settings:

from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    database_url: str
    secret_key: str
    production: bool = False
    allowed_origins: list[str] = []

    class Config:
        env_file = ".env"

settings = Settings()

CORS Configuration

from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.allowed_origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

Deploying with GitHub Integration

Connect your GitHub repository from the PandaStack dashboard. Every push to your main branch triggers an automatic build and deploy. For CLI-based deployments:

npm install -g @pandastack/cli
panda deploy

Disabling Interactive Docs in Production

FastAPI's /docs and /redoc endpoints are useful in development but should be disabled or protected in production:

app = FastAPI(
    docs_url=None if settings.production else "/docs",
    redoc_url=None if settings.production else "/redoc",
)

Production Checklist

  • Gunicorn + Uvicorn worker class handles concurrent requests
  • Database uses async connection pool via lifespan
  • All secrets stored as environment variables
  • /health endpoint returns HTTP 200
  • Interactive API docs disabled in production
  • Container runs as non-root user
  • pandastack.json specifies healthCheckPath

Background Tasks with FastAPI

FastAPI doesn't include a built-in task queue, but integrates well with Celery or ARQ for background processing. For simpler use cases, FastAPI's BackgroundTasks runs tasks after sending a response:

from fastapi import BackgroundTasks

def send_welcome_email(email: str):
    # blocking email call runs after response is sent
    mailer.send(to=email, subject="Welcome!")

@app.post("/users")
async def create_user(user: UserCreate, background_tasks: BackgroundTasks):
    db_user = await create_user_in_db(user)
    background_tasks.add_task(send_welcome_email, user.email)
    return db_user

For heavier workloads, deploy a separate Celery worker container on PandaStack using the same Docker image with a different command, backed by a Redis instance from the PandaStack Databases section.

For full documentation, visit [docs.pandastack.io](https://docs.pandastack.io).

Ready to deploy?

Start free on PandaStack — no credit card required.

Start free on PandaStack

More in Guide

Browse all Guide articles →

See also