CI/CD for FastAPI with GitHub Actions
FastAPI apps that hit a database and a cache should test against real Postgres and Redis in CI.
This recipe wires FastAPI into GitHub Actions. It installs deps, lints, runs pytest against Postgres and Redis service containers, and deploys uvicorn on main.
What the pipeline does
- pip install
- Lint with ruff
- Run pytest against Postgres and Redis
- Apply Alembic migrations
- Deploy on main
The workflow
Two service containers: Postgres for the database and Redis for caching or rate limiting.
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: fastapi_test
ports: ["5432:5432"]
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis:7
ports: ["6379:6379"]
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/fastapi_test
REDIS_URL: redis://localhost:6379/0
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: pip
- run: pip install -r requirements.txt
- run: ruff check .
- run: alembic upgrade head
- run: pytest
deploy:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: pip
- run: pip install -r requirements.txt
- run: ./deploy.shTesting against a database
Both services are reachable on localhost from the job: Postgres at 5432 and Redis at 6379. Run alembic upgrade head before pytest so the schema is present. Use an async test client (httpx.AsyncClient) and an async DB session that points at DATABASE_URL.
Deploying
Production FastAPI runs under uvicorn (often with gunicorn workers using the uvicorn worker class). Deploy to a container platform, a serverless adapter (Mangum on Lambda), or a VPS. Managed runners that auto-retry transient registry and pip flakes keep the run green, at about 69% less than GitHub-hosted runners.
Key takeaways
- FastAPI often needs both Postgres and Redis service containers in CI.
- Run alembic upgrade head before pytest to create the schema.
- Serve with uvicorn (or gunicorn + uvicorn workers) in production.