Multi-container stacks with cardinal.toml
Declare a whole stack — web server, database, cache — in one file and start it with cardinal up.
Single containers are the easy 90%. When a workload grows — a web app, its
database, a cache — you want one file that describes the whole stack and two
commands to start and stop it. That file is cardinal.toml.
1. The file
[container.web]
image = "nginx:alpine"
ports = ["80:80", "443:80"]
volumes = ["./html:/usr/share/nginx/html"]
restart = "always"
[container.db]
image = "postgres:16"
ports = ["5432:5432"]
env = { POSTGRES_PASSWORD = "secret", POSTGRES_DB = "myapp" }
volumes = ["pg_data:/var/lib/postgresql/data"]
restart = "always"
Every [container.<name>] block maps to a cardinal run with the same
fields: image, ports, volumes, env, restart, memory, cpus,
network, cap_add, and more.
2. Up and down
cardinal up # create/start everything
cardinal up web # start only one service
cardinal down # stop/remove everything from this file
cardinal down -a # remove ALL containers, regardless of file
Containers on the same cardinal0 bridge reach each other directly — the
host gateway is 10.0.2.1, and containers get sequential IPs like
10.0.2.2, 10.0.2.3.
3. Compose YAML also works
If you already have a docker-compose.yaml, cardinal reads it as-is:
services:
db:
image: postgres:16
restart: always
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_DB: myapp
POSTGRES_USER: myapp
POSTGRES_PASSWORD: secret
healthcheck:
test: pg_isready -U myapp
interval: 5s
retries: 10
api:
image: myapp:v1
restart: always
ports:
- "8000:8000"
environment:
DB_HOST: db
DB_PASSWORD: secret
depends_on:
db:
condition: service_healthy
volumes:
pgdata:
cardinal up
depends_on is respected — including service_healthy, which waits for the
database's healthcheck to pass before starting the app.
4. A realistic stack: WordPress + MySQL
[container.db]
image = "mysql:8"
env = {
MYSQL_ROOT_PASSWORD = "rootpass",
MYSQL_DATABASE = "wordpress",
MYSQL_USER = "wpuser",
MYSQL_PASSWORD = "wppass",
}
volumes = ["wp_data:/var/lib/mysql"]
restart = "always"
[container.wordpress]
image = "wordpress:latest"
ports = ["8080:80"]
env = {
WORDPRESS_DB_HOST = "db",
WORDPRESS_DB_USER = "wpuser",
WORDPRESS_DB_PASSWORD = "wppass",
WORDPRESS_DB_NAME = "wordpress",
}
volumes = ["wp_uploads:/var/www/html/wp-content/uploads"]
depends_on = ["db"]
restart = "always"
Note WORDPRESS_DB_HOST = "db" — cardinal resolves the service name to the
container's bridge IP, so no hardcoded addresses.
5. Generating a stack
The Stack Builder does all of this visually: pick services from
the blueprint registry (or start from a preset), tweak names, ports, volumes
and env, and download the finished cardinal.toml:
cardinal up -f cardinal.toml