Skip to main content

Deploy FastAPI on AWS EC2 with nginx

TL;DR

Complete guide to deploying FastAPI on AWS EC2 with Uvicorn, Gunicorn, systemd, nginx, and SSL.

Key facts

Topology
FastAPI on AWS EC2

TL;DR

FastAPI on EC2 means running Uvicorn workers behind Gunicorn as the process manager, with systemd for service persistence, nginx for SSL termination, and AWS security groups as your network firewall. This guide covers the full production setup.

Prerequisites

  • An AWS account with EC2 access
  • A domain name with DNS configured
  • An SSH key pair registered in EC2

Step 1 — Launch and configure the EC2 instance

Launch an Ubuntu 24.04 AMI. A t3.small (2 vCPU, 2 GB RAM) works for moderate API workloads. Configure the security group:

PortProtocolSourcePurpose
22TCPYour IPSSH
80TCP0.0.0.0/0HTTP
443TCP0.0.0.0/0HTTPS

Attach an Elastic IP and point your domain's A record to it.

Step 2 — System preparation and Python

ssh -i ~/.ssh/mykey.pem ubuntu@your-elastic-ip
sudo apt update && sudo apt upgrade -y
sudo apt install -y python3.12 python3.12-venv python3-pip build-essential

Verify Python:

python3.12 --version

Step 3 — Deploy the application

sudo mkdir -p /var/www/fastapiapp && sudo chown ubuntu:ubuntu /var/www/fastapiapp
cd /var/www/fastapiapp
git clone git@github.com:yourorg/fastapiapp.git .
python3.12 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
pip install gunicorn uvicorn[standard]

Create .env with production values:

APP_ENV=production
DATABASE_URL=postgresql://user:pass@db-host/fastapidb
SECRET_KEY=your-secret-key

Verify the app starts:

uvicorn main:app --host 0.0.0.0 --port 8000
curl http://localhost:8000/health

Step 4 — Uvicorn with Gunicorn workers

Gunicorn manages multiple Uvicorn worker processes. Create gunicorn.conf.py:

import multiprocessing

bind = '127.0.0.1:8000'
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = 'uvicorn.workers.UvicornWorker'
timeout = 120
graceful_timeout = 30
max_requests = 1000
max_requests_jitter = 50
accesslog = '/var/log/gunicorn/fastapi-access.log'
errorlog = '/var/log/gunicorn/fastapi-error.log'
loglevel = 'info'
sudo mkdir -p /var/log/gunicorn

Step 5 — systemd service

Create /etc/systemd/system/fastapiapp.service:

[Unit]
Description=FastAPI Application (Gunicorn + Uvicorn)
After=network.target

[Service]
User=ubuntu
Group=ubuntu
WorkingDirectory=/var/www/fastapiapp
ExecStart=/var/www/fastapiapp/venv/bin/gunicorn -c gunicorn.conf.py main:app
Restart=always
RestartSec=5
Environment=PYTHONUNBUFFERED=1
EnvironmentFile=/var/www/fastapiapp/.env

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl start fastapiapp
sudo systemctl enable fastapiapp
sudo systemctl status fastapiapp

Step 6 — nginx reverse proxy

sudo apt install -y nginx

Create /etc/nginx/sites-available/fastapiapp:

server {
    listen 80;
    server_name api.example.com;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 60s;
        proxy_connect_timeout 10s;
    }
}
sudo ln -s /etc/nginx/sites-available/fastapiapp /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Step 7 — SSL

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d api.example.com

Monitoring

Use journalctl for service logs:

sudo journalctl -u fastapiapp -f

For deeper visibility, FastAPI's /docs endpoint exposes OpenAPI specs. Add a health check endpoint returning database connectivity and uptime metrics.

With Reflex

Reflex monitors your FastAPI deployment on EC2 end-to-end — tracking Gunicorn worker health, request latency percentiles, systemd service state, and EC2 system metrics. It orchestrates zero-downtime deployments with graceful Gunicorn HUP signals, verifies the health endpoint, and rolls back automatically on failure. See How it works.