CI/CD for a FastAPI + Celery Worker with GitHub Actions
Test your FastAPI API and Celery tasks against Redis on every push.
A FastAPI app offloads background jobs to a Celery worker backed by Redis. This recipe lints and type-checks, tests both the API and the tasks against a Redis service, and builds the images.
What the pipeline does
- install deps with pip
- lint with ruff and type-check with mypy
- test the API with pytest
- test Celery tasks against a Redis broker
- build the API and worker container images
The workflow
A Redis service container backs Celery during tests; run tasks eagerly or against a real worker, then build the shared image both the API and worker run.
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
services:
redis:
image: redis:7
ports: ['6379:6379']
env:
CELERY_BROKER_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: mypy .
- run: pytest -q
- uses: actions/upload-artifact@v4
with:
name: coverage
path: .coverageCaching and speed
cache: pip restores the wheel cache keyed on requirements.txt. Tests are fast once Redis is up; cheaper managed runners such as Latchkey keep frequent runs inexpensive and auto-retry transient broker-connection races.
Deploying
Build one image and run it two ways: uvicorn for the API and celery -A app worker for the worker. Push to GHCR or ECR and deploy both as separate services (or pods) pointing at the same Redis broker and result backend.
Key takeaways
- One image serves both the API (uvicorn) and worker (celery).
- Back Celery with a Redis service container during tests.
- Deploy the API and worker as separate scalable services.