testcontainer_compose
Spin up docker-compose.yml stacks from your Gleam tests.
Pago hands you typed services; the lifecycle (up + body + down) runs automatically.
Companion to testcontainer:
use it when you have an existing compose file and want it as the source
of truth at test time, with typed access to parsed services.
let cfg =
testcontainer_compose.from_file("./docker-compose.yml")
|> testcontainer_compose.with_project_name("test_42")
|> testcontainer_compose.with_env_override("PG_PASS", "secret")
use stack <- testcontainer_compose.with_compose(cfg)
// Inspect typed service info
case testcontainer_compose.service_by_name(stack, "postgres") {
option.Some(svc) -> {
let _image = testcontainer_compose.service_image(svc)
let _ports = testcontainer_compose.service_ports(svc)
Ok(Nil)
}
option.None -> Ok(Nil)
}
with_compose/2 runs docker compose up -d --wait, calls your body,
then always tears the stack down with docker compose down --volumes --remove-orphans, even if the body returns an error. If the body succeeds
but teardown fails, the teardown error is surfaced so leaked stacks are never
silent.
Why use it
- 🐚 Native multi-container: uses Docker’s own dependency
resolution and health-wait
- 🔒 Type-safe services: parsed into
Service records with
name, image, ports
- 🩺 Reliable YAML handling: delegates parsing to
docker compose config --format json, so anchors, includes and
extends: all work
- ⏱ Hard-cleanup guarantee:
down --volumes --remove-orphans runs
even on body failure
- 🔐 Secret-safe overrides:
with_env_override wraps values in
cowl.Secret
Install
gleam add testcontainer_compose@1
API
Builder
from_file(path) → ComposeConfig
with_project_name(cfg, name) → unique project per test run
with_env_override(cfg, key, value) → values wrapped in
cowl.Secret (no leak via string.inspect)
Lifecycle
with_compose(cfg, body) — up + body + down (always). Teardown failure
surfaced when body succeeds.
formula(cfg) — returns a StandaloneFormula(ComposeServices, Error).
Pass it to testcontainer.with_standalone_formula/2 to get the same up +
body + down guarantee with a typed output:
let assert Ok(f) =
testcontainer_compose.from_file("./docker-compose.yml")
|> testcontainer_compose.with_project_name("test_42")
|> testcontainer_compose.formula()
use stack <- testcontainer.with_standalone_formula(f)
Service inspection
services(stack) → List(Service)
service_by_name(stack, name) → Option(Service)
service_name(svc) / service_image(svc) / service_ports(svc)
Parser (testable without Docker)
parse_services_json(path, json_str): feed it the output of
docker compose config --format json and validate parsing.
Errors (testcontainer_compose/error)
pub type Error {
ComposeFileNotFound(path: String)
InvalidYaml(path: String, reason: String)
ComposeFailed(path: String, reason: String)
}
ComposeFailed.reason carries the exit-code prefix (e.g.
"docker daemon error: ...") plus full stderr/stdout, so diagnostics
never get silently swallowed.
How it works
testcontainer_compose shells out to the Docker CLI for two reasons:
docker compose config --format json lets Docker handle YAML
parsing, anchors, env-var interpolation, includes, and extends:
inheritance. More reliable than re-implementing the spec.
docker compose up/down reuses Docker’s own dependency resolution
and health-wait logic.
The Erlang FFI uses erlang:open_port with a 60-second timeout and
explicit exit-code mapping (125 = daemon error, 126 = no permissions,
127 = not found).
When to use this vs alternatives
| Scenario | Tool |
| Pre-built public image, single container | testcontainer core, container.new |
| Multi-service Gleam-defined stack | testcontainer.with_network + multiple with_container |
Existing docker-compose.yml, source of truth | this package |
| Compose file rarely changes | Formula Builder → codegen native |
| Custom image built inline | testcontainer_dockerfile |
Development
gleam run -m testcontainer_compose_dev # Dev runner (needs Docker)
gleam test # Unit tests (no Docker)
TESTCONTAINERS_INTEGRATION=true gleam test # Integration tests (Docker required)
See also
Full API docs: https://hexdocs.pm/testcontainer_compose
License
MIT.