Skip to main content

Docker Compose

Generate a docker-compose.yml from typed service definitions, reading environment variables for deployment-specific overrides.

Source

examples/docker-compose/types.rank
pub ServiceDef = Object {
image: string,
ports?: [string],
environment: Object { ...: string },
depends_on?: [string],
restart: string,
}

pub NetworkDef = Object {
driver: string,
}

pub VolumeDef = Object {
driver: string,
}

pub Compose = Object {
version: string,
services: Object { ...: ServiceDef },
networks?: Object { ...: NetworkDef },
volumes?: Object { ...: VolumeDef },
}
examples/docker-compose/main.rank
/// Generates a Docker Compose file from typed service definitions.
/// Shows Env inputs for environment-specific overrides, the `with` patch
/// pattern for dev vs prod, and Emit::manifest to emit a YAML artifact.

use std::Env
use std::Emit
use std::Path
use root::types::{ Compose, ServiceDef }

SysEnv = Object {
COMPOSE_ENV?: string,
DB_PASSWORD?: string,
...: string,
}

env = Env<SysEnv> {}

compose_env = env.COMPOSE_ENV ?? `dev`
db_password = env.DB_PASSWORD ?? `changeme`

is_prod = compose_env == `prod`

base_api: ServiceDef = {
image: `my-org/api:latest`,
ports: [`8080:8080`],
environment: {
NODE_ENV: compose_env,
DB_HOST: `db`,
DB_PASSWORD: db_password,
},
depends_on: [`db`],
restart: `on-failure`,
}

api: ServiceDef = is_prod
? base_api with { restart: `always` }
: base_api with { environment: base_api.environment with { DEBUG: `true` } }

db: ServiceDef = {
image: `postgres:16`,
environment: {
POSTGRES_PASSWORD: db_password,
POSTGRES_DB: `app`,
},
restart: `always`,
}

compose: Compose = {
version: `3.9`,
services: { api, db },
networks: {
default: { driver: `bridge` },
},
volumes: {
pgdata: { driver: `local` },
},
}

pub main = || Emit::manifest({
entries: [
{
path: Path::join([`docker-compose.yml`]),
format: `yaml`,
value: compose,
},
]
})

Output

# docker-compose.yml (with COMPOSE_ENV=dev)
version: "3.9"
services:
api:
image: my-org/api:latest
ports:
- "8080:8080"
environment:
NODE_ENV: dev
DB_HOST: db
DB_PASSWORD: changeme
DEBUG: "true"
depends_on:
- db
restart: on-failure
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: changeme
POSTGRES_DB: app
restart: always
networks:
default:
driver: bridge
volumes:
pgdata:
driver: local

Key concepts

  • std::Env — reads environment variables at compile time into a typed schema. Optional fields use ?? for defaults.
  • Multi-file modulestypes.rank exports shared type definitions; this example uses the canonical absolute form root::types. In a normal project source tree, a nearby sibling import can also use super::types.
  • with patchbase_api with { restart: \always` }merges without mutatingbase_api`.
  • Emit::manifest(...) — emits one or more named artifacts. The file path and format are part of the descriptor.

Run it

COMPOSE_ENV=prod DB_PASSWORD=secret rank examples/docker-compose