Madushan Pathirana
09/09/2025, 2:54 PMMarvin
09/09/2025, 2:54 PMMarvin
09/09/2025, 3:00 PMPREFECT_SERVER_UI_API_URL
to the API URL.
Links for reference:
- Prefect 3: Self-hosted Server
- Prefect 3: Security settings
Assumptions
- Ubuntu on EC2 with a security group allowing 80 (HTTP) and 443 (HTTPS).
- You have DNS records for:
- ui.your-domain.com → your EC2 public IP
- api.your-domain.com → your EC2 public IP
- You’ll run Prefect Server bound to 127.0.0.1:4200 and use RDS for Postgres.
1) Install NGINX and Certbot
sudo apt-get update
sudo apt-get install -y nginx apache2-utils
# Certbot (Let's Encrypt)
sudo snap install core
sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
2) Create a Basic Auth file for the UI
# Create /etc/nginx/.htpasswd and add a user
sudo htpasswd -c /etc/nginx/.htpasswd admin
# (you’ll be prompted to set a password)
3) NGINX config (two virtual hosts: one for UI with Basic Auth, one for API without)
Create file: /etc/nginx/sites-available/prefect-ui
server {
listen 80;
server_name <http://ui.your-domain.com|ui.your-domain.com>;
# Increase if needed for file uploads
client_max_body_size 50m;
location / {
# Basic auth for UI only
auth_basic "Prefect UI";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass <http://127.0.0.1:4200;>
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# WebSocket/SSE headers (safe to include)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Create file: /etc/nginx/sites-available/prefect-api
server {
listen 80;
server_name <http://api.your-domain.com|api.your-domain.com>;
client_max_body_size 50m;
# NO Basic Auth on API so workers/CLI can connect
# Optional: Add IP allowlist to lock down API access further
# Example: allow your office IP only
# allow 203.0.113.10;
# deny all;
location / {
proxy_pass <http://127.0.0.1:4200;>
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Enable and test:
sudo ln -s /etc/nginx/sites-available/prefect-ui /etc/nginx/sites-enabled/prefect-ui
sudo ln -s /etc/nginx/sites-available/prefect-api /etc/nginx/sites-enabled/prefect-api
sudo nginx -t
sudo systemctl reload nginx
4) Get HTTPS certificates
sudo certbot --nginx -d <http://ui.your-domain.com|ui.your-domain.com> -d <http://api.your-domain.com|api.your-domain.com>
# Follow prompts; this will add SSL server blocks and reload NGINX
5) Start Prefect Server bound to localhost
- Prefect Server typically runs on 127.0.0.1:4200. We’ll:
- Tell the UI which public API URL to use: PREFECT_SERVER_UI_API_URL=<https://api.your-domain.com/api>
- Optionally set PREFECT_UI_URL=<https://ui.your-domain.com>
for nice links in CLI output
- Point your workers/CLI (on the same box) to localhost so they don’t go through Basic Auth: PREFECT_API_URL=<http://127.0.0.1:4200/api>
Example: run server manually
```Marvin
09/09/2025, 3:00 PMIf you want it as a systemd service, I can provide a unit file template.
6) Point your workers/CLI at the API
- On this EC2 host (same machine), before starting workers or running CLI:
export PREFECT_API_URL="http://127.0.0.1:4200/api"
- If you run workers from another machine, use the public API instead:
export PREFECT_API_URL="https://api.your-domain.com/api"
```
Note: Don’t protect the API endpoint with Basic Auth unless you have a way for workers/CLI to send Authorization headers (Prefect does not add them automatically). Use IP allowlists or private networking instead if you need to restrict access.
7) Verify
- Open https://ui.your-domain.com — you should be prompted for the Basic Auth credentials you created.
- The UI should load and talk to the API at https://api.your-domain.com/api.
- Workers should be able to connect to the API (localhost or public) without authentication prompts.
Optional: Secure the API more
- If all workers run on the same EC2 host (your case), you can:
- Keep workers/CLI pointed at http://127.0.0.1:4200/api
- Restrict the public API by IP allowlist in NGINX, or even avoid exposing api.your-domain.com at all. In that case, the UI won’t work from the public internet (you could access via VPN or SSH tunnel when needed).
Would you like me to:
- Generate a systemd unit file for Prefect Server with your RDS Postgres connection?
- Provide an NGINX config that uses a single hostname (UI + API together), accepting the trade-offs?
If you share your actual domain names and whether you want the API publicly reachable, I’ll tailor the exact config.