Requirements and defaults
- Kubernetes, Helm 3, a dedicated namespace, and an ingress controller if exposing HTTP.
- One server replica only; the chart rejects other values and the
defaultnamespace. - An existing Kubernetes Secret; the chart does not generate passwords or embed plaintext secrets in Helm release values.
- Bundled PostgreSQL by default, one replica with a
ReadWriteOncePVC,storageClassName: rook-ceph-block, and 10Gi requested storage. NosubPathmounts are used. - An image you have built/published or verified. The default image naming convention is
ghcr.io/ricsam/browser-mcp-community:<appVersion>-community; its presence in values is not a publication claim.
0.1.1-community image supports linux/amd64. Managed deployments must override image.repository to ghcr.io/ricsam/browser-mcp and provide registry credentials.
1. Prepare namespace and secrets
The following commands are operator actions, not something chart rendering performs:browser-mcp-secrets in that namespace using your secret manager. It must contain DATABASE_URL, AUTH_SECRET, SETTINGS_ENCRYPTION_KEY, and, for bundled PostgreSQL, POSTGRES_PASSWORD. Generate passwords and keys independently; do not use example strings. Managed builds also require GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET.
If importing from a protected local env file, keep it outside the repository, mode 0600, and never include export prefixes:
browser and the default chart name, the bundled database’s service is browser-browser-mcp-postgres. Its connection string takes this shape, with a URL-encoded password:
DATABASE_URL must match postgresql.username, postgresql.database, and the password stored as POSTGRES_PASSWORD. A different release name or fullnameOverride changes the service name. The chart deliberately does not synthesize a secret database URL. Do not change the configured password expecting an initialized PostgreSQL PVC to rotate automatically.
2. Configure values
Keep values non-secret:browser-<project-suffix>.r5d.app from your assigned suffix, keep ingressClassName: traefik, and omit TLS entries: Cloudflare terminates public HTTPS. Use an https:// public URL. The suffix alone without a prefix is not a valid workload host.
Outside that environment, configure your ingress TLS termination explicitly, for example ingress.tls: [{secretName: browser-tls, hosts: [browser.example.com]}]. Without TLS entries or an upstream TLS terminator, the chart does not magically provide HTTPS.
External PostgreSQL
Setpostgresql.enabled: false. Put your external database’s URL into the same secret’s DATABASE_URL; no StatefulSet or PVC is then rendered. The database must already exist, be reachable from the migration and server pods, and permit schema migration. Configure verified TLS in the connection string according to your provider; do not disable certificate verification.
Private GHCR images on r5d
Use the worker’s configured registry auth without printing its contents. After creating the namespace, an operator can create the rotation-aware pull secret:imagePullSecrets: [{name: ghcr-pull}]. The annotation allows credential rotation; do not place auth JSON in values or source control.
3. Validate and install
Render the exact intended values and submit that rendered file to server admission before installation. The namespace must already exist for dry-run validation. The file contains secret references, not secret material.helm template | kubectl apply as the upgrade mechanism: Helm release revisions identify migration jobs and prevent immutable Job updates. For an existing release, helm upgrade --dry-run=server additionally validates Helm’s actual release context. Fix all admission failures before proceeding.
Migration ordering
The migration Job is deliberately not apre-install hook. Such a hook would run before bundled PostgreSQL, its Service, and ordinary release resources exist and can deadlock the first install.
Instead:
- Helm creates the bundled database (if enabled), a revision-named migration Job, and the Deployment.
- The Job’s init container retries an authenticated
SELECT 1againstDATABASE_URL; a listening port alone is not considered usable. - The Job runs
bun dist/migrate.jsfrom the same image as the new server. - A Deployment init container reads only that exact Job’s status, using namespace-scoped
getRBAC. The application process does not start until the Job has a successful Complete condition. Failure leaves the server gated.
4. Verify the release
/healthz and /readyz return success. Inspect migration/server logs without exposing secrets. Complete one browser pairing and a harmless MCP request before declaring the deployment usable.
Uninstalling the chart may retain StatefulSet PVCs. Manage backup and deletion explicitly; do not delete retained volumes as a routine troubleshooting step.