Project: Production Deployment
Introduction
This project takes your FastAPI service (Items, Todo, or Inference API) from local uvicorn to a production server with environment-based config, Alembic migrations, Gunicorn + UvicornWorker, Nginx, and optional Docker Compose. It mirrors the Flask deploy checklist adapted for ASGI.
Prerequisites
- A working FastAPI app with
app/main.py - Deploying With Uvicorn and Nginx
- FastAPI Docker and CI
- FastAPI Security Best Practices
- Linux developer workflow
Project Goals
- App runs under systemd + Gunicorn/Uvicorn workers
- Nginx reverse proxy with TLS path documented
- Secrets in
/etc/fastapi-app/env alembic upgrade headon deployGET /healthreturns 200- Compare with Flask production project
Step 1: Production Settings
app/core/config.py:
class Settings(BaseSettings):
environment: str = "production"
debug: bool = False
secret_key: str
database_url: str
cors_origins: list[str] = []
@property
def is_production(self) -> bool:
return self.environment == "production"app/main.py:
settings = get_settings()
app = FastAPI(
title="Production API",
docs_url="/docs" if settings.debug else None,
redoc_url=None,
openapi_url="/openapi.json" if settings.debug else None,
lifespan=lifespan,
)Step 2: Server Preparation (Ubuntu)
sudo apt update
sudo apt install -y python3-venv python3-pip nginx
sudo mkdir -p /var/www/fastapi-app /var/log/fastapi-app
sudo useradd --system --home /var/www/fastapi-app --shell /bin/bash www-data 2>/dev/null || true
sudo chown -R www-data:www-data /var/www/fastapi-app /var/log/fastapi-appDeploy code:
sudo -u www-data git clone <repo> /var/www/fastapi-app
cd /var/www/fastapi-app
sudo -u www-data python3 -m venv .venv
sudo -u www-data .venv/bin/pip install -r requirements.txt gunicornStep 3: Environment File
/etc/fastapi-app/env (chmod 600, root):
ENVIRONMENT=production
SECRET_KEY=replace-with-long-random-hex
DATABASE_URL=mysql+pymysql://fastapi:STRONG_PASS@127.0.0.1/fastapi_app?charset=utf8mb4
CORS_ORIGINS=https://app.example.comCreate MySQL DB—MySQL on Linux.
Step 4: Migrate and Test Locally on Server
cd /var/www/fastapi-app
sudo -u www-data bash -c 'set -a; source /etc/fastapi-app/env; set +a; .venv/bin/alembic upgrade head'
sudo -u www-data bash -c 'set -a; source /etc/fastapi-app/env; set +a; .venv/bin/gunicorn app.main:app -k uvicorn.workers.UvicornWorker -w 2 -b 127.0.0.1:8000'
curl http://127.0.0.1:8000/healthStop manual test before enabling systemd.
Step 5: systemd Unit
/etc/systemd/system/fastapi-app.service:
sudo systemctl daemon-reload
sudo systemctl enable --now fastapi-app
sudo systemctl status fastapi-appStep 6: Nginx
/etc/nginx/sites-available/fastapi-app:
WebSocket routes add Upgrade and Connection headers—deploy chapter.
Enable HTTPS—Let's Encrypt.
sudo ln -s /etc/nginx/sites-available/fastapi-app /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginxStep 7: Deploy Script
scripts/deploy.sh:
#!/bin/bash
set -euo pipefail
APP_DIR=/var/www/fastapi-app
cd "$APP_DIR"
sudo -u www-data git pull
sudo -u www-data .venv/bin/pip install -r requirements.txt
set -a; source /etc/fastapi-app/env; set +a
sudo -u www-data .venv/bin/alembic upgrade head
sudo systemctl restart fastapi-app
curl -sf http://127.0.0.1:8000/health
echo "Deploy OK"Run after CI passes—Git CI/CD.
Step 8: Docker Alternative
Use docker compose up -d from Docker and CI chapter; Nginx on host proxies to port 8000.
Step 9: Post-Deploy Verification
curl -I https://api.example.com/health
journalctl -u fastapi-app -n 50 --no-pagerRun one authenticated API flow (Todo project) over HTTPS.
Production Checklist
-
DEBUGoff, docs disabled - Strong secrets in env file
- Migrations applied
- Gunicorn bound to 127.0.0.1
- Nginx proxy headers configured
- HTTPS live
- Firewall allows 80/443, blocks public 8000
- MySQL backups—backup and restore
- CI runs
pytestandpip audit
Rollback
cd /var/www/fastapi-app
sudo -u www-data git checkout PREVIOUS_SHA
sudo -u www-data .venv/bin/pip install -r requirements.txt
sudo systemctl restart fastapi-appAvoid alembic downgrade in production unless planned.
FAQ
502 after deploy?
Check journalctl -u fastapi-app—import errors, missing env, migration failure.
CORS fails on HTTPS?
CORS_ORIGINS must include exact frontend origin with https://.
Multiple apps on one VPS?
Separate systemd units, ports, and server_name blocks.
Zero-downtime?
Blue/green or rolling updates—advanced.
Log aggregation?
Ship /var/log/fastapi-app/* to ELK, Loki, or CloudWatch.
Track complete?
Finish with troubleshooting and chapter index.