Skip to content

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.

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.

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.

ControlHelm chartContainer platformConsequence
East-west network policyDefault-deny NetworkPolicy per serviceNo pod-level policy; subnet NSGs onlyAny workload in the environment can reach Audit and Coordinator
Container hardeningreadOnlyRootFilesystem, dropped capabilities, runAsNonRoot, seccompNot configurableThe image’s own non-root user is the only remaining boundary
Audit spill durabilityPer-replica persistent volumeReplica-local ephemeral storage by defaultA replaced replica loses audit evidence it had not drained to Redis
Disruption budgetsPodDisruptionBudget per serviceNonePlatform maintenance can take replicas below your intended floor
Failure-domain spreadTopology spread constraints and anti-affinityPlatform-chosen placementReplica distribution across zones is not something you assert
Scheduling priorityPriorityClass keeps the control plane above best-effort workNoneNo eviction ordering under pressure
Migration orderingpre-install/pre-upgrade hook, automaticExplicit job run before the rolloutOrdering depends on the deploy flow, not the platform
Vertical headroomNode-sized4 vCPU and 8 GiB per replicaScale out rather than up; STS and Gateway hit this first
Metrics collectionServiceMonitor and PrometheusRuleNot consumed by the platformAlerting 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.

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.

  1. 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; do
    az acr import --name <registry> \
    --source ghcr.io/garudex-labs/${image}:v0.2.1 \
    --image ${image}:v0.2.1
    done
  2. Store the runtime credentials in the secret manager. render.mjs writes secrets.txt listing 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.

  3. 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.1
    gh release download "$tag" --repo Garudex-Labs/caracal \
    --pattern "caracal-deploy-${tag}.tar.gz" --pattern SHA256SUMS
    grep " caracal-deploy-${tag}.tar.gz$" SHA256SUMS | sha256sum --check --strict -
    gh attestation verify "caracal-deploy-${tag}.tar.gz" --repo Garudex-Labs/caracal
    tar -xzf "caracal-deploy-${tag}.tar.gz"
    npm --prefix caracal-deploy install --omit=dev
  4. 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 ./rendered

    The console block carries operator sign-in: operatorEmails lists the addresses or @domain suffixes admitted to the console, and console.auth names 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 in stable mode, a missing identity, or an unresolvable value. Review the manifests before applying them.

  5. 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
  6. 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.

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.

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.

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.

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:

Terminal window
az group delete --name <group> --yes

This 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.

Use Monitor Health and Metrics to rebuild the signals the chart’s ServiceMonitor and alert rules would have provided.