Skip to content
Latchkey

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.

.github/workflows/ci.yml
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.sh

Testing 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.

Related guides

Run this faster and cheaper on Latchkey managed runners. Start free →