Platform replicas with shared services
containers/compose.distributed.yaml adds a platform replica to an existing
Kubernetes-backed deployment. Each host runs an AstraBox API process. The
replicas use the same PostgreSQL database, OpenSandbox client-pool Redis,
OpenSandbox Lifecycle API, LiteLLM and channel gateway. Kubernetes schedules
the sandboxes; AstraBox does not select a machine for each user request.
This overlay assembles application connections. It does not provision those services, migrate an existing deployment, install a load balancer, or make a single service highly available.
Use it for a fresh deployment with explicit shared signing keys, or to add replicas to a deployment that already uses those same explicit keys. Converting automatically derived callback keys requires a maintenance and credential rotation plan; it is not a seamless configuration-only migration.
Prepare the shared services
- Use the same AstraBox database, including SessionStore and encrypted Vault records. Both hosts need private network access to PostgreSQL and Redis; a port bound to the first host's loopback is not reachable from the second. Restrict access to the participating hosts, not the public Internet.
- Use one authenticated external OpenSandbox service connected to the same Kubernetes cluster and namespace. Both API hosts must reach the lifecycle endpoint and the sandbox endpoints it returns.
- Carry the existing workspace settings into the environment file, because the
overlay replaces the base Compose environment. Persistent workspaces use
ASTRABOX_STORAGE_PROVIDER,ASTRABOX_SANDBOX_WORKSPACE_VOLUMEandASTRABOX_WORKSPACE_MOUNTER_IMAGE. For AWS EFS, configureASTRABOX_STORAGE_PROVIDER=aws_efsand the sameASTRABOX_EFS_FILE_SYSTEM_ID, and install the official CSI driver. The overlay setsASTRABOX_WORKSPACE_STORAGE_TOPOLOGY=shared, so the backing claim must be a shared filesystem that every eligible sandbox node can mount. See workspace storage and AWS EFS workspace storage. Persistent workspaces are optional. - Give both API hosts a kubeconfig readable by container uid 999; the overlay requires it. The platform workspace-mount controller and EFS provider use Kubernetes even when the lifecycle service is external. Use the same cluster and namespace as OpenSandbox. Grant the kubeconfig identity the helper permissions listed in workspace storage; the EFS provider also reads the backing claim and its PersistentVolume.
- Share one HTTPS LiteLLM service and its existing inference/administration credentials. Share one authenticated HTTPS channel gateway and its token; separate embedded gateways are not a shared messaging runtime.
- Put the replicas behind an operator-managed HTTP/WebSocket/SSE ingress.
Use the same public origin, identity configuration and Pod-reachable callback
URL on every replica. Include the public, callback and private health-check
hostnames in
ASTRABOX_ALLOWED_HOSTS. Drain a replica before removing it from service; set the termination grace period aboveASTRABOX_SHUTDOWN_DRAIN_SECONDS.
OpenSandbox version boundary
With OpenSandbox Server 0.2.3, use a single external lifecycle service with its own persistent SQLite store. This permits multiple AstraBox replicas and sandbox nodes; it does not provide lifecycle-control-plane high availability. Do not run independent SQLite-backed lifecycle replicas and assume that sharing Kubernetes also shares snapshot records. The 0.2.3 configuration reference supports only SQLite for server-managed metadata. A different upstream version and its documented shared-store guarantees require separate acceptance.
The bundled lifecycle process listens on container loopback. Publishing a host port alone does not expose it. Operate the external service using OpenSandbox's documented server configuration, including API-key authentication. When moving an existing lifecycle service, preserve its persistent metadata and stop the old writer before handing the store to the new service; do not copy a live SQLite database and run both writers.
Configure each replica
Create a protected, absolute-path environment file on each host, for example
/etc/astrabox/replica.env, readable only by its operator. Use it for both
Compose interpolation and container environment injection. Do not commit it or
publish the rendered docker compose config output: both contain credentials.
The shared portion contains these existing application settings:
ASTRABOX_DB_URL=postgresql+asyncpg://astrabox:URL_ENCODED_PASSWORD@db.internal:5432/astrabox
ASTRABOX_AGENT_PREWARM_REDIS_URL=redis://redis.internal:6379/0
ASTRABOX_SANDBOX_OPENAPI_BASE_URL=https://sandbox-control.example.com
ASTRABOX_SANDBOX_API_KEY_SECRET_NAME=opensandbox-api-key
OPENSANDBOX_API_KEY=REPLACE_WITH_SHARED_LIFECYCLE_API_KEY
ASTRABOX_SANDBOX_SERVER_KUBE_API_SERVER=https://kubernetes.internal:6443
ASTRABOX_SANDBOX_SERVER_KUBE_NAMESPACE=opensandbox
ASTRABOX_MCP_PROXY_BASE_URL=https://callbacks.example.com
ASTRABOX_ALLOWED_HOSTS=astrabox.example.com,callbacks.example.com,api-a.internal,api-b.internal
ASTRABOX_LITELLM_BASE_URL=https://llm.example.com
ASTRABOX_CHANNEL_GATEWAY_BASE_URL=https://channels.example.com
ASTRABOX_CHANNEL_GATEWAY_TOKEN=REPLACE_WITH_EXISTING_SHARED_GATEWAY_TOKEN
ASTRABOX_VAULT_MASTER_KEY=REPLACE_WITH_EXISTING_SHARED_VAULT_KEY
ASTRABOX_AUTH_SESSION_SECRET=REPLACE_WITH_EXISTING_SHARED_SESSION_KEY
ASTRABOX_TRANSCRIPT_SIGNING_KEY=REPLACE_WITH_EXPLICIT_SHARED_TRANSCRIPT_KEY
The lifecycle API key reference uses astrabox/secrets.py's secret provider,
not the product's encrypted Vault records. Its environment provider maps a name
to uppercase with hyphens replaced by underscores: opensandbox-api-key
therefore reads OPENSANDBOX_API_KEY. Supply the same value to each replica, as
shown above; this resolver currently reads only process environment. Sharing
the product database alone does not make that named secret available. The
container environment file carries this operator-chosen name without adding
an application-specific credential variable.
Every other setting the existing deployment uses also belongs in this file.
Preserve the existing LiteLLM settings, including ASTRABOX_LITELLM_API_KEY,
ASTRABOX_LITELLM_SERVER_BASE_URL and ASTRABOX_LITELLM_ADMIN_URL when the
deployment sets them. When ASTRABOX_LITELLM_API_KEY is empty, each replica's
entry point uses LITELLM_MASTER_KEY to confirm or create the sandbox model
key derived from ASTRABOX_AUTH_SESSION_SECRET, and the replica does not start
without it. Preserve the existing identity-provider settings, such as
ASTRABOX_WEB_IDENTITY and the registered OIDC settings. This overlay does not
configure an identity provider or install a second Casdoor instance.
The overlay selects the local secret-store provider backed by the shared
database. Its encryption key must be the existing deployment key, not a newly
generated key. The encoded value in /data/vault.key can populate
ASTRABOX_VAULT_MASTER_KEY without changing Vault encryption. Likewise,
/data/auth-session.key can populate ASTRABOX_AUTH_SESSION_SECRET without
changing the browser-session signing key.
Callback signing has a different boundary. When no explicit transcript key is configured, a file-backed Vault key and its environment representation use different callback-key derivations. Copying the encoded Vault value into the environment therefore does not preserve existing callback credentials. The explicit transcript setting accepts a text secret; it does not import an arbitrary binary derived key. Do not base64-encode a derived key and assume the result reproduces it.
An existing deployment without an explicit transcript key needs a coordinated
rotation before using this overlay: stop admitting work, drain active work,
retire prepared and resident sandbox runtimes carrying old callback credentials,
and configure one explicit transcript key across all replicas before preparing
replacement runtimes. Preserve the product database, SessionStore and workspace
storage. Accept the replacement runtimes against the new callback credentials
before reopening traffic. This overlay does not automate that rotation or
refresh tokens inside existing sandboxes. Do not copy /data wholesale: each
replica gets a separate local volume, not a database copy.
Add the host-specific Compose inputs to that same file:
ASTRABOX_REPLICA_ENV_FILE=/etc/astrabox/replica.env
ASTRABOX_SERVER_IMAGE=registry.example.com/astrabox/server@sha256:REPLACE_WITH_IMAGE_DIGEST
ASTRABOX_SERVER_BIND_IP=10.0.1.12
ASTRABOX_SERVER_HOST_PORT=8088
ASTRABOX_KUBECONFIG_HOST_PATH=/etc/astrabox/kubeconfig
ASTRABOX_STATE_VOLUME=astrabox-api-b-state
Use the same immutable server image and sandbox images on every host. Only the
bind address, local paths and local volume name vary between replicas. The
ASTRABOX_REPLICA_ENV_FILE input is a Compose file path, not an application
setting. Absolute paths avoid Compose's base-file-relative path resolution.
The overlay fixes the container listener at 0.0.0.0:8000; change only
ASTRABOX_SERVER_BIND_IP and ASTRABOX_SERVER_HOST_PORT for its host publication.
Start only the platform replica
From the repository root, on each host:
docker compose --env-file /etc/astrabox/replica.env \
-p astrabox-api-b \
-f containers/compose.yaml \
-f containers/compose.kubernetes.yaml \
-f containers/compose.distributed.yaml config --quiet
docker compose --env-file /etc/astrabox/replica.env \
-p astrabox-api-b \
-f containers/compose.yaml \
-f containers/compose.kubernetes.yaml \
-f containers/compose.distributed.yaml up -d --no-deps --no-build server
Use Docker Compose 2.24.4 or newer for the !override merge tag. The distributed
overlay must be last. Do not use scripts/compose.sh for this topology: that
wrapper prepares credentials for a local database. Do not enable the
local-data-only or docker-runtime-only profiles. The overlay removes local
service dependencies and mounts no Docker socket or local database secrets.
The entry point sees the external service URLs and does not start embedded
OpenSandbox, LiteLLM or channel-gateway processes.
Check /healthz and /readyz through each replica's private address, then
register healthy replicas with the ingress. Validate the deployment with
concurrent pool claims from both replicas, unique sandbox assignment,
same-session history and streaming through either replica, and cross-replica
stop/approval actions. Verify shared workspace persistence on different sandbox
nodes separately. These checks exercise shared-service connections that a
configuration render or a health check cannot verify.