Skip to content
BoringStack
Star

Infra template: Kubernetes (k3s)

3 min read

infra/k3s is the cluster deployment target, the alternative to the single-host infra/compose and OpenTofu/Hetzner paths. Pick the one that fits your operating model; you don’t need all three.

It ships BoringStack to any ArgoCD-managed k3s/Kubernetes cluster as GitOps: push code, CI builds and pushes a GHCR image, argocd-image-updater bumps the tag, and ArgoCD syncs. The whole app runs in one namespace with HA, autoscaling, and TLS.

BoringStack is Compose-first on purpose, so this target lives in its own infra/k3s/ subtree and is entirely opt-in. It never sits in the default deploy path.

WorkloadManifestNotes
apibase/api/Bun/Elysia, port 7330, /health (liveness) and /ready (readiness)
uibase/ui/nginx static SPA, port 8080
Postgresbase/postgres/CloudNativePG Cluster, 1 instance (3 in prod)
Valkeybase/valkey/in-namespace cache and BullMQ backend
GlitchTipoverlays/prod/glitchtip/per-project error tracking (web and worker)
migrationsbase/api/migration-job.yamlArgoCD PreSync Job: db:migrate && db:seed

Routing mirrors Compose prod: one domain with same-origin path routing. Host && (/api or /health) goes to the api; everything else goes to the ui (the SPA).

infra/k3s/
├── argocd/ ArgoCD Application (+ image-updater) and registration example
├── base/ env-agnostic manifests: namespace, api, ui, valkey, postgres
└── overlays/prod/ HA + TLS + secrets + GlitchTip + monitoring + patches
└── secrets/ swappable backend: vault (default) | sealed | plain

base/ is generic. The prod overlay adds replicas and anti-affinity (api ×3, ui ×2, Postgres ×3), HPAs, PDBs, a ResourceQuota/LimitRange, Traefik middleware, the TLS Certificate, GlitchTip, and the monitoring integration.

The cluster must provide ArgoCD + argocd-image-updater, the CloudNativePG operator, cert-manager with a DNS-01 ClusterIssuer, Traefik (k3s default), and a persistent StorageClass. Optionally kube-prometheus-stack (for the ServiceMonitor and Grafana dashboards) and your chosen secrets backend.

Every workload reads two plain k8s Secrets, boringstack-secrets (app env) and ghcr-registry-secret (GHCR pull), plus the CNPG-generated boringstack-db-app that carries DATABASE_URL. The manifests never reference Vault directly, so how those Secrets are produced is a swappable Kustomize component under overlays/prod/secrets/:

  • vault (default): HashiCorp Vault via the Vault Secrets Operator.
  • sealed: Bitnami SealedSecrets, encrypted and safe to commit.
  • plain: a kustomize secretGenerator over a gitignored secret.env.

Switch by editing one line in overlays/prod/kustomization.yaml. See the secrets backends runbook.

The target does not deploy Prometheus/Grafana/Loki. It plugs into the cluster’s kube-prometheus-stack via a ServiceMonitor that scrapes the api, plus optional Grafana dashboard ConfigMaps. See overlays/prod/monitoring/README.md.

Full knob list and walkthrough: Provisioning with k3s and infra/k3s/README.md.