<@ULVA73B9P> i deployed prefect with ecs and appli...
# ask-marvin
m
@Marvin i deployed prefect with ecs and application load balancer, now i want a reverse proxy to have ui with user authentication
m
thought for 329.7 seconds
Great question. You’ve got a few solid patterns to put Prefect Server behind a reverse proxy and add user authentication to the UI while keeping your workers able to reach the API. Below are two battle-tested approaches for ECS + ALB. I’ll call out the Prefect 3.x settings you’ll need either way and then share example configs. Key Prefect settings you’ll use - server.api.base_path — lets you mount the API at a path prefix (e.g., /prefect/api) - server.api.auth_string — optional shared-secret Basic auth for the API - server.api.csrf_protection_enabled — recommended when serving the UI - server.api.cors_allowed_origins — lock this down if you must cross origins - server.ui.serve_base — where the UI is served (e.g., /prefect) - server.ui.api_url — where the UI should call the API (e.g., https://ui.example.com/prefect/api) - PREFECT_UI_URL — external URL used in links (e.g., https://ui.example.com/prefect) - PREFECT_API_URL — what workers/clients use to reach the API Refs: - Security settings - Settings reference Option 1 (recommended in AWS): Public ALB with OIDC for UI + Internal LB for workers Goal: Make the UI public and authenticated; keep the API reachable by workers without human auth. - Public “UI ALB” - Internet-facing ALB with an Authenticate action (AWS Cognito or your OIDC provider) on all paths (including /api) so browser users must log in. - Forwards to your Prefect ECS service target group. - Domain: ui.your-domain.com - Internal “API LB” - Internal ALB or NLB in your VPC that forwards to the same ECS service without any auth. - Restrict access via security groups (only your ECS tasks/worker hosts can reach it). - DNS: internal-prefect-api.your-vpc.local (or similar) - Prefect config - Make UI and API same-origin for users (the UI ALB domain) to avoid CORS. - Point workers to the internal LB. Example profile (prefect.toml) or equivalent env:
Copy code
[server.api]
host = "0.0.0.0"
port = 4200
base_path = "/api"
csrf_protection_enabled = true
cors_allowed_origins = "<https://ui.your-domain.com>"

[server.ui]
enabled = true
serve_base = "/"
api_url = "<https://ui.your-domain.com/api>"

[ui]
url = "<https://ui.your-domain.com>"
Workers (task definition env) use the internal endpoint:
Copy code
PREFECT_API_URL=<http://internal-prefect-api.your-vpc.local/api>
Notes: - WebSockets/log streaming: ALB supports WebSockets. Ensure idle timeouts are high enough and no auth prompts interfere with ws under /api. - This keeps your public surface area protected by OIDC while workers bypass auth via the internal LB. Option 2: Single public ALB -> NGINX reverse proxy that enforces login and injects API auth Goal: One public endpoint; NGINX handles user login and lets UI call the API; API requires a shared secret for all non-proxied clients. - Public ALB - Just forwards traffic to an NGINX service (sidecar or separate ECS service). - NGINX - Enforce OIDC with oauth2-proxy (or nginx-openidc) for all UI paths. - For requests to /api from authenticated users, inject a Basic Authorization header with your Prefect API auth secret so the UI can call the API. - Prefect config - Enable Prefect API auth string so only clients with the secret can call the API. - Workers set the same auth string in their environment. Prefect settings:
Copy code
[server.api]
host = "0.0.0.0"
port = 4200
base_path = "/api"
csrf_protection_enabled = true
auth_string = "admin:supersecret"   # store securely

[server.ui]
enabled = true
serve_base = "/"
api_url = "<https://ui.your-domain.com/api>"

[ui]
url = "<https://ui.your-domain.com>"
Worker env:
Copy code
PREFECT_API_URL=<https://ui.your-domain.com/api>
PREFECT_API_AUTH_STRING=admin:supersecret
NGINX sketch (with oauth2-proxy) that injects the header for UI-originated API calls: ``` # OIDC gate for UI and API location /oauth2/ { proxy_pass http://oauth2-proxy; } # UI location / {
auth_request /oauth2/auth; error_page 401 = /oauth2/sign_in; proxy_pass http://prefect:4200/; proxy_set_header Host $host; } # API (inject Prefect auth so the UI can call it) location /api/ { auth_request /oauth2/auth; error_page 401 = /oauth2/sign_in; proxy_set_header Authorization "Basic <base64(admin:supersecret)>"; proxy_pass http://prefect:4200/api/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } ``` When to choose which - Option 1 (two LBs) is clean if you’re all-in on ALB-auth and want the API private to your VPC. No header injection needed. - Option 2 (NGINX) is great if you prefer a single public endpoint and want to lock the API with Prefect’s shared secret while letting the UI access it via the proxy. A few gotchas to watch for - Keep UI and API same-origin for the browser if at all possible. Otherwise you’ll need to tune CORS and CSRF. - If you use a path prefix (e.g., /prefect), set both server.api.base_path and server.ui.serve_base consistently and update server.ui.api_url, PREFECT_UI_URL, and PREFECT_API_URL accordingly. - Make sure WebSocket upgrade headers are preserved through your proxy for log/event streaming. - Do not enable server.api.auth_string unless all non-proxied clients (workers, CLI) are configured to send it. Questions to tailor the final setup 1) Which auth provider do you want to use: AWS Cognito via ALB, or another OIDC provider (Keycloak/Okta/etc.)? 2) Do you want the API to be private to the VPC (recommended), or is a single public endpoint preferred? 3) Will you serve Prefect at the root (/) or behind a prefix (e.g., /prefect)? 4) What domain will you use for the UI? If you share those, I’ll provide a concrete step-by-step with exact ALB rules and the final Prefect settings for your ECS service.