Skip to content

Run the application with PostgreSQL

This chapter starts a production-shaped dependency chain: PostgreSQL becomes healthy, a one-shot container applies the PostgreSQL artifacts, and only then does the API start.

%% Compose startup
flowchart LR
    P[PostgreSQL 17] -->|service_healthy| M[Migration job]
    M -->|service_completed_successfully| A[FastAPI service]
    A -->|/health ready| U[Client]

Inspect the Compose definition

name: ormdantic-todo

services:
  postgres:
    image: postgres:17-alpine
    environment:
      POSTGRES_DB: ${POSTGRES_DB:-todo}
      POSTGRES_USER: ${POSTGRES_USER:-todo}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-todo}
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}']
      interval: 2s
      timeout: 3s
      retries: 20
    volumes:
      - todo-postgres-data:/var/lib/postgresql/data

  migrate:
    build:
      context: ../..
      dockerfile: examples/todo_app/Dockerfile
    environment: &application-environment
      APP_ENV: production
      DATABASE_URL: postgresql://${POSTGRES_USER:-todo}:${POSTGRES_PASSWORD:-todo}@postgres:5432/${POSTGRES_DB:-todo}
    command:
      - ormdantic
      - migrations
      - apply-dir
      - /app/migrations/postgresql
      - --allow-destructive
    depends_on:
      postgres:
        condition: service_healthy
    restart: 'no'

  api:
    build:
      context: ../..
      dockerfile: examples/todo_app/Dockerfile
    environment: *application-environment
    ports:
      - '${TODO_API_PORT:-8000}:8000'
    depends_on:
      migrate:
        condition: service_completed_successfully
    healthcheck:
      test:
        - CMD
        - python
        - -c
        - 'import urllib.request; urllib.request.urlopen(''http://127.0.0.1:8000/health'', timeout=2)'
      interval: 5s
      timeout: 3s
      retries: 12
    restart: unless-stopped

volumes:
  todo-postgres-data:

No service mounts the Docker socket. PostgreSQL data lives in a named volume, and the runtime image runs as a non-root user. The image builds the local Ormdantic wheel in a Rust builder stage, then copies only the wheel and example into a slim Python runtime stage.

Start the stack

From the repository root:

docker compose -f examples/todo_app/docker-compose.yml up --build

Wait until api is healthy, then open http://127.0.0.1:8000/docs or run:

curl http://127.0.0.1:8000/health

The expected backend is postgresql. Stop containers without erasing data:

docker compose -f examples/todo_app/docker-compose.yml down

The named volume remains. Add --volumes only when you intentionally want to delete all example data.

The default credentials are demonstration values. Override POSTGRES_DB, POSTGRES_USER, and POSTGRES_PASSWORD locally; use managed secrets and TLS in a real deployment. Review PostgreSQL driver behavior before selecting URL options or relying on native schema features.