Day 10: Building a Multi‑Container FastAPI App with PostgreSQL, Redis, and Celery

This tutorial extends the previous FastAPI example by adding Redis caching and Celery asynchronous tasks, showing the project layout, a complete docker‑compose configuration, required Python packages, sample FastAPI and Celery worker code, startup commands, health‑check setup, and key takeaways.

Tech Ocean
Tech Ocean
Tech Ocean
Day 10: Building a Multi‑Container FastAPI App with PostgreSQL, Redis, and Celery

Project Structure

project/
├── docker-compose.yml
├── Dockerfile
├── requirements.txt
└── app/
    ├── __init__.py
    ├── main.py
    └── celery_worker.py

docker-compose.yml

services:
  api:
    build: .
    ports:
      - "8000:8000"
    environment:
      DATABASE_URL: postgresql+asyncpg://app:secret@db:5432/mydb
      REDIS_URL: redis://redis:6379/0
      CELERY_BROKER_URL: redis://redis:6379/1
      CELERY_RESULT_BACKEND: redis://redis:6379/2
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    volumes:
      - ./app:/app
    command: ["uvicorn", "app.main:app", "--reload", "--host", "0.0.0.0"]
  worker:
    build: .
    environment:
      DATABASE_URL: postgresql+asyncpg://app:secret@db:5432/mydb
      REDIS_URL: redis://redis:6379/0
      CELERY_BROKER_URL: redis://redis:6379/1
      CELERY_RESULT_BACKEND: redis://redis:6379/2
    depends_on:
      - db
      - redis
    volumes:
      - ./app:/app
    command: ["celery", "-A", "app.celery_worker", "worker", "--loglevel=info"]
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: secret
      POSTGRES_USER: app
      POSTGRES_DB: mydb
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app"]
      interval: 5s
      timeout: 5s
      retries: 5
  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data
volumes:
  postgres_data:
  redis_data:

Code Implementation

requirements.txt

fastapi
uvicorn[standard]
asyncpg
psycopg2-binary
redis
celery
pydantic-settings

app/main.py

from fastapi import FastAPI
from contextlib import asynccontextmanager
import asyncpg

db_pool = None

@asynccontextmanager
async def lifespan(app: FastAPI):
    global db_pool
    db_pool = await asyncpg.create_pool("postgresql://app:secret@db:5432/mydb")
    yield
    await db_pool.close()

app = FastAPI(lifespan=lifespan)

@app.get("/")
async def root():
    return {"status": "ok"}

app/celery_worker.py

from celery import Celery

celery_app = Celery(
    "worker",
    broker="redis://redis:6379/1",
    backend="redis://redis:6379/2",
)

@celery_app.task
def send_email(to: str):
    return {"sent": True, "to": to}

Start and Verify

# Start all services
docker compose up -d

# Check service status
docker compose ps

# Test API
curl http://localhost:8000/

# Test Celery task
docker compose exec api python -c "
from app.celery_worker import send_email
result = send_email.delay('[email protected]')
print(result.get(timeout=5))
"

Day 10 Summary

Multi‑container orchestration includes api, worker, db, and redis services.

Health checks ensure dependent services are ready before the API starts.

Celery uses Redis as both broker and backend.

The depends_on with condition: service_healthy controls startup order.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

PythonRediscelerypostgresqlfastapidocker-compose
Tech Ocean
Written by

Tech Ocean

Focused on AI programming, sharing ready-to-use development efficiency solutions.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.