Skip to content

Kubernetes

Chart path: helm/garrison/. It deploys API, CRDT merge worker, queue worker, optional scheduler, UI, and optional in-cluster Postgres and Redis. The marketing site (www) is disabled by default (www.enabled: false).

The chart sets NOTE_CRDT_MERGE_URL (and DB_HOST, REDIS_HOST, etc.) in a ConfigMap. Override those only when using external Postgres, Redis, or a CRDT merge worker outside the cluster.

FilePurpose
helm/garrison/values.yamlDefault values (safe for local or trial installs)
helm/garrison/values-production.example.yamlProduction-oriented overrides — copy to a private repo or merge into your install
helm/garrison/argocd-application.example.yamlExample Argo CD Application manifest
helm/garrison/README.mdQuick reference in the repository

For GitOps with Argo CD, see Argo CD.

  • Kubernetes cluster and kubectl
  • Helm 3
  • Images in a registry (built by GitLab CI or locally)
  • TLS certificate on your Gateway listener (for example cert-manager Certificate referenced by the Gateway), or legacy Ingress TLS
  • Kubernetes Secret with Laravel env for production (recommended via api.existingSecret; see helm/garrison/secrets.env.example)

Start from helm/garrison/values-production.example.yaml or create your own values file (my-values.yaml):

global:
imageRegistry: registry.gitlab.com/churchtechhelp/garrison
# When the registry is private:
# imagePullSecrets:
# - name: garrison-registry
api:
image:
repository: api
tag: latest # pin to $CI_COMMIT_SHORT_SHA after CI build
existingSecret: garrison-secrets
# Laravel env (APP_URL, MAIL_*, SESSION_*, etc.) — put in garrison-secrets; see helm/garrison/secrets.env.example
crdtMerge:
image:
repository: crdt-merge
tag: latest
ui:
image:
repository: ui
tag: latest
scheduler:
enabled: true
postgres:
auth:
password: "" # use api.existingSecret DB_PASSWORD instead
# Optional marketing site (default false in values.yaml):
# www:
# enabled: true
# image:
# repository: www
# tag: latest
ingress:
enabled: false
httpRoute:
enabled: true
parentRefs:
- name: external-gateway
namespace: gateway-system
sectionName: https
hosts:
api: api.garrison.test
app: app.garrison.test
www: www.garrison.test

Set httpRoute.parentRefs to your cluster’s existing Gateway (the chart does not create it). List Gateways with kubectl get gateway -A, then list listener names:

Terminal window
kubectl get gateway <gateway-name> -n <gateway-namespace> \
-o jsonpath='{range .spec.listeners[*]}{.name}{" ("}{.protocol}{" :"}{.port}{")\n"}{end}'

Use the HTTPS listener’s name as sectionName. See Argo CD → Gateway API for the full field mapping and Argo parameter names.

Create the Secret before install when using api.existingSecret (copy and customize helm/garrison/secrets.env.example):

Terminal window
kubectl create namespace garrison
kubectl create secret generic garrison-secrets -n garrison \
--from-env-file=secrets.env

Generate APP_KEY with php artisan key:generate --show from a local Laravel install.

Install:

Terminal window
helm upgrade --install garrison ./helm/garrison \
--namespace garrison --create-namespace \
-f my-values.yaml

Set in your values file:

www:
enabled: true
image:
repository: www
tag: latest

When using HTTPRoute or Ingress, set httpRoute.hosts.www or ingress.hosts.www when enabling the marketing site.

Disable bundled services and point at managed instances:

postgres:
enabled: false
externalDatabase:
enabled: true
host: postgres.example.com
port: 5432
database: garrison
username: garrison
password: "secret"
redis:
enabled: false
externalRedis:
enabled: true
host: redis.example.com
port: 6379

Prefer api.existingSecret for DB_PASSWORD in production rather than committing passwords in values.

API, queue, and scheduler pods run php artisan migrate --force --isolated in an init container on every rollout, after waiting for Postgres (and Redis where configured). If a migration fails, the new pod does not become ready and the rollout stalls instead of serving traffic against an outdated schema.

To run migrations manually (for example during incident response):

Terminal window
kubectl exec -n garrison deploy/garrison-api -- php artisan migrate --force

On first install the chart applies resources in order so dependencies are ready:

  1. Postgres PVC (-20)
  2. ConfigMap, Secret, and Postgres/Redis Services (-15)
  3. Postgres and Redis Deployments (-10)
  4. CRDT merge worker (5)
  5. API, queue, scheduler, UI, and their Services (10) — init containers wait for Postgres/Redis, then run migrations
  6. HTTPRoutes or Ingress (20)
  • API readiness and liveness: HTTP GET /up on port 80
  • CRDT merge worker: HTTP GET /health on port 3000
  • UI and www: HTTP GET / on port 80

Build the UI image with VITE_API_URL matching your public API (docker/ui/Dockerfile ARG). The chart serves the pre-built image; changing API URL requires rebuilding the UI image.

Terminal window
helm upgrade garrison ./helm/garrison -f my-values.yaml
kubectl rollout status deployment/garrison-api -n garrison