Address the highest-impact Kubernetes chart issues from #1245. Align PG_ADDR with postgresql.host/port, wire OAUTH2_SERVER_SIGNING_KEY, add early validation for required base64 and PEM secrets, and isolate the main service/deployment with component labels so Chrome pods are not selected by server traffic. Update chart values and documentation to clarify required secret formats, managed PostgreSQL prerequisites, ACME account key persistence, and current Azure Blob compatibility caveats for S3 proxy deployments. Signed-off-by: Cursor Agent <cursoragent@cursor.com>
11 KiB
Probo Helm Chart
This Helm chart deploys Probo - an open-source SOC-2 compliance platform - on Kubernetes.
Prerequisites
- Kubernetes 1.23+
- Helm 3.8+
- External PostgreSQL database (AWS RDS, GCP Cloud SQL, Azure Database, etc.)
- S3 or S3-compatible object storage (AWS S3, GCS, DigitalOcean Spaces, SeaweedFS, etc.)
- OpenSSL installed (for generating secrets)
Azure Blob behind S3 compatibility proxies currently has known metadata compatibility issues. Prefer native S3-compatible backends for production.
Installing the Chart
From OCI Registry
helm install probo oci://artifact.probo.inc/probo/probo --version <chart-version> \
--set probo.baseUrl="probo.example.com" \
--set probo.encryptionKey="$ENCRYPTION_KEY" \
--set probo.auth.cookieSecret="$COOKIE_SECRET" \
--set probo.auth.passwordPepper="$PASSWORD_PEPPER" \
--set probo.trustAuth.tokenSecret="$TRUST_TOKEN_SECRET" \
--set-file probo.oauth2.signingKey="./oauth2_signing_key.pem" \
--set postgresql.host="postgres.example.com" \
--set postgresql.password="<db-password>" \
--set s3.bucket="probo-production" \
--set s3.accessKeyId="<aws-access-key-id>" \
--set s3.secretAccessKey="<aws-secret-access-key>"
From Local Chart
Generate Required Secrets
# Generate required secrets
export ENCRYPTION_KEY=$(openssl rand -base64 32)
export COOKIE_SECRET=$(openssl rand -base64 32)
export PASSWORD_PEPPER=$(openssl rand -base64 32)
export TRUST_TOKEN_SECRET=$(openssl rand -base64 32)
# Generate OAuth2 signing key (PEM)
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 \
-out oauth2_signing_key.pem
# Generate private key and certificate valid for 10 years
TEMP_KEY=$(mktemp)
TEMP_CERT=$(mktemp)
openssl req -x509 -newkey rsa:2048 -keyout "$TEMP_KEY" -out "$TEMP_CERT" \
-days 3650 -nodes -subj "/CN=probo-saml/O=Probo/C=US" 2>/dev/null
# Read generated files and export as environment variables
export SAML_PRIVATE_KEY=$(cat "$TEMP_KEY")
export SAML_CERTIFICATE=$(cat "$TEMP_CERT")
# Clean up temporary files
rm -f "$TEMP_KEY" "$TEMP_CERT"
echo "Save these secrets securely!"
Install
helm install probo . \
--set probo.baseUrl="probo.example.com" \
--set probo.encryptionKey="$ENCRYPTION_KEY" \
--set probo.auth.cookieSecret="$COOKIE_SECRET" \
--set probo.auth.passwordPepper="$PASSWORD_PEPPER" \
--set probo.trustAuth.tokenSecret="$TRUST_TOKEN_SECRET" \
--set-file probo.oauth2.signingKey="./oauth2_signing_key.pem" \
--set probo.saml.privateKey="$SAML_PRIVATE_KEY" \
--set probo.saml.certificate="$SAML_CERTIFICATE" \
--set postgresql.host="postgres.example.com" \
--set postgresql.password="<db-password>" \
--set s3.bucket="probo-production" \
--set s3.accessKeyId="<aws-access-key-id>" \
--set s3.secretAccessKey="<aws-secret-access-key>"
Production Deployment
For production, create a values-production.yaml file:
# values-production.yaml
image:
repository: artifact.probo.inc/probo/probo
tag: "0.74.7"
replicaCount: 3
ingress:
enabled: true
className: "nginx"
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
hosts:
- host: probo.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: probo-tls
hosts:
- probo.example.com
probo:
baseUrl: "probo.example.com"
encryptionKey: "<secret>"
cors:
allowedOrigins:
- "https://probo.example.com"
auth:
cookieDomain: "example.com"
cookieSecret: "<secret>"
passwordPepper: "<secret>"
oauth2:
signingKey: "<PEM private key>"
trustAuth:
cookieDomain: "example.com"
tokenSecret: "<secret>"
mailer:
senderEmail: "noreply@example.com"
smtp:
addr: "smtp.sendgrid.net:587"
user: "apikey"
password: "<smtp-password>"
tlsRequired: true
postgresql:
host: "postgres.example.com"
password: "<db-password>"
s3:
region: "us-east-1"
bucket: "probo-production"
accessKeyId: "<aws-access-key-id>"
secretAccessKey: "<aws-secret-access-key>"
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
Install with:
helm install probo . -f values-production.yaml
Configuration
Required Configuration
The following parameters must be configured:
| Parameter | Description |
|---|---|
probo.encryptionKey |
Base64-encoded encryption key (32+ bytes) |
probo.auth.cookieSecret |
Cookie signing secret (32+ bytes) |
probo.auth.passwordPepper |
Password hashing pepper (32+ bytes) |
probo.trustAuth.tokenSecret |
Trust token secret (32+ bytes) |
probo.oauth2.signingKey |
PEM-encoded OAuth2 private signing key |
postgresql.host |
PostgreSQL hostname |
postgresql.password |
PostgreSQL password |
s3.accessKeyId |
S3 access key ID |
s3.secretAccessKey |
S3 secret access key |
Secret Format Quick Reference
probo.encryptionKey,probo.auth.cookieSecret,probo.auth.passwordPepper,probo.trustAuth.tokenSecret: base64-encoded 32-byte secrets (for example:openssl rand -base64 32)probo.oauth2.signingKey: PEM-encoded private key (--set-filestrongly recommended)
Key Configuration Parameters
| Parameter | Description | Default |
|---|---|---|
image.repository |
Probo image repository | artifact.probo.inc/probo/probo |
image.tag |
Probo image tag | Chart appVersion |
replicaCount |
Number of Probo replicas | 1 |
probo.baseUrl |
Public baseUrl | probo.example.com |
probo.oauth2.signingKey |
OAuth2 signing key (PEM private key) | "" (required) |
postgresql.host |
PostgreSQL host | "" (required) |
postgresql.port |
PostgreSQL port | 5432 |
postgresql.database |
Database name | probod |
postgresql.username |
Database user | probod |
postgresql.caBundle |
PostgreSQL TLS CA certificate bundle (inline) | "" |
postgresql.caBundlePath |
PostgreSQL TLS CA certificate bundle (file path) | "" |
s3.bucket |
S3 bucket name | probod |
s3.region |
AWS region | us-east-1 |
s3.endpoint |
S3 endpoint (for S3-compatible) | "" |
s3.usePathStyle |
Use path-style URLs for selected S3-compatible backends | false |
chrome.enabled |
Deploy Chrome | true |
chrome.external.addr |
External Chrome (if disabled) | "" |
ingress.enabled |
Enable ingress | false |
See values.yaml for all available options.
Components
The chart deploys the following:
- Probo Application: Main Go binary serving GraphQL APIs and SPAs
- Chrome Headless: For PDF generation (optional, can use external)
- Ingress: For external access with TLS (optional)
External Dependencies (Required)
- PostgreSQL: Managed database service
- S3 Storage: Object storage for files and documents
Database Management
Migrations
Database migrations run automatically when Probo starts. No manual intervention is required.
For managed PostgreSQL providers (notably PostgreSQL >= 15), ensure the
Probo role can own and modify the public schema before first startup:
ALTER SCHEMA public OWNER TO probod;
GRANT ALL ON SCHEMA public TO probod;
Probo migrations also require these extensions to be installable:
citextpgcryptounaccentpg_stat_statements
TLS/SSL Configuration
For secure PostgreSQL connections, you can provide a CA certificate bundle in two ways:
-
Inline CA Bundle (
postgresql.caBundle): Provide the certificate content directly in values.yamlpostgresql: caBundle: | -----BEGIN CERTIFICATE----- MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl ... -----END CERTIFICATE----- -
File Path (
postgresql.caBundlePath): Mount the CA bundle as a ConfigMap/Secret and reference the pathpostgresql: caBundlePath: /etc/ssl/certs/ca-certificates.crt # Then mount your CA bundle using volumes/volumeMounts volumes: - name: ca-bundle configMap: name: postgres-ca-bundle volumeMounts: - name: ca-bundle mountPath: /etc/ssl/certs readOnly: true
Note: Using caBundlePath is recommended for large CA bundles (for
example system CA bundles) as it avoids environment variable size limitations.
Backup
Use your PostgreSQL provider's backup solution (e.g., AWS RDS automated backups, GCP Cloud SQL backups).
Upgrading
helm upgrade probo . -f values-production.yaml
Uninstalling
helm uninstall probo
Note: This does not delete your external PostgreSQL database or S3 bucket.
Troubleshooting
View Logs
kubectl logs -f deployment/probo
Check Configuration
kubectl describe configmap probo
kubectl get secret probo -o yaml
Test Database Connection
Check the Probo logs for database connection errors. The application will fail to start if it cannot connect to PostgreSQL.
Test S3 Connection
Check the Probo logs for S3 connection errors when uploading files.
IP geolocation table is empty warning
If startup logs show:
IP geolocation table is empty; run geoloc-import to populate it
the instance is running correctly, but geolocation data is missing. Populate
it by running the geoloc-import command in your operational workflow.
Examples
AWS Deployment
postgresql:
host: "mydb.abc123.us-east-1.rds.amazonaws.com"
password: "<rds-password>"
# Optional: Add RDS CA bundle for TLS connections
# caBundle: |
# -----BEGIN CERTIFICATE-----
# ...RDS CA certificate...
# -----END CERTIFICATE-----
s3:
region: "us-east-1"
bucket: "my-probo-bucket"
accessKeyId: "<aws-key>"
secretAccessKey: "<aws-secret>"
# Leave endpoint empty for AWS S3
GCP Deployment
postgresql:
host: "10.0.0.5" # Cloud SQL private IP
password: "<cloudsql-password>"
s3:
region: "us-east1"
bucket: "my-probo-bucket"
endpoint: "https://storage.googleapis.com"
accessKeyId: "<hmac-access-key>"
secretAccessKey: "<hmac-secret>"
DigitalOcean Deployment
postgresql:
host: "db-postgresql-nyc1-12345.ondigitalocean.com"
password: "<db-password>"
s3:
region: "nyc3"
bucket: "my-probo-bucket"
endpoint: "https://nyc3.digitaloceanspaces.com"
accessKeyId: "<spaces-access-key>"
secretAccessKey: "<spaces-secret>"
Using External Chrome Service
By default, Chrome is deployed in the cluster. To use an external Chrome service:
chrome:
enabled: false
external:
addr: "chrome.browserless.io:3000"