Deploying Care onto the cluster
With a Kubernetes cluster provisioned (see Provisioning the cluster), Care and its supporting services are rolled out module by module from the deployment-k8 repository. This page describes the deployer workflow and the order in which modules are applied.
The exact commands, variable values, and manifests live in the
deployment-k8 repo and evolve over time. Treat the
commands below as the canonical shape of each step; always run them from a checkout of the repo so
you pick up the current modules and values. A few steps are still being automated — the repo carries
TODO markers for a database migrations job and the terminology server — so expect to perform those
manually for now.
The deployer model
Deployments are driven by deployer.py, a Typer CLI in the root of
the repo, with a Makefile that proxies the same commands. Both forms are equivalent — use whichever
you prefer.
Per-environment configuration lives under environments/<env>/:
| File | Purpose |
|---|---|
config.json | Holds KUBECONFIG_PATH — the kubeconfig the deployer uses to talk to the cluster. |
variables.tfvars | The OpenTofu variables for that environment. |
Select the active environment, then inspect what the deployer will use:
# Select the active environment
python deployer.py set-current-env <env>
# or
make set-current-env <env>
# Inspect the current selection and its resolved config
python deployer.py show-current-env
python deployer.py show-defaults
set-current-env writes the choice to .current-env.json, and every subsequent apply-tofu /
apply-k8 command reads environments/<env>/ from there. You can sanity-check connectivity to the
selected cluster at any time:
python deployer.py check-k8-cluster
Two operations per module
Each module is applied in up to two phases:
| Operation | What it does |
|---|---|
apply-tofu <module> | Provisions infrastructure and Helm releases for the module via OpenTofu (tofu init + tofu apply against infra/<module>/). |
apply-k8 <module> | Applies the Kubernetes manifests generated under infra/<module>/resources/ with kubectl apply. |
Both have a make proxy. The two forms for a module are:
# OpenTofu provisioning
python deployer.py apply-tofu <module>
make apply-tofu-<module>
# Kubernetes manifests
python deployer.py apply-k8 <module>
make apply-k8-<module>
Not every module needs the apply-k8 phase — some are fully provisioned by apply-tofu. Run
apply-k8 <module> only where the module ships manifests under infra/<module>/resources/
(for example volumes, networking, and database). The deployer will tell you if the resources
directory is missing.
Module rollout order
Apply the modules in the order below. Several steps require manual configuration in between (creating secrets, pushing images, creating buckets), so do not batch them blindly.
-
volumes — installs Longhorn for volume management. After Longhorn is up, configure nodes and storage classes, create the Longhorn S3 backup-target secret, then
apply-k8 volumes. Wait a couple of minutes for Longhorn to detect the volumes before continuing. -
networking — installs the ingress controllers, then routing. TLS secrets are created per namespace (
care-be,care-fe,odoo,rustfs,metabase) beforeapply-k8 networking:kubectl create secret tls lets-encrypt-cert \--cert=fullchain.pem --key=privkey.pem -n <namespace> -
registry — provisions the container registry used to store all application images. Then build and push images to the registry using the scripts in
build_scripts/(which hascare_be/,care_fe/,odoo/, andsnowstorm/builders). -
database — provisions PostgreSQL via the CloudNativePG operator together with Redis/Valkey. Set up WAL archive storage and the required secrets/operator plugins, then
apply-k8 databaseto create the catalogs, secrets, roles, and databases. -
objectstore — provisions RustFS. Then create the required buckets:
chunks,ruler,admin,care-facility,care-fileupload. -
monitoring — installs the monitoring stack (Grafana/Loki). Once stable, log in to Grafana and add the Loki datasource and any dashboards you need.
-
care — deploys the Care backend and frontend.
-
odoo — deploys Odoo. Perform addon configuration, then update the Care secrets and re-apply the Care config as needed.
-
opensearch — installs the OpenSearch controller, then apply the cluster manifest:
kubectl apply -f infra/opensearch/cluster.yaml -
metabase — deploys Metabase.
-
snowstorm — deploys Snowstorm (the terminology server backing).
Before deploying care, confirm that all images have been written to the registry (step 3). The
backend and frontend pods pull from the registry provisioned earlier, and a missing image will leave
pods in ImagePullBackOff.
Image build and push
The registry module only provisions the registry — it does not populate it. Building and pushing
images is a separate, explicit step that uses the per-application build scripts under
build_scripts/:
build_scripts/
├── care_be/ # Care backend image
├── care_fe/ # Care frontend image
├── odoo/ # Odoo image
└── snowstorm/ # Snowstorm image
Run the relevant builders after the registry module is up and before deploying the modules that
consume those images (care, odoo, snowstorm).
Known gaps
The repo is explicit about steps that are not yet fully automated. Until they land, handle them manually:
- Database migrations — a job to run Care migrations and post-deploy cleanups after the
caremodule is still aTODO. Run migrations manually until it is wired up. - Terminology server — the terminology-server configuration section is a
TODO. Thesnowstormmodule deploys the service, but end-to-end configuration is still being documented.
Next steps
Once Care is running, see Day-2 operations for backups, monitoring, and ongoing maintenance.