Skip to content

Quick start guide feedback #1245

Description

@ivanfeanor

Salut. First of all, thanks for the great product. I've tried to provide some feedback based on my quick (no so quick) start experience to maybe help you improve your documentation and fix a few things.

Self-hosting on Kubernetes (Azure AKS) — Documentation & Chart Issues

I deployed Probo v0.192.0 to AKS with external Azure Postgres Flexible Server and (initially) Azure Blob Storage via S3Proxy, following the Kubernetes self-hosting docs. The deployment ultimately works, but I hit a number of blockers that took hours to diagnose — most of them avoidable with documentation or small chart fixes. Sharing in case it helps prioritize improvements.

Environment

  • AKS, Kubernetes 1.35, Helm 4.2
  • Azure Postgres Flexible Server (PostgreSQL 18.3, France Central)
  • Azure Blob Storage → AWS S3 (after S3Proxy failed)
  • Traefik 3.x + cert-manager (Let's Encrypt)
  • Probo chart 0.1.0 / app 0.192.0

Issues encountered, roughly in order

1. OCI chart registry returns 401 for anonymous pulls

helm install probo oci://artifact.probo.inc/charts/probo (from the docs) returns 401 Unauthorized: project charts not found. Anonymous pulls don't work; no auth instructions in the docs. Worked around by cloning the repo and installing from contrib/helm/charts/probo.

Suggestion: either expose the chart anonymously, publish it to a public registry (Artifact Hub, etc.), or document the auth flow.

2. Docs --set postgresql.host=... doesn't actually wire up the database

The Kubernetes docs example uses:
--set postgresql.host="..." --set postgresql.password="..."
But the Deployment template reads .Values.probo.pg.addr, not .Values.postgresql.host. Result: chart fails at render time with nil pointer evaluating interface {}.addr. Two parallel postgres config blocks (postgresql: and probo.pg:) is confusing — only the second is read by the template.

Suggestion: unify the config, or at least document the probo.pg.* keys in the README.

3. Chart is missing OAUTH2_SERVER_SIGNING_KEY env var

The chart's secret.yaml and deployment.yaml don't include OAUTH2_SERVER_SIGNING_KEY, but probod-bootstrap requires it (along with AUTH_COOKIE_SECRET etc.). Pod crashlooped with missing required environment variables: OAUTH2_SERVER_SIGNING_KEY.

Worked around by patching the chart templates locally to add probo.oauth2.signingKey → Secret entry → valueFrom.secretKeyRef in the Deployment.

Suggestion: add probo.oauth2.signingKey to the chart, with --set-file instructions in docs (the value must be a PEM RSA private key — also undocumented).

4. encryption-key accepts the wrong format silently

Docs use openssl rand -base64 32. If you generate with openssl rand -hex 32 instead (common reflex), the pod crashes with key must be 32 bytes for AES-256, got 48 bytes after base64 decoding. Hex output is 64 chars → base64-decoded as 48 bytes. Same applies to OAUTH2_SERVER_SIGNING_KEY which must be a PEM RSA key, not a random string. Both errors are clear once they happen, but the chart's required validator only checks "non-empty" — not "valid".

Suggestion: add format validation in secret.yaml (length checks via gt, len, b64dec), and be explicit in the README about the format of each secret. Stating openssl rand -base64 32 would have saved me an hour.

5. No SSL configuration knob for external Postgres

Azure Postgres Flexible Server requires TLS. The chart only exposes postgresql.caBundle and postgresql.caBundlePath — no sslmode flag. Probo's driver apparently uses SSL only when a CA bundle is present, which is implicit and undocumented.

Compounding issue: passing the full Mozilla CA bundle (curl.se/ca/cacert.pem) via --set-file postgresql.caBundle=... causes exec /usr/local/bin/entrypoint.sh: argument list too long — the bundle gets injected as an env var that blows past ARG_MAX. Even a trimmed bundle (~6 KB) hit the same problem because env vars accumulate.

Worked around by creating a separate Secret with the CA bundle, mounting it as a volume, and using caBundlePath. Both caBundle and caBundlePath exist as values, but the chart only wires caBundle into a Secret it manages — for a CA mounted from your own Secret, there's no built-in path.

Suggestion: mount the CA via a Secret-backed volume in the chart by default, or at minimum document that caBundle only works for small bundles. Also expose sslmode / sslrootcert explicitly.

6. Schema migrations require ownership of public schema on PG ≥ 15

If you create a dedicated probod user, the first migration fails with permission denied for schema public. Required:

ALTER SCHEMA public OWNER TO probod;
GRANT ALL ON SCHEMA public TO probod;

Suggestion: call this out explicitly in the docs as part of the DB setup steps for managed PG providers.

7. Migrations create extensions that need cluster-level allow-listing on managed PG

Migration 20250202T141800Z runs CREATE EXTENSION citext. Migration 20250312T165900Z runs CREATE EXTENSION IF NOT EXISTS pg_stat_statements. On Azure Flexible Server (and most managed Postgres providers), these require server-level allow-listing via the azure.extensions parameter. pg_stat_statements additionally requires azure_pg_admin role membership.

The full list of extensions Probo uses: citext, pgcrypto, unaccent, pg_stat_statements.

Suggestion: document this list prominently in the "External PostgreSQL" section. Single biggest source of "why is my install failing" for managed-DB users.

8. Azure Blob Storage via S3Proxy is not actually viable

The S3 storage docs imply Azure Blob works via S3Proxy. In practice:

  • azureblob jclouds provider (the default) hits "command is not replayable" errors on the SDK's chunked upload retries
  • azureblob-sdk provider passes uploads through, but Azure rejects them with InvalidMetadata: characters not permitted — Probo sets metadata keys with hyphens (organization-id, trust-center-id, file-id, framework-id), which violate Azure's "valid C# identifier" rule
  • S3Proxy has open issues for metadata key transformation since 2017 (gaul/s3proxy#243, #466) with no fix

S3Proxy fundamentally cannot work for Probo against Azure Blob Storage without either patching S3Proxy or patching Probo to use underscored metadata keys. Switched to AWS S3 and everything worked instantly.

Suggestion: either drop Azure Blob from the "S3-compatible storage" list (with a clear "not actually supported" note), or — much better — implement native Azure Blob Storage support in Probo. The Azure SDK for Go is mature and would let users keep data in their existing Azure tenancy without the S3 translation layer. Given how many users will arrive from Azure-shop environments, first-class Blob support would be a real differentiator.

9. Service selector matches both the main app and the Chrome pod

The chart's _helpers.tpl defines probo.selectorLabels with only name and instance. The Chrome Deployment correctly adds app.kubernetes.io/component: chrome to its template labels AND its Service selector. The main Probo Deployment does not add a corresponding component: server to its template labels.

Result: the main probo Service selects both pods. Traffic to ports 80, 443, 8080 occasionally hits the Chrome pod, which doesn't listen on those, causing intermittent 502s. Also affects kubectl port-forward svc/probo which fails with Pod 'probo-chrome-...' does not have a named port 'backoffice'.

Workaround: kubectl patch the Service selector to add component: server and the Deployment template to add a matching pod label. But Helm doesn't track those patches, so every helm upgrade reverts them and requires --force-conflicts + re-patching.

Suggestion: add app.kubernetes.io/component: server to the main Probo Deployment's pod labels and to the Service selector in the chart. One-line fix per file. Same convention the Chrome Deployment already uses.

10. Misc smaller things

  • Chart.yaml only lists one dependency (haproxy-ingress) but the default values enable both haproxy-ingress and a cloudnative-pg-based Postgres demo — the postgresql-demo-app template lives directly in templates/ with no dependency hook, which means disabling postgresql.enabled is the only way to use external PG. Documented behavior, but the values structure makes it confusing.
  • IP geolocation table is empty; run geoloc-import to populate it — warning at every startup with no mention in the docs of what geoloc-import is, where it lives, or whether it's needed.
  • no account key provided, generating new ACME account - this will create a new account on each restart — for the trust-center custom-domains feature, the chart's customDomains.acme.accountKey should be wired through to a Secret so it persists, otherwise users get rate-limited by Let's Encrypt.
  • i think somewhere i saw that there should be known third parties (like google/microsoft), maybe in the code, but for now i have to add everything by hand

What worked great

  • Bootstrap entrypoint that generates the config from env vars at startup — very clean.
  • probod-bootstrap failing loudly with a clear list of missing env vars is genuinely helpful.
  • Migration system + clear logging of each migration as it runs.
  • Once everything was configured correctly, the product itself starts up cleanly and the UI is solid.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions