Kyle McChesney
10/16/2025, 5:10 PMMarvin
10/16/2025, 5:10 PMMarvin
10/16/2025, 5:25 PMprefecthq/prefect:3-latest image as another service
- Wires Prefect to Postgres via PREFECT_SERVER_DATABASE_CONNECTION_URL
- Waits until the API is ready
Assumes Prefect 3.x.
name: Prefect Server in CI
on:
push:
pull_request:
jobs:
prefect-server:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: prefect
POSTGRES_PASSWORD: prefect
POSTGRES_DB: prefect
ports:
- 5432:5432
options: >-
--health-cmd="pg_isready -U prefect"
--health-interval=5s
--health-timeout=5s
--health-retries=20
prefect:
image: prefecthq/prefect:3-latest
# Expose the API/UI port to the job runner
ports:
- 4200:4200
env:
# Bind API inside the container to all interfaces so the job can reach it
PREFECT_SERVER_API_HOST: 0.0.0.0
# Point Prefect at the Postgres service.
# Note: use the service name "postgres" as the hostname.
PREFECT_SERVER_DATABASE_CONNECTION_URL: <postgresql+asyncpg://prefect:prefect@postgres:5432/prefect>
# Optional: enable UI in CI if you intend to hit it (usually not needed)
PREFECT_UI_ENABLED: "true"
# Optional: make migrations more forgiving in slow CI environments
# PREFECT_SERVER_DATABASE_CONNECTION_TIMEOUT: "10"
# PREFECT_SERVER_DATABASE_TIMEOUT: "30"
# Start Prefect Server
command:
[
"prefect", "server", "start",
"--host", "0.0.0.0",
"--analytics-off"
]
steps:
- uses: actions/checkout@v4
# Wait for Prefect API to become ready
- name: Wait for Prefect API
run: |
echo "Waiting for Prefect API on <http://localhost:4200>..."
for i in {1..60}; do
if curl -sf <http://localhost:4200/api/health> > /dev/null; then
echo "Prefect API is up!"
exit 0
fi
sleep 2
done
echo "Timed out waiting for Prefect API"
exit 1
# Example: prove it works
- name: Hit Prefect API
run: curl -sSf <http://localhost:4200/api/health> | jq .
Key points:
- Use the async SQLAlchemy driver in your URL: postgresql+asyncpg://...
- In GitHub Actions, service containers can talk to each other via their service name (e.g., postgres) as the hostname.
- Binding PREFECT_SERVER_API_HOST=0.0.0.0 (and/or --host 0.0.0.0) ensures the API is reachable from the job via the mapped port.
- Default API port is 4200; the example maps it so the job can poll /api/health.
Useful references:
- Prefect 3 settings reference (env vars): Settings reference
- Self-hosted via Docker: Server in Docker
- Docker Compose examples: Docker Compose guide
If you prefer component-wise env vars instead of a full URL, you can set:
- PREFECT_SERVER_DATABASE_DRIVER=postgresql+asyncpg
- PREFECT_SERVER_DATABASE_HOST=postgres
- PREFECT_SERVER_DATABASE_PORT=5432
- PREFECT_SERVER_DATABASE_USER=prefect
- PREFECT_SERVER_DATABASE_PASSWORD=prefect
- PREFECT_SERVER_DATABASE_NAME=prefect
Do you also need the UI during CI, or just the API for tests? I can tailor the workflow accordingly.