pradana adnyana
12/24/2025, 6:25 AMMarvin
12/24/2025, 6:26 AMMarvin
12/24/2025, 6:28 AMexport PREFECT_SERVER_API_AUTH_STRING="username:strongpassword"
# then start your server (e.g., prefect server start ...)
- On every client that talks to your server (workers, CLI, flows, automations):
export PREFECT_API_AUTH_STRING="username:strongpassword"
Notes:
- Requests must include Basic auth; health endpoints remain open.
- WebSockets are protected (needed for live UI features).
- There is no built‑in OIDC/SAML on self‑hosted; API keys are Cloud‑only.
SSO/OIDC via reverse proxy (recommended for enterprise setups)
- Put an auth proxy in front of Prefect (e.g., oauth2‑proxy) and terminate TLS at your reverse proxy (NGINX/Traefik).
- Minimal NGINX + oauth2‑proxy example:
# oauth2-proxy listens on :4180 and handles OIDC with your IdP
# NGINX protects all routes and forwards to Prefect on :4200
location = /oauth2/auth {
proxy_pass <http://oauth2-proxy:4180;>
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
auth_request /oauth2/auth;
error_page 401 = /oauth2/sign_in;
proxy_pass <http://prefect-server:4200;>
# WebSocket support (important for UI)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
- Traefik users can use a forwardAuth middleware pointing at oauth2‑proxy.
- You can run with only the proxy auth, or keep Prefect’s Basic auth enabled for defense‑in‑depth.
Recommended security settings
- Enable CSRF protection on the server:
export PREFECT_SERVER_API_CSRF_PROTECTION_ENABLED=true
- If you’re behind a proxy, set the UI -> API URL so the UI targets the public address:
export PREFECT_UI_API_URL="<https://prefect.example.com/api>"
Docs and references
- Security settings: Security settings (Prefect 3)
- Self‑hosting guide: Self‑hosted Prefect
- Helm (if you’re on Kubernetes): Server Helm guide
- Settings reference: Settings reference
Want me to tailor a config for your setup? Let me know:
- Prefect version (2.x or 3.x)?
- Where you’re running (Docker Compose, Kubernetes, bare VM)?
- Which proxy (NGINX, Traefik) and whether you want OIDC (which IdP)?pradana adnyana
12/24/2025, 6:30 AMMarvin
12/24/2025, 6:30 AMMarvin
12/24/2025, 6:32 AMversion: "3.9"
services:
postgres:
image: postgres:15
environment:
POSTGRES_USER: prefect
POSTGRES_PASSWORD: prefect
POSTGRES_DB: prefect
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U prefect"]
interval: 5s
timeout: 5s
retries: 10
redis:
image: redis:7
volumes:
- redis_data:/data
healthcheck:
test: ["CMD-SHELL", "redis-cli ping || exit 1"]
interval: 5s
timeout: 5s
retries: 10
prefect-server:
image: prefecthq/prefect:3-latest
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
environment:
# Database
PREFECT_API_DATABASE_CONNECTION_URL: <postgresql+asyncpg://prefect:prefect@postgres:5432/prefect>
# Messaging (required in Prefect 3)
PREFECT_MESSAGING_BROKER: prefect_redis.messaging
PREFECT_MESSAGING_CACHE: prefect_redis.messaging
PREFECT_REDIS_MESSAGING_HOST: redis
PREFECT_REDIS_MESSAGING_PORT: 6379
# Security: Basic auth and CSRF
PREFECT_SERVER_API_AUTH_STRING: ${PREFECT_AUTH_STRING}
PREFECT_SERVER_API_CSRF_PROTECTION_ENABLED: "true"
# Bind API to container network
PREFECT_SERVER_API_HOST: 0.0.0.0
command: prefect server start --host 0.0.0.0 --no-services
ports:
- "4200:4200"
healthcheck:
test: ["CMD-SHELL", "python -c 'import urllib.request as u; u.urlopen(\"<http://localhost:4200/api/health\>", timeout=1)' || exit 1"]
interval: 30s
timeout: 10s
retries: 5
start_period: 60s
prefect-services:
image: prefecthq/prefect:3-latest
depends_on:
prefect-server:
condition: service_healthy
environment:
PREFECT_API_DATABASE_CONNECTION_URL: <postgresql+asyncpg://prefect:prefect@postgres:5432/prefect>
PREFECT_MESSAGING_BROKER: prefect_redis.messaging
PREFECT_MESSAGING_CACHE: prefect_redis.messaging
PREFECT_REDIS_MESSAGING_HOST: redis
PREFECT_REDIS_MESSAGING_PORT: 6379
command: prefect server services start
# Example worker that connects to your self-hosted API using Basic Auth
prefect-worker:
image: prefecthq/prefect:3-latest
depends_on:
prefect-server:
condition: service_healthy
environment:
PREFECT_API_URL: <http://prefect-server:4200/api>
PREFECT_API_AUTH_STRING: ${PREFECT_AUTH_STRING}
command: prefect worker start --pool default
restart: on-failure
volumes:
postgres_data:
redis_data:
.env (same directory as docker-compose.yml):
# Use strong credentials; do not commit this file
PREFECT_AUTH_STRING=admin:change-me-now
How to run
- Start the stack: docker compose up -d
- Open the UI: http://localhost:4200 (you’ll be prompted for Basic auth)
- Verify API health with auth:
curl -u admin:change-me-now <http://localhost:4200/api/health>
- Ensure all clients (workers, CLI on your machine, job containers) set:
PREFECT_API_URL=http://<host-or-service>:4200/api
PREFECT_API_AUTH_STRING=admin:change-me-now
Notes and tips
- Do not set PREFECT_API_KEY on self-hosted; it’s for Prefect Cloud.
- If you put Prefect behind a reverse proxy at a public URL, set the UI -> API URL so the in-browser UI calls the right API:
PREFECT_UI_API_URL=<https://prefect.example.com/api>
- For SSO/OIDC, add an auth proxy (e.g., oauth2-proxy) and front with NGINX/Traefik in the same compose stack. I can share a ready-to-run oauth2-proxy + NGINX compose add‑on if you want that pattern.
Docs
- Security settings (Basic auth, CSRF): Prefect 3 Security settingsMarvin
12/24/2025, 6:32 AMpradana adnyana
12/24/2025, 6:42 AMMarvin
12/24/2025, 6:42 AMMarvin
12/24/2025, 6:42 AMdocker compose logs --no-color postgres
Common causes:
- Existing volume from a different Postgres major version (e.g., old data from 14 but image is 15) → Postgres exits with “data directory … is not compatible” or similar.
- Corrupted or partially-initialized volume.
- Permissions/ownership issues on the mounted data directory.
If you don’t need the existing DB, reset the volume:
docker compose down
docker volume ls | grep postgres_data
docker volume rm <yourproject>_postgres_data
docker compose up -d
If you do need the data and it was created by another version, you’ll need to upgrade with pg_upgrade or stick to the original major version.
Step 2: Fix the Compose variables (these will cause failures later)
You’ve got angle brackets in a few environment values that will make the URLs invalid. Replace these:
- In prefect-server and prefect-services:
# WRONG
PREFECT_API_DATABASE_CONNECTION_URL: <postgresql+asyncpg://prefect>:${POSTGRES_PASSWORD:-prefect}@postgres:5432/prefect
# RIGHT
PREFECT_API_DATABASE_CONNECTION_URL: <postgresql+asyncpg://prefect:${POSTGRES_PASSWORD:-prefect}@postgres:5432/prefect>
- In prefect-worker:
# WRONG
PREFECT_API_URL: <http://prefect-server:4200/api>
# RIGHT
PREFECT_API_URL: <http://prefect-server:4200/api>
Optional: put secrets in a .env (same folder as docker-compose.yml) and keep angle brackets out of values:
POSTGRES_PASSWORD=your-strong-password
PREFECT_AUTH_STRING=admin:another-strong-password
Step 3: Re-run and verify
docker compose up -d
docker compose ps
docker compose logs -f postgres
When the API is up, verify:
curl -u admin:another-strong-password <http://localhost:4200/api/health>
If Postgres still exits
- Please paste the output of docker compose logs postgres here. The exact message will tell us whether it’s a version mismatch, permission issue, or something else, and I’ll give you precise next steps.pradana adnyana
12/24/2025, 6:55 AMMarvin
12/24/2025, 6:55 AMMarvin
12/24/2025, 6:56 AMprefect profile create self-hosted
prefect profile use self-hosted
prefect config set PREFECT_API_URL="<http://10.101.3.91:4200/api>"
prefect config set PREFECT_API_AUTH_STRING="admin:your-password"
- Verify:
prefect config view --show-sources
- Test:
prefect server status # or any command that hits the API
If you previously set a Cloud API key, unset it so it doesn’t override:
prefect config unset PREFECT_API_KEY
Set it directly in profiles.toml
- Edit ~/.prefect/profiles.toml and add:
active = "self-hosted"
[profiles.self-hosted]
PREFECT_API_URL = "<http://10.101.3.91:4200/api>"
PREFECT_API_AUTH_STRING = "admin:your-password"
Using environment variables instead (per-process)
export PREFECT_API_URL="<http://10.101.3.91:4200/api>"
export PREFECT_API_AUTH_STRING="admin:your-password"
Common pitfalls
- Don’t set PREFECT_API_KEY for self-hosted; it’s Cloud-only and will take precedence over PREFECT_API_AUTH_STRING.
- Make sure the server side is set with the same credentials:
PREFECT_SERVER_API_AUTH_STRING=admin:your-password
- If you access the UI via a different host (proxy), set UI to point at that API:
PREFECT_UI_API_URL="<https://your-domain.example.com/api>"
If it still 401s, try:
- curl -u admin:your-password <http://10.101.3.91:4200/api/health>
- prefect config view --show-secrets to confirm the auth string is loaded in the active profile.Bring your towel and join one of the fastest growing data communities. Welcome to our second-generation open source orchestration platform, a completely rethought approach to dataflow automation.
Powered by