<@ULVA73B9P> I'm deploying prefect in an k8s clust...
# marvin-ai
k
@Marvin I'm deploying prefect in an k8s cluster (locally) and so far, I'm using `values-prefect.yaml`:
Copy code
- |
        helm repo add prefect <https://prefecthq.github.io/prefect-helm/>
        helm repo update
        kubectl create namespace prefect
        helm install prefect-server prefect/prefect-server -n prefect -f manifests-dependencies/values-prefect.yaml
Now, this is an issues, since I'm using traefik for routing, so localhost:4200 does not work. It should connect to
prefect.localhost:8080/api
. I suppose I can configure this in values-prefect.yaml?
Copy code
nameOverride: "prefect-server"
fullnameOverride: "prefect-server"
namespaceOverride: "prefect"
postgresql:
  auth:
    database: prefect-server
    username: prefect-default-user
    password: prefect-default-password
serviceAccount:
  name: "prefect-server"
m
thought for 291.4 seconds
k
@Marvin (or should I move to using raw manifests or helm charts, instead of the values-prefect.yaml approach?)
m
thought for 203.1 seconds
Short answer: stick with the Helm chart + values file. That’s the recommended and supported path for Prefect 3.x on Kubernetes. Only reach for raw manifests if you have very specific, non-templatable needs or strict org policies. Here’s how I’d think about it: - Helm + values (your values-prefect.yaml): recommended, supported, and easiest to maintain and upgrade - Helm-rendered manifests (GitOps): if your platform doesn’t allow Helm at runtime, render once and
kubectl apply
the output - Hand-written raw manifests: only if you truly need full object-level control that the chart cannot express What to use where - Workers: use the official prefect-worker chart; point it at your work pool and server URL - Server: use the prefect-server chart if you want to run Prefect Server in-cluster. Note: the server chart uses Postgres; SQLite is for local dev and not supported in the Helm chart Minimal worker values example for your self-hosted Server (adjust the API URL and work pool name)
Copy code
worker:
  apiConfig: selfHostedServer
  selfHostedServerApiConfig:
    apiUrl: http://<your-server-host>:4200/api
  config:
    workPool: <your-k8s-work-pool-name>
Install/upgrade with Helm (keep using your values-prefect.yaml)
Copy code
helm repo add prefect <https://prefecthq.github.io/prefect-helm>
helm repo update
helm upgrade --install prefect-worker prefect/prefect-worker \
  -n prefect -f values-prefect.yaml
If you need raw manifests but still want chart features (render-only)
Copy code
helm template prefect-worker prefect/prefect-worker \
  -n prefect -f values-prefect.yaml > rendered.yaml
kubectl apply -n prefect -f rendered.yaml
Optional: server chart basic values (uses Postgres)
Copy code
postgresql:
  enabled: true
  auth:
    password: "change-me"

backgroundServices:
  runAsSeparateDeployment: true

redis:
  enabled: true
Install:
Copy code
helm upgrade --install prefect-server prefect/prefect-server \
  -n prefect -f server-values.yaml
Tip: you don’t need raw manifests to control the flow-run Pods. Use the Kubernetes work pool’s “base job template” to set things like resources, node selectors, tolerations, volumes, sidecars, etc.; the worker chart just deploys the worker Deployment. Links - Prefect Helm charts (prefect-worker, prefect-server) - Kubernetes how-to guide - Work pools concept If you share your current values-prefect.yaml and your Server URL (in/out of cluster), I’m happy to sanity-check it and suggest the exact keys to set.
Yes — you can (and should) configure this via the Helm chart values. Two things matter here: - Ingress: expose the Prefect Server service via Traefik on your desired host/path - UI config: point the browser to your ingress URL so the UI calls the right API (defaults to http://localhost:4200/api otherwise) Try extending your
values-prefect.yaml
like this:
Copy code
nameOverride: "prefect-server"
fullnameOverride: "prefect-server"
namespaceOverride: "prefect"

# Expose Prefect through Traefik
ingress:
  enabled: true
  className: "traefik"     # adjust if your IngressClass is named differently
  host:
    hostname: "prefect.localhost"
    path: "/"
    pathType: "Prefix"
  annotations:
    # If your Traefik entrypoint for HTTP is named "web" (default)
    <http://traefik.ingress.kubernetes.io/router.entrypoints|traefik.ingress.kubernetes.io/router.entrypoints>: "web"
    # If you really terminate on :8080, ensure Traefik is listening there or your NodePort maps to 8080

service:
  type: ClusterIP
  port: 4200
  targetPort: 4200
  portName: "server-svc-port"

# Tell the UI which URL to use for API calls (critical)
server:
  uiConfig:
    # Use your external URL (Traefik) with /api
    prefectUiApiUrl: "<http://prefect.localhost:8080/api>"

postgresql:
  auth:
    database: prefect-server
    username: prefect-default-user
    password: prefect-default-password

serviceAccount:
  name: "prefect-server"
Notes: - If Traefik serves HTTP on :80 (common), change
prefectUiApiUrl
to
<http://prefect.localhost/api>
(no port) and drop the 8080. If you’re actually terminating on :8080 locally, keep it as you proposed. - If you enable TLS on Traefik, change to
<https://prefect.localhost/api>
and add the appropriate TLS config/annotations to the ingress. - If any workers/clients run outside the cluster and need to reach the API through Traefik, set
PREFECT_API_URL
in those environments to
<http://prefect.localhost:8080/api>
. In-cluster workers can use the cluster DNS, e.g.
<http://prefect-server.prefect.svc.cluster.local:4200/api>
. References: - prefect-helm: prefect-server chart - values.yaml - chart README (server) Quick check: - Is your Traefik IngressClass actually named
traefik
? - Do you really want to expose on :8080 rather than :80? If not, I can adjust the snippet accordingly.
k
@Marvin right, now, about the db, I'm having a cloudnative postgres operator, which is configurable (helm charts). Having the approach of
values-prefect.yaml
, I can't have variable auth here, right? So how would that work properly? Meaning, how to set the auth of the prefect db the same as configured in
apiVersion: <http://postgresql.cnpg.io/v1|postgresql.cnpg.io/v1>
m
thought for 73.3 seconds
Great question. With CNPG (CloudNativePG), you typically don’t want the Prefect chart to manage Postgres at all — you want Prefect to point at the CNPG cluster credentials. In the prefect-server Helm chart, the supported way to do this is: - Disable the bundled Postgres subchart - Provide a Secret that contains a single key named
connection-string
with the full SQLAlchemy URL - Point the chart to that Secret Two ways to supply the Secret: Option A — Let the Prefect chart create the Secret from fields (useful for static values or when you can template them in your values file)
Copy code
postgresql:
  enabled: false

secret:
  create: true
  # name: ""              # optional; if omitted, chart will name it for you
  username: "prefect"
  password: "your-password"
  host: "my-cnpg-rw.default.svc.cluster.local"
  port: "5432"
  database: "prefect"
The chart will generate and store:
Copy code
connection-string: <postgresql+asyncpg://prefect:your-password@my-cnpg-rw.default.svc.cluster.local:5432/prefect>
Option B — Use the Secret created by CNPG (recommended) CNPG usually creates a Secret with individual fields (
host
,
port
,
user
,
password
,
dbname
) rather than a single connection string. Prefect expects a single
connection-string
key. You have two sub-options: - Create a small “wrapper” Secret from the CNPG Secret values:
Copy code
# Example: construct Secret once (or via your GitOps pipeline)
kubectl create secret generic prefect-db-connection \
  -n prefect \
  --from-literal=connection-string="postgresql+asyncpg://<user>:<pass>@<host>:<port>/<dbname>"
Then in your values:
Copy code
postgresql:
  enabled: false

secret:
  create: false
  name: "prefect-db-connection"
- Or template a Helm helper to build the string from the CNPG secret’s keys (if you control the Helm release that also knows the CNPG Secret name). In practice, most teams prefer the wrapper Secret for clarity. A few tips: - Don’t put
host: host:port
in the values; host and port are separate fields if you use
secret.create: true
. - If you want TLS to Postgres, include params in the connection string (e.g.,
?ssl=verify-ca
) and mount your CA at
/home/prefect/.postgresql/root.crt
via `server.extraVolumes`/`server.extraVolumeMounts`. - Keep
postgresql.enabled: false
whenever you’re using CNPG. If you can share your CNPG Secret name and its keys, I can show the exact kubectl command to generate the Prefect-compatible wrapper Secret, or convert it in-place with a short script.
k
@Marvin how would
prefect-db-connection
look like. That's just a secret type, I suppose, what which
data
does it need?
m
thought for 18.5 seconds
You got it — it’s a plain Opaque Secret with a single key named
connection-string
. The value is the full SQLAlchemy URL Prefect needs. Format of the connection string:
Copy code
postgresql+asyncpg://<user>:<password>@<host>:<port>/<database>
YAML manifest (using stringData for convenience):
Copy code
apiVersion: v1
kind: Secret
metadata:
  name: prefect-db-connection
  namespace: prefect
type: Opaque
stringData:
  connection-string: <postgresql+asyncpg://prefect:your-password@my-cnpg-rw.default.svc.cluster.local:5432/prefect>
Or create it via kubectl (auto base64-encodes):
Copy code
kubectl create secret generic prefect-db-connection \
  -n prefect \
  --from-literal=connection-string="<postgresql+asyncpg://prefect:your-password@my-cnpg-rw.default.svc.cluster.local:5432/prefect>"
Then in your Helm values:
Copy code
postgresql:
  enabled: false

secret:
  create: false
  name: "prefect-db-connection"
If you want to build it from a CNPG secret’s fields:
Copy code
HOST=$(kubectl get secret my-cnpg-app -n <ns> -o jsonpath='{.data.host}' | base64 -d)
PORT=$(kubectl get secret my-cnpg-app -n <ns> -o jsonpath='{.data.port}' | base64 -d)
USER=$(kubectl get secret my-cnpg-app -n <ns> -o jsonpath='{.data.user}' | base64 -d)
PASS=$(kubectl get secret my-cnpg-app -n <ns> -o jsonpath='{.data.password}' | base64 -d)
DB=$(kubectl get secret my-cnpg-app -n <ns> -o jsonpath='{.data.dbname}' | base64 -d)

kubectl create secret generic prefect-db-connection -n prefect \
  --from-literal=connection-string="postgresql+asyncpg://${USER}:${PASS}@${HOST}:${PORT}/${DB}"
Notes: - Only the
connection-string
key is required; no separate `user`/`password` keys. - If your password has special characters, URL‑encode it in the connection string. - For TLS, append parameters like
?ssl=verify-ca
and mount your CA at
/home/prefect/.postgresql/root.crt
via `server.extraVolumes`/`server.extraVolumeMounts`. Chart refs: - prefect-server chart - values.yaml
k
@Marvin oh, you put a connection string in there. Shouldn't we add the dbname, host, password etc seperately?
m
thought for 39.4 seconds
Good instinct to ask — many operators emit per-field secrets, but the Prefect Server chart expects a single connection URL. What the chart actually reads: - It sets
PREFECT_API_DATABASE_CONNECTION_URL
from a Secret key named
connection-string
- There are no separate
host/user/password/dbname
keys consumed by the chart From the chart’s Deployment:
Copy code
- name: PREFECT_API_DATABASE_CONNECTION_URL
  valueFrom:
    secretKeyRef:
      name: <your-secret-name>
      key: connection-string
So your options are: - Provide that one Secret with
data.connection-string
(or
stringData.connection-string
), e.g.
Copy code
postgresql:
    enabled: false
  secret:
    create: false
    name: prefect-db-connection
and the Secret:
Copy code
apiVersion: v1
  kind: Secret
  metadata:
    name: prefect-db-connection
    namespace: prefect
  type: Opaque
  stringData:
    connection-string: <postgresql+asyncpg://user:pass@host:5432/dbname>
- Or let the chart build that connection string for you if you can specify the fields in values:
Copy code
postgresql:
    enabled: false
  secret:
    create: true
    username: "user"
    password: "pass"
    host: "host.example.svc.cluster.local"
    port: "5432"
    database: "dbname"
If your CNPG secret exposes separate keys (
host
,
port
,
user
,
password
,
dbname
), you’ll need to either: - Create a wrapper Secret that assembles those into one
connection-string
, or - Template a Helm helper in your GitOps to generate
connection-string
from the CNPG secret at deploy time Happy to help craft the wrapper Secret from your CNPG secret if you share its name/namespace.