Cloud-managed swap paths¶
Status: Current | Last reviewed: 2026-08-30
How to replace each in-cluster infra component with a managed cloud equivalent. The umbrella chart's per-cloud values files already do most of this; this page explains what they do and what to override.
The matrix¶
| Service | In-cluster | Helm toggle | AWS managed | GCP managed | Azure managed | Override env vars | Swap blockers |
|---|---|---|---|---|---|---|---|
| postgres | supabase/postgres:15.8.1 |
postgres.enabled: false |
RDS PostgreSQL | Cloud SQL | Azure Database for PostgreSQL | DATABASE_URL |
None — JSONB / RLS / PL/pgSQL all supported |
| pgbouncer | bitnami/pgbouncer |
pgbouncer.enabled: true |
Run pgbouncer in-cluster pointing at RDS | Skip (use Cloud SQL connector) | Run in-cluster | n/a (apps see pgbouncer as if it were postgres) | None |
| redis | redis:7-alpine |
redis.enabled: false |
ElastiCache (cluster mode) | Memorystore | Azure Cache for Redis | REDIS_URL, COLLAB_REDIS_URL |
None — pubsub + TTL supported |
| neo4j | neo4j:5.24 |
neo4j.enabled: false |
Neo4j Aura | Neo4j Aura | Neo4j Aura | NEO4J_URI, NEO4J_USER, NEO4J_PASSWORD |
None — Aura is bolt-compatible |
| rabbitmq | rabbitmq:3-management-alpine |
rabbitmq.enabled: true/false |
Amazon MQ for RabbitMQ | self-managed | Azure Service Bus (different protocol) | RABBITMQ_URL |
Azure Service Bus is not AMQP-compatible without a shim |
| object storage | none (S3 is the default; MinIO exists only as a CI conformance container) | n/a | S3 (default) | GCS S3 interoperability | Azure Blob (future native adapter) | STORAGE_BUCKET_*, STORAGE_REGION, STORAGE_ENDPOINT, STORAGE_FORCE_PATH_STYLE, STORAGE_ACCESS_KEY/STORAGE_SECRET_KEY |
S3-compatible endpoint required — see object-storage.md |
| asterisk / kamailio / janus / rtpengine / coturn | custom | per-service nodeSelector + tolerations |
None — operator-managed | None — operator-managed | None — operator-managed | hostNetwork pod env (PUBLIC_IP, LAN_IP) |
Kernel module unavailable — userspace forwarding only |
How to actually swap each one¶
postgres → managed PostgreSQL¶
In your values-<cloud>.yaml:
postgres:
enabled: false
externalPostgres:
host: trueserve-rds.cluster-xxx.us-east-1.rds.amazonaws.com # or Cloud SQL / Azure DB
port: 5432
database: trusrv_agents
username: tsf_app
passwordSecret:
name: trueserve-db
key: password
The chart wires DATABASE_URL for every app from these values. RLS, JSONB, and PL/pgSQL are all supported on RDS, Cloud SQL, and Azure DB.
The dbMigration Helm hook job runs deploy/init-scripts/*.sql against the managed DB on each release.
Note: AWS deploys put pgbouncer in-cluster between apps and RDS. Set
pgbouncer.enabled: true. GCP uses the Cloud SQL connector sidecar (no pgbouncer). Azure runs pgbouncer in-cluster.
redis → ElastiCache / Memorystore / Azure Cache¶
redis:
enabled: false
externalRedis:
url: rediss://trueserve-redis.xxx.cache.amazonaws.com:6380
collabUrl: rediss://trueserve-redis.xxx.cache.amazonaws.com:6380/4
passwordSecret:
name: trueserve-redis
key: auth-token
ElastiCache cluster mode requires the redis-cluster URL form and a client that handles slot routing. The Python redis-py and Node ioredis clients TrueServe uses both support cluster mode out of the box. No app changes.
The collab-gateway specifically uses Redis DB 4 (collab:<scope>:<roomId>:<userId> keys). ElastiCache cluster mode does not support multiple databases — collapse the keyspace into prefixes, or run a separate cluster for collab.
neo4j → Neo4j Aura¶
neo4j:
enabled: false
externalNeo4j:
uri: neo4j+s://xxxxx.databases.neo4j.io
username: neo4j
passwordSecret:
name: trueserve-neo4j
key: password
The bolt protocol is identical between in-cluster Neo4j and Aura.
rabbitmq → Amazon MQ / managed alternative¶
AWS:
rabbitmq:
enabled: false
externalRabbitmq:
url: amqps://trueserve.mq.us-east-1.amazonaws.com:5671
passwordSecret:
name: trueserve-mq
key: password
Amazon MQ for RabbitMQ is a 1:1 drop-in.
Azure Service Bus is not a drop-in — the protocol is AMQP 1.0 with a different management API. Either:
- Run RabbitMQ in-cluster on AKS (set
rabbitmq.enabled: true). - Adopt Service Bus and add a translation layer in
async-agent's consumer.
GCP has no managed RabbitMQ. Self-host in-cluster, or migrate to Pub/Sub (which event-publisher already supports for events but not as the main work queue).
Object storage → S3 / GCS S3 interoperability¶
AWS S3 is the default (empty STORAGE_ENDPOINT). All services ride one seam per
language — @trueserve/storage (TS), trueserve_storage (Python),
internal/storage (Go, media-proxy) — so a provider swap is configuration only:
# GCS S3-interop (values-cloud-gcp.yaml carries this shape)
env:
STORAGE_ENDPOINT: "https://storage.googleapis.com"
STORAGE_FORCE_PATH_STYLE: "true"
STORAGE_BUCKET_RECORDINGS: "<gcs bucket>"
STORAGE_REGION: "us-east-1" # SigV4 region label for the XML API
# HMAC keys via trueserve-storage-secrets → STORAGE_ACCESS_KEY/STORAGE_SECRET_KEY
Leave STORAGE_ACCESS_KEY unset on AWS and the SDK default credential chain (IRSA)
engages. Conversation archive sinks can still carry tenant-specific destination
credentials through an integration row. Full detail, roles → buckets, and the
IRSA/presign caveats: object-storage.md.
Azure Blob needs a native adapter or an S3-compatible gateway before it can be used for conversation recordings or archive bundles.
Media services — no managed equivalent¶
asterisk, kamailio, janus, rtpengine, coturn always run in-cluster. No cloud has a managed Asterisk or Kamailio service.
What changes per cloud is the node pool configuration. See media-pool-tuning.md.
Verification¶
After swapping, run the smoke tests:
# postgres reachable?
kubectl run -it --rm psql --image=postgres:15 -- psql "$DATABASE_URL" -c '\l'
# redis reachable?
kubectl run -it --rm redis --image=redis:7 -- redis-cli -u "$REDIS_URL" ping
# object store reachable? (HEAD via the seam — a 404 from a reachable,
# authorized store returns None; connectivity/credential faults raise)
kubectl exec deploy/conversation-api -- python -c \
"from storage import store_for_bucket; s = store_for_bucket(None); print(s.bucket, s.stat('.reachability-probe'))"
What you cannot do¶
- Run TrueServe with no Postgres at all. Every service depends on it.
- Run TrueServe with no object store. Recordings need somewhere to land.
- Run TrueServe with no Redis. Session caching and collab-gateway pubsub require it.
- Replace media services with WebRTC SFUs alone. SIP carriers will not call into LiveKit/Daily.