Skip to main content
This reference architecture describes the canonical production deployment pattern for Cognisafe on Azure Kubernetes Service. It is the pattern a well-run platform team would build: zone-redundant, GitOps-driven, secret-rotation-safe, and observable from day one. Use it as an authoritative baseline, not a starting point you have to work backwards from. This document is intentionally opinionated. Where there are trade-offs, a specific choice is made and the rationale is given.

Architecture overview

Why this architecture

Zone redundancy at every layer

A single AZ failure must not cause an outage. This requires: a zone-redundant load balancer, NGINX Ingress replicas across zones, application pods spread across zones via topologySpreadConstraints, a zone-redundant PostgreSQL Flexible Server, and an Azure Cache for Redis Standard C1 (which includes a replica in a separate fault domain). The AKS control plane is zone-redundant by default when you set --zones 1 2 3 on the node pools.

Spot instances for the safety worker only

The safety worker is the only component suitable for spot instances. It is stateless, processes jobs from a queue, and handles SIGTERM gracefully. The proxy, API, and web services are latency-sensitive and must run on regular-priority nodes to avoid the 30-second eviction notice.

GitOps, not imperative kubectl

All cluster state is declared in a Helm chart checked into a deploy/ directory. The CI pipeline runs helm upgrade --install on every merge to main. There is no “apply some YAML files” step in production — that creates configuration drift. The Helm chart is the single source of truth.

Node pool design

Spot nodes receive 30 seconds notice before eviction via a scheduled event. The safety worker must catch SIGTERM and finish or re-queue its in-flight job before exiting. Set terminationGracePeriodSeconds: 60 on the safety-worker pod spec. If a worker exits mid-job, the Redis job will remain in the queue and be picked up by another replica.

Namespace design

Three namespaces keep platform, data, and monitoring concerns cleanly separated. RBAC policies are applied per namespace so the monitoring namespace cannot write to platform secrets.
In the reference architecture, PostgreSQL and Redis run as fully managed Azure services (PostgreSQL Flexible Server and Azure Cache for Redis), not in-cluster. The cognisafe-data namespace is only relevant if you choose to run in-cluster databases for cost or latency reasons — this is not recommended for production.

High availability configuration

Proxy (2+ replicas)

API (2+ replicas)

Safety worker (3 replicas minimum, HPA up to 20)

If queue depth is a better signal than CPU for your workload (common during batch red team runs), deploy KEDA with the Azure Cache for Redis scaler targeting the safety_score_jobs list length. Set listLength: 100 as the trigger threshold — this scales one worker replica per 100 queued jobs.

PostgreSQL Flexible Server (zone-redundant)

Azure Cache for Redis (Standard C1 with replica)


Secret management

All secrets live in Azure Key Vault and are injected into pods at runtime via the Secrets Store CSI driver. Secrets are never stored in etcd, never in manifests, and never in environment files committed to source control. Key rotation happens in Key Vault; the CSI driver picks up new values on the next pod restart without a redeployment.

Certificate management

cert-manager issues and renews a wildcard TLS certificate for *.cognisafe.yourco.com using DNS-01 challenge against Azure DNS. This avoids exposing each individual subdomain for HTTP-01 validation.

Ingress rules


Deployment pipeline

The deployment pipeline runs on GitHub Actions. The pipeline builds, tests, pushes images to ACR, and performs a Helm upgrade. The Alembic migration runs as a Kubernetes Job before the new API pods are promoted, not as an init container in production (init containers on every replica are acceptable in staging but create race conditions at scale).
--atomic on the Helm upgrade automatically rolls back to the previous release if any pod fails its readiness probe within the timeout window. This prevents a bad deploy from leaving the cluster in a partially-upgraded state.

Observability

Azure Monitor + Container Insights

Container Insights is enabled at cluster creation. It provides per-node and per-pod CPU, memory, network, and disk metrics in Azure Monitor, plus log forwarding to a Log Analytics workspace. Navigate to AKS → Monitoring → Insights in the Azure Portal.

kube-prometheus-stack

Key alerts

Custom Cognisafe Prometheus metrics

Expose these metrics from the API service by adding a /metrics endpoint (FastAPI + prometheus-fastapi-instrumentator):
Then define a ServiceMonitor so Prometheus scrapes the API pods:

Backup and restore

Daily pg_dump to Azure Blob Storage

Retention: configure a lifecycle management policy on the storage account to delete blobs older than 30 days.

Tested restore procedure

1

List available backups

2

Download the target dump

3

Restore to a recovery PostgreSQL instance

4

Verify row counts match expectation

5

Promote the recovery instance

Update the POSTGRES_URL secret in Key Vault to point at the recovery server. Restart all pods to pick up the new connection string.
Run this procedure quarterly in a dedicated recovery subscription, not against production. An untested backup is not a backup.

Disaster recovery

Failover procedure (full region loss)

1

Promote PostgreSQL geo-replica

2

Deploy AKS cluster in secondary region

Run the same Terraform/Bicep that provisions the primary cluster, targeting the secondary region (e.g., northeurope).
3

Update Key Vault secrets

Update cognisafe-postgres-url and cognisafe-redis-url in the secondary region’s Key Vault to point at the promoted replicas.
4

Deploy application via Helm

5

Reroute DNS

Update Azure DNS or Traffic Manager to point *.cognisafe.yourco.com at the secondary region’s load balancer IP.

Cost estimate

All prices are approximate UK South list pricing as of mid-2026. Actual costs depend on reserved instance discounts, egress, and storage.
Purchase 1-year reserved instances for the system and user node pools. Spot instances on the worker pool already provide 60–80% discount. Reserved instances on the regular pools typically save an additional 30–40% over pay-as-you-go.

Security hardening checklist

  • Network Policy enabled (--network-policy azure) — deny all ingress/egress by default, allow only required pod-to-pod paths
  • Private endpoint for PostgreSQL Flexible Server — no public network access
  • Private endpoint for Azure Cache for Redis — TLS 1.2 minimum, no non-SSL port
  • Azure Key Vault firewall — allow only AKS subnet CIDR and deployment pipeline IPs
  • ACR network rules — allow only AKS kubelet subnet
  • Pod Security Standards — set pod-security.kubernetes.io/enforce: restricted on cognisafe-system namespace
  • Container images built on distroless base, non-root UID, read-only root filesystem
  • Dependabot or Renovate enabled on the repository for automated dependency updates
  • Microsoft Defender for Containers enabled on the AKS cluster
  • Audit logging enabled on the AKS control plane, forwarded to Log Analytics