---
title: "Cloud Support Matrix"
url: "https://docs.caracal.run/v1.0/operations/cloud-support-matrix/"
markdown_url: "https://docs.caracal.run/markdown/v1.0/operations/cloud-support-matrix.md"
description: "What each deployment model requires per cloud, what ships today, and what a new provider must supply."
page_type: "reference"
concepts: []
requires: []
---

# Cloud Support Matrix

Canonical URL: https://docs.caracal.run/v1.0/operations/cloud-support-matrix/
Markdown URL: https://docs.caracal.run/markdown/v1.0/operations/cloud-support-matrix.md
Description: What each deployment model requires per cloud, what ships today, and what a new provider must supply.
Page type: reference
Concepts: none
Requires: none

---

Caracal ships three deployment models. The stack itself is provider-neutral: the same released images, the same environment contract, the same schema migrations. Each model has a provider-neutral core and a small adapter per cloud, so a provider is a translation layer rather than a branch in the deployment logic. To add one, see [Add a Cloud Provider](/v1.0/operations/add-a-cloud-provider/).

## Status

| Model | Core | Azure | AWS | Google Cloud |
| --- | --- | --- | --- | --- |
| Virtual machine | `caracalHost` cloud-init | Supported | Experimental | Experimental |
| Managed container platform | `containerPlatform` topology | Supported (Container Apps) | Experimental (ECS on Fargate) | Experimental (Cloud Run) |
| Kubernetes | Helm chart | Supported (AKS overlay) | Experimental (EKS overlay) | Experimental (GKE overlay) |

**Supported** means the adapter is implemented, validated in CI, and exercised against a live account. It is not a capacity, availability, or compliance claim.

**Experimental** means the adapter renders, validates against the provider's own schema, and satisfies the shared contract, but has not been deployed to a live account. Azure is the reference implementation; treat the others as a starting point you verify in your own environment.

## What Never Changes

These hold on every cloud and every model, and no adapter may alter them:

* The published images, with the container start command selecting the service role.
* The environment and secret contract in [Configuration Reference](/v1.0/reference/configuration/).
* Expand-only schema migrations applied to completion before a rollout.
* Postgres with migration privileges, and Redis with Streams and `noeviction`.
* `/health` and `/ready`, and the same readiness semantics.

## Virtual Machines

`caracalHost` renders cloud-init; a `providers/<cloud>/host` adapter creates the instance, its inbound exposure, its cloud identity, and its DNS records. Every adapter takes the same inputs and returns the same outputs, so changing cloud means changing a module source. Set `tlsProxy` on the core to terminate HTTPS and derive the console origin and token issuer from its routes.

| Concept | Azure | AWS | Google Cloud |
| --- | --- | --- | --- |
| Instance | Linux virtual machine | EC2 instance | Compute Engine instance |
| Public address | Static public IP | Elastic IP | Static external address |
| Inbound exposure | Network security group | Security group | VPC firewall rule |
| Identity | User-assigned managed identity | IAM role and instance profile | Attached service account |
| DNS | Azure DNS A record | Route 53 record | Cloud DNS record set |

**Caveat.** The bootstrap installs Docker through `get.docker.com`, which supports Debian, Ubuntu, RHEL, and Fedora. It does not support Amazon Linux 2023 or Container-Optimized OS. The adapters use Ubuntu images; if you change the image, install the container runtime with `extraRuncmd`.

## Managed Container Platforms

`topology.yaml` describes the deployment once; an adapter maps it onto a provider. Each adapter declares how its platform delivers secrets and how services address each other, and the core does the rest.

| Neutral concept | Azure Container Apps | AWS ECS on Fargate | Google Cloud Run |
| --- | --- | --- | --- |
| Role selection | `command` override | `entryPoint` and `command` | `command` override |
| Secret delivery | Projected file | Environment variable | Environment variable |
| Secret reference | Key Vault URL with a managed identity | Secrets Manager ARN with a task role | Secret Manager version with a service account |
| Registry auth | Managed identity with `AcrPull` | Task execution role | Runtime service account |
| Internal address | `https://<app>.internal.<envDomain>` | Service Connect discovery name | Internal-ingress service URL |
| One-shot migration | Container Apps Job | Task definition run with `RunTask` | Cloud Run Job |
| Autoscaling | `http` concurrency rule | Service desired count | Concurrency and instance bounds |
| Durable spill storage | NFS Azure Files share | EFS access point | None available |
| Resource shape | Fixed vCPU-to-memory ladder, 4 vCPU ceiling | Fargate task sizes | Up to 8 vCPU and 32 GiB |

Two consequences are worth knowing before choosing a provider:

* **Only Container Apps projects credentials as files.** ECS cannot mount a secret at all, and Cloud Run refuses two secret volumes at one mount path, so neither can give a service the several credentials it needs as files. On both, credentials are bound to environment variables and resolved before the instance starts. They never appear in a task definition or service manifest, but a principal who can describe the running workload can read them; scope that permission accordingly.
* **Cloud Run has no durable per-instance volume.** STS and Gateway spill audit evidence to disk when Redis is unreachable; on Cloud Run that evidence survives only while the instance lives.

## Kubernetes

The chart is cloud-neutral. Four values carry the entire provider surface:

| Value | AKS | EKS | GKE |
| --- | --- | --- | --- |
| `replayPersistence.storageClassName` | `managed-csi` | `gp3` | `premium-rwo` |
| `ingress.*.className` | `webapprouting.kubernetes.azure.com` | `alb` | `gce` |
| `serviceAccount.annotations` | `azure.workload.identity/client-id` | `eks.amazonaws.com/role-arn` | `iam.gke.io/gcp-service-account` |
| `global.podLabels` | `azure.workload.identity/use: "true"` | not required | not required |

`infra/helm/caracal/examples` carries a worked overlay for each. A cloud that needs a chart change is a bug in the chart.

## Database Prerequisites

The baseline migration creates the `pgcrypto` extension and six service roles, and grants `CREATEDB` to the role that owns the console's auth database. Managed Postgres restricts all three, so confirm them before the first deployment on any provider. A migration job that fails here fails the whole rollout, by design.

| Requirement | Azure Database for PostgreSQL | Amazon RDS and Aurora | Cloud SQL |
| --- | --- | --- | --- |
| `pgcrypto` | **Must be added to the `azure.extensions` server parameter first.** `CREATE EXTENSION` fails until it is, and the parameter change needs a server restart | Available to the master user | Available to the default user |
| Create roles | Administrative user is a member of `azure_pg_admin` | Master user holds `rds_superuser` | Default user holds `cloudsqlsuperuser` |
| Grant `CREATEDB` | Permitted for the administrative user | Permitted for the master user | Permitted for the default user |

Run the migration job against the managed instance before provisioning anything else. It is the cheapest possible failure and it exercises connectivity, TLS, credentials, and privileges in one step.

## Choosing

Use [Choose a Deployment Profile](/v1.0/operations/deployment-profiles/) to pick a model, then the workflow page for it: [Docker Compose](/v1.0/operations/docker-compose/), [a managed container platform](/v1.0/operations/managed-container-platforms/), or [Helm](/v1.0/operations/kubernetes-helm/).
