Deploy on a Managed Container Platform
The published images are role-agnostic: one Go image carries the STS, Gateway, and Audit binaries, one Node image carries API and Coordinator, and the container start command selects the role. Any platform that runs an OCI image with a custom start command, file-projected secrets, and HTTP probes can therefore run Caracal from released artifacts with no build step of your own.
infra/containerPlatform renders that deployment. topology.yaml describes the stack once, in platform-neutral terms; a target module maps it onto a provider. Azure Container Apps ships today.
Use Criteria
Section titled “Use Criteria”Choose this path when you want a managed runtime, per-service scaling, and revision-based rollout without operating a cluster, and when the traded guarantees below are acceptable. Choose Deploy with Helm when any of them are not.
What You Trade Away
Section titled “What You Trade Away”The Helm chart enforces controls that container platforms do not expose. None of them are optional in a hardened deployment; on this path you replace them or accept their absence.
| Control | Helm chart | Container platform | Consequence |
|---|---|---|---|
| East-west network policy | Default-deny NetworkPolicy per service | No pod-level policy; subnet NSGs only | Any workload in the environment can reach Audit and Coordinator |
| Container hardening | readOnlyRootFilesystem, dropped capabilities, runAsNonRoot, seccomp | Not configurable | The image’s own non-root user is the only remaining boundary |
| Audit spill durability | Per-replica persistent volume | Replica-local ephemeral storage by default | A replaced replica loses audit evidence it had not drained to Redis |
| Disruption budgets | PodDisruptionBudget per service | None | Platform maintenance can take replicas below your intended floor |
| Failure-domain spread | Topology spread constraints and anti-affinity | Platform-chosen placement | Replica distribution across zones is not something you assert |
| Scheduling priority | PriorityClass keeps the control plane above best-effort work | None | No eviction ordering under pressure |
| Migration ordering | pre-install/pre-upgrade hook, automatic | Explicit job run before the rollout | Ordering depends on the deploy flow, not the platform |
| Vertical headroom | Node-sized | 4 vCPU and 8 GiB per replica | Scale out rather than up; STS and Gateway hit this first |
| Metrics collection | ServiceMonitor and PrometheusRule | Not consumed by the platform | Alerting must be rebuilt on the provider’s monitor |
Kubernetes remains the recommended path for production because those controls are the deployment’s security and availability posture, not decoration. This path is appropriate for evaluation, staging, lower-traffic production, and teams with no cluster to run.
What does not change: horizontal autoscaling, replica floors and ceilings, health-gated rolling updates, TLS ingress, and per-service resource limits are all expressed natively. Revision traffic splitting is a stronger canary primitive than the chart’s rolling update.
Prerequisites
Section titled “Prerequisites”Operate managed Postgres reachable with migration privileges, managed Redis with Streams and noeviction, a secret manager holding the runtime credentials, a registry, and a workload identity with pull and secret-read access. Review Choose a Cloud Profile for the dependency contract, which is identical here.
Procedure
Section titled “Procedure”-
Import the released images into your registry so deployments pull from a private, in-region source. No rebuild is involved, and the digest and its provenance are preserved:
Terminal window for image in caracal-go caracal-node caracal-web caracal-postgres; doaz acr import --name <registry> \--source ghcr.io/garudex-labs/${image}:v0.2.1 \--image ${image}:v0.2.1done -
Store the runtime credentials in the secret manager.
render.mjswritessecrets.txtlisting every required name in the platform’s naming form; the key material itself is the same set the Helm chart consumes, described in Rotate Keys and Secrets. -
Download the deployment tooling for the release you are deploying. It is a published, checksummed, attested artifact, so no source checkout is involved:
Terminal window tag=v0.2.1gh release download "$tag" --repo Garudex-Labs/caracal \--pattern "caracal-deploy-${tag}.tar.gz" --pattern SHA256SUMSgrep " caracal-deploy-${tag}.tar.gz$" SHA256SUMS | sha256sum --check --strict -gh attestation verify "caracal-deploy-${tag}.tar.gz" --repo Garudex-Labs/caracaltar -xzf "caracal-deploy-${tag}.tar.gz"npm --prefix caracal-deploy install --omit=dev -
Copy
caracal-deploy/containerPlatform/examples/azure.yaml, set your endpoints, public origins, operator admission, and provider wiring, and render:Terminal window node caracal-deploy/containerPlatform/render.mjs --config deployment.yaml --out ./renderedThe
consoleblock carries operator sign-in:operatorEmailslists the addresses or@domainsuffixes admitted to the console, andconsole.authnames at least one sign-in method - a Google or GitHub client id with the name of its client secret in the secret manager, or an SMTP transport. Rendering fails without a method, on a plaintext public origin, debug logging instablemode, a missing identity, or an unresolvable value. Review the manifests before applying them. -
Apply in order. Schema migrations must finish before any service revision rolls, because a release’s migrations are expand-only against the version still serving:
Terminal window CARACAL_RESOURCE_GROUP=<group> bash caracal-deploy/containerPlatform/scripts/deployAzure.sh ./rendered -
Restore what the platform does not provide: subnet-level network restrictions, provider-native metrics and alert rules equivalent to the chart’s, and backup and restore for Postgres and Redis.
Durable Audit Spill
Section titled “Durable Audit Spill”STS, Gateway, and Audit write audit evidence to disk when Redis is unreachable and drain it on the next start. With ephemeral storage that evidence does not survive replica replacement.
To keep it, attach an NFS file share and set azure.stateStorageName. Use NFS rather than SMB: the spill path fsyncs each file and its parent directory, an SMB share does not reliably honor that, and a failed fsync is accounted as lost evidence and fails readiness.
Verify
Section titled “Verify”Confirm secrets resolve into the container, migrations completed, and every service answers /ready. Run caracal-deploy/smokeTest.sh against the ingress hosts, then a canary token exchange and Gateway request, and locate the resulting audit evidence. Confirm internal-only services are not externally reachable.
Rollback or Recovery
Section titled “Rollback or Recovery”Roll back by activating the previous revision; it is immediate and does not rebuild. Migrations are never reversed by a revision rollback, which is why they stay expand-only. Keep the previous rendered manifests alongside the release they describe.
Teardown
Section titled “Teardown”Which teardown is correct depends on whether the environment holds anything you need to keep.
For a long-lived environment, caracal-deploy/containerPlatform/scripts/destroyAzure.sh removes only the container apps and jobs it created. The database, cache, vault, registry, and the environment itself survive, so audit evidence and credentials are not destroyed along with the compute.
For a temporary environment, put every resource in one resource group and delete the group:
az group delete --name <group> --yesThis is deliberately manual and deliberately not automated. It is atomic and complete, whereas removing only the applications leaves the database, cache, registry, and environment billing indefinitely. Confirm the result in Cost Analysis rather than trusting the command’s exit code: a teardown is proven by the spend reaching zero.
Next Step
Section titled “Next Step”Use Monitor Health and Metrics to rebuild the signals the chart’s ServiceMonitor and alert rules would have provided.

