<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://ohcnwiki.tellmey.fyi/index.php?action=history&amp;feed=atom&amp;title=Deployment%2FKubernetes%2FReference_architecture</id>
	<title>Deployment/Kubernetes/Reference architecture - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://ohcnwiki.tellmey.fyi/index.php?action=history&amp;feed=atom&amp;title=Deployment%2FKubernetes%2FReference_architecture"/>
	<link rel="alternate" type="text/html" href="https://ohcnwiki.tellmey.fyi/index.php?title=Deployment/Kubernetes/Reference_architecture&amp;action=history"/>
	<updated>2026-07-06T05:18:31Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.45.4</generator>
	<entry>
		<id>https://ohcnwiki.tellmey.fyi/index.php?title=Deployment/Kubernetes/Reference_architecture&amp;diff=396&amp;oldid=prev</id>
		<title>Admin: Add Navbox deployment template before Related (via update-page on MediaWiki MCP Server)</title>
		<link rel="alternate" type="text/html" href="https://ohcnwiki.tellmey.fyi/index.php?title=Deployment/Kubernetes/Reference_architecture&amp;diff=396&amp;oldid=prev"/>
		<updated>2026-07-06T04:01:53Z</updated>

		<summary type="html">&lt;p&gt;Add Navbox deployment template before Related (via update-page on MediaWiki MCP Server)&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 09:31, 6 July 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;
  &lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 152:&lt;/td&gt;
  &lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 152:&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/td&gt;
  &lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;For how these components are stood up in practice, continue to [[Deployment/Kubernetes/Provisioning the cluster|Provisioning the cluster]] and [[Deployment/Kubernetes/Deploying Care onto the cluster|Deploying Care onto the cluster]].&lt;/div&gt;&lt;/td&gt;
  &lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;For how these components are stood up in practice, continue to [[Deployment/Kubernetes/Provisioning the cluster|Provisioning the cluster]] and [[Deployment/Kubernetes/Deploying Care onto the cluster|Deploying Care onto the cluster]].&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td colspan=&quot;2&quot; class=&quot;diff-empty diff-side-deleted&quot;&gt;&lt;/td&gt;
  &lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td colspan=&quot;2&quot; class=&quot;diff-empty diff-side-deleted&quot;&gt;&lt;/td&gt;
  &lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Navbox deployment}}&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/td&gt;
  &lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Related}}&lt;/div&gt;&lt;/td&gt;
  &lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Related}}&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://ohcnwiki.tellmey.fyi/index.php?title=Deployment/Kubernetes/Reference_architecture&amp;diff=202&amp;oldid=prev</id>
		<title>Admin: OHC identity seed</title>
		<link rel="alternate" type="text/html" href="https://ohcnwiki.tellmey.fyi/index.php?title=Deployment/Kubernetes/Reference_architecture&amp;diff=202&amp;oldid=prev"/>
		<updated>2026-07-04T23:07:41Z</updated>

		<summary type="html">&lt;p&gt;OHC identity seed&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{Doc header&lt;br /&gt;
|type=guide&lt;br /&gt;
|domain=deployment&lt;br /&gt;
|title=Reference architecture&lt;br /&gt;
|order=3&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
This page describes the reference deployment topology for Care: a self-contained, two-node Kubernetes cluster that runs the Care backend and frontend alongside every supporting datastore and platform service. It is the architecture implemented by the [&amp;lt;nowiki/&amp;gt;https://github.com/ohcnetwork/deployment-k8 &amp;lt;code&amp;gt;deployment-k8&amp;lt;/code&amp;gt;] repository, where the infrastructure is provisioned with OpenTofu/Terraform and Helm.&lt;br /&gt;
&lt;br /&gt;
The design targets on-premise or single-tenant cloud installations: everything — database, object storage, search, container registry, terminology server, monitoring — lives inside the cluster, so the deployment has no hard dependency on managed cloud services beyond an optional S3-compatible target for off-site backups.&lt;br /&gt;
&lt;br /&gt;
{{Info|1=This is a reference design, not a turnkey product. Several pieces are still hardening or evolving — see [[#planned-and-evolving|Planned and evolving]] below, and treat the [[Deployment/Kubernetes/Day-2 operations|day-2 operations]] page as the operational companion to this one.}}&lt;br /&gt;
&lt;br /&gt;
== How the repository maps to the architecture ==&lt;br /&gt;
&lt;br /&gt;
The component inventory below is grounded in three directories of the &amp;lt;code&amp;gt;deployment-k8&amp;lt;/code&amp;gt; repository. Reading these is the fastest way to see exactly what gets deployed:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Directory&lt;br /&gt;
! What it holds&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;infra/&amp;lt;/code&amp;gt;&lt;br /&gt;
| OpenTofu/Terraform modules, applied per concern: &amp;lt;code&amp;gt;networking&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;volumes&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;database&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;objectstore&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;registry&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;opensearch&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;monitoring&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;care&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;odoo&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;metabase&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;snowstorm&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;helm_charts/&amp;lt;/code&amp;gt;&lt;br /&gt;
| First-party Helm charts maintained in-repo: &amp;lt;code&amp;gt;care_be&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;care_fe&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;odoo&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;metabase&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;snowstorm&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;helm_values/&amp;lt;/code&amp;gt;&lt;br /&gt;
| Values overlays for upstream and first-party charts: &amp;lt;code&amp;gt;care-be&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;care-fe&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;longhorn&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;rustfs&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;postgres-db&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;valkey&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;opensearch-operator&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;opensearch-cluster&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;zot&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;odoo&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;metabase&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;snowstorm&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;nginx&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;prom-stack&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;loki&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;alloy&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;grafana-agent-operator&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;argo&amp;lt;/code&amp;gt;.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Each &amp;lt;code&amp;gt;infra/&amp;amp;lt;module&amp;amp;gt;&amp;lt;/code&amp;gt; is applied with the repository&amp;#039;s &amp;lt;code&amp;gt;deployer.py&amp;lt;/code&amp;gt; helper (&amp;lt;code&amp;gt;apply-tofu&amp;lt;/code&amp;gt; to run the OpenTofu module, &amp;lt;code&amp;gt;apply-k8&amp;lt;/code&amp;gt; to apply any generated Kubernetes manifests). See [[Deployment/Kubernetes/Deploying Care onto the cluster|Deploying Care onto the cluster]] for the exact order.&lt;br /&gt;
&lt;br /&gt;
== Cluster topology ==&lt;br /&gt;
&lt;br /&gt;
The reference cluster is &amp;#039;&amp;#039;&amp;#039;two nodes on the same network&amp;#039;&amp;#039;&amp;#039;, provisioned with &amp;lt;code&amp;gt;kubeadm&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* A &amp;#039;&amp;#039;&amp;#039;primary node&amp;#039;&amp;#039;&amp;#039; (labelled &amp;lt;code&amp;gt;ohn/role=primary&amp;lt;/code&amp;gt;) that runs the control plane and hosts the stateful, latency-sensitive workloads — PostgreSQL, the RustFS object store, the container registry, and search.&lt;br /&gt;
* A &amp;#039;&amp;#039;&amp;#039;secondary / failover node&amp;#039;&amp;#039;&amp;#039; (labelled &amp;lt;code&amp;gt;ohn/role=secondary&amp;lt;/code&amp;gt;) that hosts the public-facing ingress and the observability stack, and acts as the failover target.&lt;br /&gt;
&lt;br /&gt;
The primary control-plane node is made schedulable (its control-plane taint is removed) so it can also run application workloads. Node labels drive placement: Helm values pin pods to the correct node via &amp;lt;code&amp;gt;nodeSelector&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;ohn/role&amp;lt;/code&amp;gt;. Data is &amp;#039;&amp;#039;&amp;#039;replicated across both nodes&amp;#039;&amp;#039;&amp;#039; at the storage layer (see [[#storage|Storage]]) so the cluster can tolerate the loss of a node and be rebuilt from replicated volumes plus off-site backups.&lt;br /&gt;
&lt;br /&gt;
{{Note|1=The cluster is built directly with &amp;lt;code&amp;gt;kubeadm&amp;lt;/code&amp;gt; against a stable Kubernetes release line (the install notes target the &amp;lt;code&amp;gt;v1.34&amp;lt;/code&amp;gt; package channel). The full node-bootstrap procedure — installing containerd, &amp;lt;code&amp;gt;kubelet&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;kubeadm&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;kubectl&amp;lt;/code&amp;gt;, joining the worker, removing taints, and applying node labels — is covered in [[Deployment/Kubernetes/Provisioning the cluster|Provisioning the cluster]].}}&lt;br /&gt;
&lt;br /&gt;
== Networking ==&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CNI: Cilium.&amp;#039;&amp;#039;&amp;#039; Pod networking and service routing are handled by Cilium, installed in &amp;lt;code&amp;gt;kubernetes&amp;lt;/code&amp;gt; IPAM mode and pointed at the primary node&amp;#039;s API server.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Load balancing: NodePort / node external IPs.&amp;#039;&amp;#039;&amp;#039; Ingress controllers are exposed on the nodes rather than via a cloud load balancer. The public ingress binds the secondary node&amp;#039;s IP via &amp;lt;code&amp;gt;externalIPs&amp;lt;/code&amp;gt;; other services (for example the registry) are reached on fixed NodePorts. MetalLB is the intended direction for cleaner L2/L3 load balancing — see [[#planned-and-evolving|Planned and evolving]].&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Ingress + WAF.&amp;#039;&amp;#039;&amp;#039; Two ingress controllers run side by side, split into a &amp;#039;&amp;#039;&amp;#039;private&amp;#039;&amp;#039;&amp;#039; ingress class (&amp;lt;code&amp;gt;private-nginx&amp;lt;/code&amp;gt;, for internal/admin surfaces such as Grafana, the object store console, Odoo, and Metabase) and a &amp;#039;&amp;#039;&amp;#039;public&amp;#039;&amp;#039;&amp;#039; ingress class (&amp;lt;code&amp;gt;public-nginx&amp;lt;/code&amp;gt;, for the Care frontend, Care API, and other internet-facing hosts). The public ingress runs with a &amp;#039;&amp;#039;&amp;#039;web application firewall enabled&amp;#039;&amp;#039;&amp;#039; — ModSecurity with the OWASP Core Rule Set — to filter inbound traffic. The longer-term target is an Apache APISIX-based ingress with an integrated WAF/firewall.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TLS termination at the ingress.&amp;#039;&amp;#039;&amp;#039; Certificates terminate at the ingress controller. The reference setup uses Let&amp;#039;s Encrypt, ideally a &amp;#039;&amp;#039;&amp;#039;pre-fetched wildcard certificate&amp;#039;&amp;#039;&amp;#039; issued via DNS validation and loaded into each application namespace as a TLS secret (&amp;lt;code&amp;gt;lets-encrypt-cert&amp;lt;/code&amp;gt;). Internal pod-to-pod TLS is on the roadmap, not yet in place.&lt;br /&gt;
&lt;br /&gt;
{{Warning|1=Routing host names, node IPs, and domains are environment-specific and supplied through Terraform variables (for example &amp;lt;code&amp;gt;care_public_domain&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;careapi_public_domain&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;primary_node_ip&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;secondary_node_ip&amp;lt;/code&amp;gt;). Do not hardcode site-specific host names into shared manifests — keep them in your environment&amp;#039;s variable file.}}&lt;br /&gt;
&lt;br /&gt;
== Storage ==&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Block storage: Longhorn.&amp;#039;&amp;#039;&amp;#039; Replicated block volumes are provisioned by Longhorn, with dedicated &amp;lt;code&amp;gt;StorageClass&amp;lt;/code&amp;gt;es per workload (&amp;lt;code&amp;gt;longhorn-database&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;longhorn-object&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;longhorn-registry&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;longhorn-opensearch&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;longhorn-prom&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;longhorn-loki&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;longhorn-grafana-dashboard&amp;lt;/code&amp;gt;). Disks are tagged (&amp;lt;code&amp;gt;ssd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hdd&amp;lt;/code&amp;gt;) so latency-sensitive data lands on SSD and bulkier data on HDD, and a Longhorn &amp;lt;code&amp;gt;BackupTarget&amp;lt;/code&amp;gt; points at an S3-compatible bucket for volume snapshots.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Object storage: RustFS.&amp;#039;&amp;#039;&amp;#039; A MinIO-compatible object store (RustFS) provides S3-style buckets for Care&amp;#039;s file uploads and facility data, plus log/audit storage. It runs in standalone mode on the primary node, backed by a Longhorn volume, and is fronted by its own ingress for the management console.&lt;br /&gt;
&lt;br /&gt;
The buckets Care and the platform expect (for example &amp;lt;code&amp;gt;care-facility&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;care-fileupload&amp;lt;/code&amp;gt;, and the log/ruler buckets used by Loki) are created in RustFS as part of bring-up.&lt;br /&gt;
&lt;br /&gt;
== Components ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Component&lt;br /&gt;
! Role&lt;br /&gt;
|-&lt;br /&gt;
| Care backend&lt;br /&gt;
| Care API. Runs as a web deployment plus Celery workers and a Celery beat scheduler; a one-shot migration job applies database migrations. Pulls its image from the in-cluster registry.&lt;br /&gt;
|-&lt;br /&gt;
| Care frontend&lt;br /&gt;
| Static Care web app served by nginx.&lt;br /&gt;
|-&lt;br /&gt;
| PostgreSQL&lt;br /&gt;
| Primary relational datastore, run by the &amp;#039;&amp;#039;&amp;#039;CloudNativePG (CNPG) operator&amp;#039;&amp;#039;&amp;#039; as a managed &amp;lt;code&amp;gt;Cluster&amp;lt;/code&amp;gt;. Hosts the &amp;lt;code&amp;gt;care&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;odoo&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;metabase&amp;lt;/code&amp;gt; databases as managed roles/databases, with WAL archiving and scheduled backups to S3 via the Barman Cloud plugin.&lt;br /&gt;
|-&lt;br /&gt;
| Valkey / Redis&lt;br /&gt;
| In-memory cache and Celery broker for the Care backend. Run non-persistent (no disk, no backups).&lt;br /&gt;
|-&lt;br /&gt;
| OpenSearch&lt;br /&gt;
| Search and indexing tier for Care, run via an operator with a single-node cluster and a dedicated Longhorn volume.&lt;br /&gt;
|-&lt;br /&gt;
| RustFS&lt;br /&gt;
| S3-compatible object store (MinIO-compatible) for uploads, facility files, and logs.&lt;br /&gt;
|-&lt;br /&gt;
| Odoo&lt;br /&gt;
| ERP/back-office integration that Care talks to through the &amp;lt;code&amp;gt;care_odoo&amp;lt;/code&amp;gt; plugin.&lt;br /&gt;
|-&lt;br /&gt;
| Metabase&lt;br /&gt;
| Analytics and dashboards over the Care database.&lt;br /&gt;
|-&lt;br /&gt;
| Snowstorm&lt;br /&gt;
| SNOMED CT terminology server (with a data-loader step to seed terminology).&lt;br /&gt;
|-&lt;br /&gt;
| zot&lt;br /&gt;
| OCI container registry that holds the Care backend/frontend images built and pushed during deployment.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{{Note|1=The Care backend is pluggable: integrations such as &amp;lt;code&amp;gt;care_odoo&amp;lt;/code&amp;gt; are installed as additional plugs at deploy time rather than baked into the core image. See the contributor guide on [[Contributing/Plugin system|Care pluggable apps]] for how plugs are structured.}}&lt;br /&gt;
&lt;br /&gt;
== Observability ==&lt;br /&gt;
&lt;br /&gt;
Monitoring is a self-hosted Grafana stack, installed by the &amp;lt;code&amp;gt;monitoring&amp;lt;/code&amp;gt; module:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Prometheus + Grafana&amp;#039;&amp;#039;&amp;#039; via the &amp;lt;code&amp;gt;kube-prometheus-stack&amp;lt;/code&amp;gt; chart, for metrics and dashboards. Grafana is exposed on the private ingress.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Loki&amp;#039;&amp;#039;&amp;#039; for log aggregation, with its chunk/ruler storage backed by the in-cluster RustFS object store.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Alloy&amp;#039;&amp;#039;&amp;#039; runs as the log shipper: it discovers pods, relabels Kubernetes metadata onto log streams, and pushes them to Loki.&lt;br /&gt;
&lt;br /&gt;
Prometheus, Loki, and Grafana persist to Longhorn volumes pinned to the secondary node.&lt;br /&gt;
&lt;br /&gt;
== Request and data flow ==&lt;br /&gt;
&lt;br /&gt;
The high-level path from a client to a datastore:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;                         Clients (browsers, integrations)&lt;br /&gt;
                                      |&lt;br /&gt;
                                      v&lt;br /&gt;
              +-------------------------------------------------+&lt;br /&gt;
              |   Ingress controllers (nginx + WAF / OWASP CRS) |&lt;br /&gt;
              |   TLS termination (Let&amp;#039;s Encrypt wildcard)      |&lt;br /&gt;
              |   public-nginx  |  private-nginx                |&lt;br /&gt;
              +-------------------------------------------------+&lt;br /&gt;
                       |                         |&lt;br /&gt;
                       v                         v&lt;br /&gt;
              +----------------+        +------------------+&lt;br /&gt;
              |   Care FE      |        |   Care BE        |&lt;br /&gt;
              |   (nginx)      |  ---&amp;gt;  |  web + celery    |&lt;br /&gt;
              +----------------+        |  + beat          |&lt;br /&gt;
                                        +------------------+&lt;br /&gt;
                                               |&lt;br /&gt;
            +----------------+----------------+----------------+&lt;br /&gt;
            v                v                v                v&lt;br /&gt;
     +-------------+  +-------------+  +-------------+  +-------------+&lt;br /&gt;
     | PostgreSQL  |  | Valkey/     |  | OpenSearch  |  |  RustFS     |&lt;br /&gt;
     | (CNPG)      |  | Redis       |  | (search)    |  | (S3 object  |&lt;br /&gt;
     | care DB     |  | cache/broker|  |             |  |  storage)   |&lt;br /&gt;
     +-------------+  +-------------+  +-------------+  +-------------+&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Supporting services — Odoo, Metabase, Snowstorm, the zot registry, and the Prometheus/Grafana/Loki stack — sit alongside this path. Odoo and Metabase share the same CNPG-managed PostgreSQL cluster (separate databases), Care reaches Odoo over the internal service network, and the registry serves the Care images that the backend and frontend deployments pull.&lt;br /&gt;
&lt;br /&gt;
== Planned and evolving ==&lt;br /&gt;
&lt;br /&gt;
Several aspects of this architecture are explicitly works in progress. Treat the items below as roadmap direction drawn from the design&amp;#039;s own backlog, not as current guarantees:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PostgreSQL fully on a cluster operator.&amp;#039;&amp;#039;&amp;#039; PostgreSQL is being consolidated onto the CloudNativePG operator (moving off hand-written YAML and onto operator-managed clusters) for both Care and the supporting databases.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Ingress and load balancing.&amp;#039;&amp;#039;&amp;#039; Migrating toward an APISIX-based ingress with an integrated WAF/firewall, and toward MetalLB for load balancing in place of plain NodePort/external-IP exposure.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Terminology server.&amp;#039;&amp;#039;&amp;#039; The Snowstorm-based terminology service is still being wired into the standard deployment flow.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Secrets and automation.&amp;#039;&amp;#039;&amp;#039; Generating credentials through Terraform and auto-wiring them into dependent services, rather than creating secrets by hand.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Backups and restore.&amp;#039;&amp;#039;&amp;#039; Extending snapshots/backups to cover every stateful component and documenting a clean restore of a fresh cluster from backups.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Inter-pod TLS.&amp;#039;&amp;#039;&amp;#039; Adding TLS for database and other pod-to-pod communication.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Version-locking the charts.&amp;#039;&amp;#039;&amp;#039; Pinning all Helm chart versions so deployments are fully reproducible.&lt;br /&gt;
&lt;br /&gt;
For how these components are stood up in practice, continue to [[Deployment/Kubernetes/Provisioning the cluster|Provisioning the cluster]] and [[Deployment/Kubernetes/Deploying Care onto the cluster|Deploying Care onto the cluster]].&lt;br /&gt;
&lt;br /&gt;
{{Related}}&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
</feed>