Multi-Service Apps (compose-style)
One app can run several containers — e.g. a web frontend plus an internal cache or database. It’s the platform’s take on a Compose stack: 1 app = N services, but it still counts as one unit against your quota.
Rules
- Provide a
servicesarray (at least one). - Exactly one service must be
public: true— it gets the app’s HTTPS domain. - Internal services (
public:false) are not exposed to the internet. They live on a private per-app network and are reachable by other services by theirname(e.g.http://cache:6379). - Each service can use its own
image, or build from a shared top-levelrepo. repo/branch/repoTokenstay at the app level (one repo for the stack);port/env/cpu/memory/ secrets are per service.
Example — public web + internal redis
curl -s -X POST $API/deploy \
-H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
-d '{
"name": "shop",
"services": [
{ "name": "web", "image": "acme/storefront:1.0", "port": 8080, "public": true,
"env": { "REDIS_URL": "redis://cache:6379" } },
{ "name": "cache", "image": "redis:7-alpine", "port": 6379, "public": false }
]
}'
https://shop.cynchro.cloud→ the web container.webreachescacheover the private network atredis://cache:6379.cacheis invisible from the internet.
Service fields
| Field | Required | Notes |
|---|---|---|
name | ✅ | DNS-safe; also the internal hostname. |
image | ✅* | Prebuilt image, OR omit to build from the app-level repo. |
dockerfile / context | When building from repo (relative paths). | |
port | Default 80. Port the service listens on. | |
public | true for the one internet-facing service. | |
domain | Override the public service’s hostname. | |
cpu / memory | Per-service limits. | |
env | Per-service env (encrypted at rest). | |
envFrom / secretFiles | Per-service Secrets references. |
* Each service needs either its own image or a top-level repo to build from.
Building all services from one repo
-d '{
"name": "stack",
"repo": "https://github.com/acme/monorepo",
"services": [
{ "name":"api", "dockerfile":"api/Dockerfile", "context":"api", "port":3000, "public":true },
{ "name":"worker", "dockerfile":"worker/Dockerfile", "context":"worker", "port":0, "public":false }
]
}'
The repo is cloned once and each service is built from its own Dockerfile.
Lifecycle
Restart / stop / delete act on the whole app (all its containers). Deleting also tears down the private network. Self-healing watches every container — if any dies, the app is redeployed.
→ Back to basics: Deploy from Git · Inject config: Secrets