services: postgres: image: postgres:15 e...
# ask-marvin
p
services: postgres: image: postgres:15 environment: POSTGRES_USER: prefect POSTGRES_PASSWORD: prefect POSTGRES_DB: prefect volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: pg_isready -h localhost -U $$POSTGRES_USER interval: 2s timeout: 5s retries: 15 redis: image: redis:7 migrate: image: prefecthq/prefect:3-latest depends_on: postgres: condition: service_healthy command: prefect server database upgrade -y environment: PREFECT_API_DATABASE_CONNECTION_URL: postgresql+asyncpg://prefect:prefect@postgres:5432/prefect prefect-api: image: prefecthq/prefect:3-latest depends_on: migrate: condition: service_completed_successfully postgres: condition: service_healthy redis: condition: service_started deploy: replicas: 3 command: prefect server start --host 0.0.0.0 --no-services environment: PREFECT_API_DATABASE_CONNECTION_URL: postgresql+asyncpg://prefect:prefect@postgres:5432/prefect PREFECT_API_DATABASE_MIGRATE_ON_START: "false" PREFECT_MESSAGING_BROKER: prefect_redis.messaging PREFECT_MESSAGING_CACHE: prefect_redis.messaging PREFECT_SERVER_EVENTS_CAUSAL_ORDERING: prefect_redis.ordering PREFECT_SERVER_CONCURRENCY_LEASE_STORAGE: prefect_redis.lease_storage PREFECT_REDIS_MESSAGING_HOST: redis PREFECT_REDIS_MESSAGING_PORT: "6379" ports: - "4200-4202:4200" # Maps to different ports for each replica prefect-background: image: prefecthq/prefect:3-latest depends_on: migrate: condition: service_completed_successfully postgres: condition: service_healthy redis: condition: service_started command: prefect server services start environment: PREFECT_API_DATABASE_CONNECTION_URL: postgresql+asyncpg://prefect:prefect@postgres:5432/prefect PREFECT_API_DATABASE_MIGRATE_ON_START: "false" PREFECT_MESSAGING_BROKER: prefect_redis.messaging PREFECT_MESSAGING_CACHE: prefect_redis.messaging PREFECT_SERVER_EVENTS_CAUSAL_ORDERING: prefect_redis.ordering PREFECT_SERVER_CONCURRENCY_LEASE_STORAGE: prefect_redis.lease_storage PREFECT_REDIS_MESSAGING_HOST: redis PREFECT_REDIS_MESSAGING_PORT: "6379" volumes: postgres_data: Can't connect to Server API at http://127.0.0.1:4200/api. Check that it's accessible from your machine. why? @Marvin
m
thought for 229.9 seconds
Short answer: your API container isn’t actually publishing port 4200 to your host, so hitting http://127.0.0.1:4200 won’t work. What’s going on: - The
deploy.replicas: 3
section is ignored by docker compose unless you’re using Docker Swarm. You end up with a single
prefect-api
instance. - The ports line
- "4200-4202:4200"
is invalid for compose (a host range can’t map to a single container port). In practice, this means nothing ends up published on 4200. - Bonus: your database URL strings have angle brackets. Those are placeholders in docs; keep the value without
< >
or the DB connection/migration will fail and the API won’t start. How to fix: 1) Publish a single API instance on 4200
Copy code
prefect-api:
  image: prefecthq/prefect:3-latest
  depends_on:
    migrate:
      condition: service_completed_successfully
    postgres:
      condition: service_healthy
    redis:
      condition: service_started
  command: prefect server start --host 0.0.0.0 --no-services
  environment:
    PREFECT_API_DATABASE_CONNECTION_URL: <postgresql+asyncpg://prefect:prefect@postgres:5432/prefect>
    PREFECT_API_DATABASE_MIGRATE_ON_START: "false"
    PREFECT_MESSAGING_BROKER: prefect_redis.messaging
    PREFECT_MESSAGING_CACHE: prefect_redis.messaging
    PREFECT_SERVER_EVENTS_CAUSAL_ORDERING: prefect_redis.ordering
    PREFECT_SERVER_CONCURRENCY_LEASE_STORAGE: prefect_redis.lease_storage
    PREFECT_REDIS_MESSAGING_HOST: redis
    PREFECT_REDIS_MESSAGING_PORT: "6379"
  ports:
    - "4200:4200"
Also update the same DB URL (no angle brackets) in the
migrate
and
prefect-background
services. 2) Verify it’s up - From your host:
curl <http://127.0.0.1:4200/api/health>
- Or check logs:
docker compose logs -f prefect-api
If you truly want multiple API replicas locally: - Don’t use the
deploy:
section with compose; instead use
docker compose up --scale prefect-api=3
. - You cannot publish the same host port from multiple replicas. Put the API behind a reverse proxy/load balancer (e.g., Traefik/Nginx) or run a single API for local dev. Notes: -
--host 0.0.0.0
is correct for binding inside the container. - Your Redis/messaging env vars are valid for Prefect 3. - Default server host/port are 127.0.0.1:4200; binding to 0.0.0.0 and publishing 4200 exposes it to your host. References: - Self-hosted scaling guide: Prefect self-hosted scaling - CLI server commands: prefect server CLI
p
give me the docker compose for productions @Marvin
m
thought for 126.2 seconds
Great — here’s a production-ready docker-compose stack for Prefect 3 with: - Postgres + Redis - A one-shot DB migration job - Horizontally scalable API replicas behind Traefik - Background services on a separate container - Redis-backed messaging required for scaling Important: - Use a custom Prefect image that includes prefect-redis (needed for the Redis messaging backend) - Replace domains/passwords before running Dockerfile (build a custom Prefect image with Redis extras)
Copy code
FROM prefecthq/prefect:3-latest
RUN pip install --no-cache-dir "prefect-redis>=0.5"
docker-compose.yml ``` services: traefik: image: traefik:v3.1 command: - --providers.docker=true - --providers.docker.exposedbydefault=false - --entrypoints.web.address=:80 # Uncomment TLS if you want automatic certs with Let's Encrypt # - --entrypoints.websecure.address=:443 # - --certificatesresolvers.le.acme.tlschallenge=true # - --certificatesresolvers.le.acme.email=${ACME_EMAIL} # - --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json ports: - "80:80" # - "443:443" volumes: - /var/run/docker.sock/var/run/docker.sockro # - letsencrypt:/letsencrypt restart: unless-stopped postgres: image: postgres:15 environment: POSTGRES_USER: ${POSTGRES_USER:-prefect} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env} POSTGRES_DB: ${POSTGRES_DB:-prefect} volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -h localhost -U $$POSTGRES_USER"] interval: 5s timeout: 5s retries: 20 restart: unless-stopped redis: image: redis:7 command: ["redis-server", "--appendonly", "yes"] volumes: - redis_data:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 3s retries: 20 restart: unless-stopped migrate: build: . depends_on: postgres: condition: service_healthy environment: PREFECT_API_DATABASE_CONNECTION_URL: postgresql+asyncpg://${POSTGRES_USER prefect}${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-prefect} command: prefect server database upgrade -y restart: "no" prefect-api: build: . depends_on: migrate: condition: service_completed_successfully postgres: condition: service_healthy redis: condition: service_started environment: PREFECT_API_DATABASE_CONNECTION_URL: postgresql+asyncpg://${POSTGRES_USER prefect}${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-prefect} PREFECT_API_DATABASE_MIGRATE_ON_START: "false" # Redis-backed messaging for scaled deployments PREFECT_MESSAGING_BROKER: prefect_redis.messaging PREFECT_MESSAGING_CACHE: prefect_redis.messaging PREFECT_SERVER_EVENTS_CAUSAL_ORDERING: prefect_redis.ordering PREFECT_SERVER_CONCURRENCY_LEASE_STORAGE: prefect_redis.lease_storage PREFECT_REDIS_MESSAGING_HOST: redis PREFECT_REDIS_MESSAGING_PORT: "6379" # Optional auth if your Redis requires it # PREFECT_REDIS_MESSAGING_USERNAME: default # PREFECT_REDIS_MESSAGING_PASSWORD: yourpassword command: prefect server start --host 0.0.0.0 --no-services labels: - traefik.enable=true - traefik.http.routers.prefect.rule=Host(
${PREFECT_HOST}
) - traefik.http.routers.prefect.entrypoints=web # For TLS via websecure: # - traefik.http.routers.prefect.tls=true # - traefik.http.routers.prefect.tls.certresolver=le - traefik.http.services.prefect.loadbalancer.server.port=4200 restart: unless-stopped prefect-background: build: . depends_on: migrate: condition: service_completed_successfully postgres: condition: service_healthy redis: condition: service_started environment:
PREFECT_API_DATABASE_CONNECTION_URL: postgresql+asyncpg://${POSTGRES_USER prefect}${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-prefect} PREFECT_API_DATABASE_MIGRATE_ON_START: "false" PREFECT_MESSAGING_BROKER: prefect_redis.messaging PREFECT_MESSAGING_CACHE: prefect_redis.messaging PREFECT_SERVER_EVENTS_CAUSAL_ORDERING: prefect_redis.ordering PREFECT_SERVER_CONCURRENCY_LEASE_STORAGE: prefect_redis.lease_storage PREFECT_REDIS_MESSAGING_HOST: redis PREFECT_REDIS_MESSAGING_PORT: "6379" command: prefect server services start restart: unless-stopped volumes: postgres_data: redis_data: # letsencrypt:
Copy code
.env example
# Domain that Traefik should route to your Prefect API/UI PREFECT_HOST=prefect.example.com # DB credentials (use stronger passwords and consider Docker secrets) POSTGRES_USER=prefect POSTGRES_PASSWORD=change-me POSTGRES_DB=prefect # For TLS with Traefik (if you uncomment TLS flags) # ACME_EMAIL=you@example.com
Copy code
How to run
- Build the image (includes prefect-redis):
docker compose build
Copy code
- Start the stack:
docker compose up -d
Copy code
- Scale API replicas (Traefik will load-balance them):
docker compose up -d --scale prefect-api=3 ``` - Verify health: - API: http://your-domain/api/health (or http://localhost if you point DNS to it) - Logs:
docker compose logs -f prefect-api
and
docker compose logs -f prefect-background
Notes - For a single-node/no-LB setup, you can remove Traefik and expose the API directly with
ports: ["4200:4200"]
on
prefect-api
(then don’t scale). - For production, consider managed Postgres/Redis and externalize their backups and monitoring. - The Redis-backed settings are required for multi-replica API deployments and robust event/concurrency handling in Prefect 3. Docs - Self-hosted scaling: Prefect self-hosted scaling - Server CLI: prefect server CLI