Skip to content

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:

  1. Run RabbitMQ in-cluster on AKS (set rabbitmq.enabled: true).
  2. 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.

See also