Skip to main content
This guide installs the complete Bud platform, the application plus its in-cluster dependencies (PostgreSQL, ClickHouse, Kafka, MongoDB, Valkey, SeaweedFS, Keycloak, Dapr, cert-manager), into an existing Kubernetes cluster using ArgoCD. ArgoCD pulls every chart from the OCI registry (registry.bud.studio/charts), so you do not need access to the source repository. Your configuration lives in a GitOps repo you own. This page contains every file that repo needs, written for an environment called production on bud.example.com.
Already run managed databases (Azure Database for PostgreSQL, Cosmos DB, external Kafka/ClickHouse, etc.) and want a single Helm install instead of in-cluster dependencies? Follow the Deployment Guide, it installs only the bud chart pointed at your external services.

How it works

The config repo has three moving parts:
You apply one object by hand, the bootstrap Application in apps/<environment>.yaml. It syncs appsets/<environment>.yaml, and that ApplicationSet generates one ArgoCD Application per component, named <environment>-<component>.

Components

ArgoCD manages itself as the argocd component, so after the bootstrap the ArgoCD install is upgraded the same way as everything else.

How value files are layered

Each generated Application joins two sources: the OCI chart, and your Git repo mounted as $values for the value files (the templatePatch at the bottom of the ApplicationSet). Each component lists its value files in order; later files win. For example, postgres:
  • Paths without $values/ (such as example.minimal.yaml) resolve inside the chart itself: ready-made minimal profiles shipped with each dependency chart.
  • values.budruntime.yaml, values.yaml and the values.minimal*.yaml profiles are the shared, environment-neutral baseline. Do not edit them per environment.
  • values.<environment>.yaml is your public overlay.
  • secrets.<environment>.yaml is SOPS-encrypted.
Secrets stay encrypted in Git. The argocd chart bakes helm-secrets + sops + age into the ArgoCD repo-server, and the age private key is supplied through the chart’s sopsAgeKeys value. ArgoCD therefore decrypts every secrets.<environment>.yaml in-cluster, and nothing is stored in plaintext.

TLS profiles

See Self-signed or internal-CA TLS for how the CA profiles work.
Syncs are manual by default. The ApplicationSet sets syncPolicy.automated.enabled: false (with selfHeal: true), deliberate for production: a Git push never changes a cluster until someone syncs. Flip it to true if you want hands-off reconciliation. Either way, the addon charts install operators (CloudNativePG, Strimzi, Altinity, Percona, …) whose CRDs must exist before the database custom resources apply, so expect a few Applications to show Degraded/Progressing on the first sync and converge on a retry.

Prerequisites

budctl generates the repo described in Option B for you: it writes the shared values and your environment’s files, generates coordinated credentials for every component, encrypts each secret file with SOPS, and commits the result to your repo. Run the guided installer:
For a non-interactive preview of what it would generate:
For an environment named production, budctl writes:
  • apps/production.yaml and appsets/production.yaml
  • values/<component>/values.production.yaml
  • SOPS-encrypted values/<component>/secrets.production.yaml
  • a production rule in .sops.yaml
OpenSandbox is included by default. The --tls mode decides whether cert-manager and Kyverno are added (see TLS profiles). Bud Studio is the only optional add-on. Then continue at Verify.

Option B, Install manually

Use this path when you can’t run budctl. You create the repo by hand from the files on this page. Replace production with your environment name, bud.example.com with your domain, and https://github.com/example-org/bud-foundry-config.git with your repo.

Step 1, Create your config repo

.gitignore, the age key and installer state must stay out of Git:
Do not add a broad secrets*.yaml ignore rule: the encrypted secret files must be committed, or ArgoCD silently installs without the credentials.
apps/production.yaml is the bootstrap “app of apps”, a single Application that tells ArgoCD to apply your ApplicationSet from Git:
apps/production.yaml
appsets/production.yaml lists every component, its chart, version and value files. Drop the kyverno element, and adjust the cert-manager value files, if you are not using a self-signed or internal CA (see TLS profiles).
appsets/production.yaml
The shared baseline. These files are the same for every environment: node affinity toward bud.studio/control-plane nodes, the users and databases Bud expects in each datastore, the Keycloak realm and clients, and the SeaweedFS buckets. The passwords in them are placeholders that your secrets.production.yaml files override.
values/argocd/values.budruntime.yaml
values/dapr/values.yaml
values/kafka/values.budruntime.yaml
values/mongodb/values.budruntime.yaml
values/cert-manager/values.budruntime.yaml
values/kyverno/values.budruntime.yaml
values/clickhouse/values.budruntime.yaml
values/postgres/values.budruntime.yaml
values/postgres/values.minimal.budruntime.yaml
values/seaweedfs/values.budruntime.yaml
values/valkey/values.yaml
values/valkey/values.minimal.yaml
values/valkey/values.budruntime.yaml
values/keycloak/values.budruntime.yaml
The cert-manager and Kyverno CA files, values/cert-manager/values.selfsigned-ca.yaml and values/kyverno/values.inject-ca.yaml, are listed in full under Self-signed or internal-CA TLS.

Step 2, Generate an age key and wire up SOPS

Generate a keypair for the environment (and optionally one per admin):
Register the public key in .sops.yaml so the environment’s secrets files encrypt to it:
The private key is handed to ArgoCD through the argocd chart’s sopsAgeKeys value, itself stored SOPS-encrypted:
From then on, every secrets.*.yaml your ApplicationSet references is decrypted transparently by the repo-server’s helm-secrets wrapper.

Step 3, Environment values

Your public overlays. The shared baseline already carries the defaults, so these stay small. values/argocd/values.production.yaml sets the ArgoCD hostname, registers your repo, and creates the AppProject the ApplicationSet targets (the projects key must match spec.template.spec.project):
values/argocd/values.production.yaml
values/bud/values.production.yaml is the platform’s cluster config; everything else falls back to chart defaults. global.ingress.hosts.root is the only host you set: every service sub-host (admin., app., auth., s3., …) derives from it. ingress.https: internal means cert-manager issues the certificates and they terminate in-cluster; use external when an upstream LB/WAF terminates TLS, or disabled for plain HTTP.
values/bud/values.production.yaml
See the Helm Configuration Reference for the full option list. Optional services (buddoc, askbud, budpipeline, …) can be trimmed with microservices.<name>.enabled: false for a smaller footprint. values/keycloak/values.production.yaml sets the auth host and the redirect and logout URIs of the frontend clients:
values/keycloak/values.production.yaml
values/seaweedfs/values.production.yaml sets the S3 host and sizes the model-registry bucket to match storage.budmodelRegistry.size:
values/seaweedfs/values.production.yaml
You only edit a handful of values. The bud chart’s defaults already point the application at the in-cluster databases this guide deploys (pooler-rw.postgres, clickhouse-clickhouse.clickhouse, mongodb-rs0.mongodb, valkey-master.valkey). Leave the externalServices.*.host keys at their defaults, those are only changed for the managed-database path in the Deployment Guide.

Step 4, Secrets

Every secrets.production.yaml the ApplicationSet references must exist: argocd (from Step 2), kafka, mongodb, clickhouse, postgres, seaweedfs, valkey, keycloak, bud and opensandbox (plus cert-manager for an imported CA). For the datastores and Keycloak, override the placeholder passwords from the shared baseline: the users.*.password entries (postgres, clickhouse, kafka, mongodb), identity.bud.accessKey/secretKey (seaweedfs), auth.password (valkey), and the Keycloak client secrets and root user credentials. For values/bud/secrets.production.yaml, the per-key reference, what each secret is for and how to generate it, lives in the deployment guide; the set is the same regardless of install method: Encrypt each file before committing:
Credentials must agree across components. Because the databases run in-cluster, the Postgres password in values/bud/secrets.production.yaml must match the one in values/postgres/secrets.production.yaml, and likewise for ClickHouse, Kafka, MongoDB, Valkey, SeaweedFS and the Keycloak client secrets. A mismatch shows up as backend pods failing to connect after they start. budctl generates these coordinated values for you.

Self-signed or internal-CA TLS

A public ACME issuer needs publicly resolvable hosts, so air-gapped and internal deployments issue certificates from a CA of their own instead. This takes two charts: cert-manager (with trust-manager) to issue the certificates and publish a trust bundle, and kyverno to inject that bundle into the platform’s pods. Without the injection the services do not trust the new CA, and calls between them fail on certificate verification. values/cert-manager/values.selfsigned-ca.yaml creates a 10-year self-signed root, an issuer that signs from it, and a trust-manager Bundle that distributes the root to workloads:
values/cert-manager/values.selfsigned-ca.yaml
The two issuers are deliberate: selfsigned exists only to sign the root Certificate, and selfsigned-ca (backed by that root) is what actually issues the leaf certificates. The Bundle writes the public roots plus your own root into a ca-pemstore ConfigMap in every namespace; Kyverno mounts it into pods (see below), so nothing in the platform has to learn the certificate individually. Clients outside the cluster (browsers, curl) will not trust these certificates until you distribute the root from the selfsigned-ca-root secret to their trust stores. values/kyverno/values.inject-ca.yaml defines a Kyverno ClusterPolicy that mutates every pod created in the bud and bud-* namespaces: it mounts the ca-pemstore ConfigMap at /etc/ssl/certs/cafebabe-ca.pem and sets the environment variables each runtime (OpenSSL, Node.js, Python, pip, curl, git) uses to find a CA bundle:
values/kyverno/values.inject-ca.yaml
Because the policy runs at pod admission, it only affects pods created after it is in place. Sync kyverno before the bud chart, or restart the platform’s workloads once the policy is active. The bundle also ships a PKCS#12 copy (cafebabe-ca.p12) for JVM trust stores. Also trusting an existing internal CA? Add its certificate to the bundle’s sources in a values/cert-manager/values.production.yaml, and list that file after values.selfsigned-ca.yaml in the cert-manager element of the ApplicationSet:
sources is a list, and Helm replaces lists rather than merging them, so the overlay restates the two entries from values.selfsigned-ca.yaml and appends the third. This only adds the internal CA to what the cluster trusts. To have that CA issue the certificates as well, store its certificate and key in a secret (in secrets.production.yaml), point an issuer at it (issuers.<name>.ca.secretName, the same shape as selfsigned-ca), and set cert-manager.ingressShim.defaultIssuerName to that issuer.
Public ACME instead. Drop values.selfsigned-ca.yaml and the kyverno element, define an ACME issuer under issuers: in values/cert-manager/values.production.yaml, and set cert-manager.ingressShim.defaultIssuerName to it. With an HTTP-01 issuer every ingress host must be publicly resolvable and reachable on port 80; a DNS-01 issuer needs a DNS-provider API token in secrets.production.yaml. If certs stay Ready=False, check kubectl -n bud describe certificate <name> and kubectl -n bud get challenge.
Commit and push everything to your repo.

Step 5, Bootstrap ArgoCD

The ApplicationSet manages ArgoCD itself (the production-argocd Application), but the very first install must come from your machine: ArgoCD cannot decrypt your secrets until the chart has delivered the age key. Install the argocd chart with the same value files its ApplicationSet element references. The release name must be argocd so the Application adopts it afterwards:
Then register the OCI registry as a Helm repository credential so ArgoCD can pull the charts:
Once the first sync of production-argocd completes (Step 6), ArgoCD is self-managed: future ArgoCD upgrades are a targetRevision bump in your ApplicationSet.

Step 6, Apply the bootstrap Application and sync

Open the ArgoCD UI (or use the argocd CLI) and sync:
  1. bootstrap, applies appsets/production.yaml, which generates the production-* Applications.
  2. production-argocd, adopts the Helm install from Step 5.
  3. The addons (production-dapr, production-cert-manager, production-kyverno, production-postgres, production-clickhouse, production-kafka, production-mongodb, production-seaweedfs, production-valkey), a few may show Degraded until their operators’ CRDs establish; re-sync and they converge.
  4. production-keycloak, then production-bud and production-opensandbox last, once the databases are up.
Syncs stay manual unless you enable automated (see How it works).

Verify

All pods should reach Running within ~5–10 minutes of the Applications going Healthy. Backend pods run a Dapr sidecar, so expect 2/2 READY for most of them.

Optional add-ons

Add one to your environment by appending an element to appsets/<environment>.yaml (name, namespace, repoURL registry.bud.studio/charts, chart, targetRevision, valueFiles), exactly like the required ones.
Storage is still required, just not from openebs. The databases need a working StorageClass either way. openebs is one way to provide it; a cloud CSI driver is another. Set storage.budmodelRegistry.className (Step 3) to whichever your cluster offers.

Post-installation

Open https://admin.<your-domain> (e.g. https://admin.bud.example.com) and log in with the SUPER_USER_EMAIL / SUPER_USER_PASSWORD from values/bud/secrets.<environment>.yaml (with budctl, the email is the --admin-email you passed). With the self-signed TLS profile, browsers will not trust the certificates until you import the root from the selfsigned-ca-root secret in the cert-manager namespace.

Upgrading

Bump the element’s targetRevision in appsets/<environment>.yaml (e.g. bud to a new chart version), commit, and sync.

Uninstalling

Common pitfalls

  • An Application errors with Error decrypting / helm-secrets failures, the age key in values/argocd/secrets.<environment>.yaml (sopsAgeKeys) does not match the recipients your secrets.*.yaml files were encrypted to. Re-check .sops.yaml and re-encrypt (sops updatekeys <file>).
  • Addon Applications stuck Degraded on first sync, usually the operator CRDs not yet established. Re-sync after a minute; the ApplicationSet uses ServerSideApply=true so large CRDs apply cleanly.
  • Backend pods crash-loop on DB connection, almost always a password mismatch between a datastore’s secrets and values/bud/secrets.<environment>.yaml (see the warning in Step 4).
  • Service-to-service calls fail certificate verification with a self-signed or internal CA, the Kyverno policy only mutates pods created after it is active. Sync kyverno first, then restart the bud workloads.
  • registries.registry.bud.studio credentials are required even on clusters that mirror images locally, the chart still creates the imagePullSecret referenced by every Deployment.
  • budmetrics ClickHouse database name must be metrics, it is hardcoded. See the Deployment Guide.

Next steps

Deployment Guide

Per-service secret & config reference; managed-database install path.

Helm Configuration

Full list of chart values and overrides.