testcontainer_compose
Spin up
docker-compose.ymlstacks 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
Servicerecords withname,image,ports - 🩺 Reliable YAML handling: delegates parsing to
docker compose config --format json, so anchors, includes andextends:all work - ⏱ Hard-cleanup guarantee:
down --volumes --remove-orphansruns even on body failure - 🔐 Secret-safe overrides:
with_env_overridewraps values incowl.Secret
Install
gleam add testcontainer_compose@1
API
Builder
from_file(path)→ComposeConfigwith_project_name(cfg, name)→ unique project per test runwith_env_override(cfg, key, value)→ values wrapped incowl.Secret(no leak viastring.inspect)
Lifecycle
with_compose(cfg, body)— up + body + down (always). Teardown failure surfaced when body succeeds.formula(cfg)— returns aStandaloneFormula(ComposeServices, Error). Pass it totestcontainer.with_standalone_formula/2to 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 ofdocker compose config --format jsonand 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 jsonlets Docker handle YAML parsing, anchors, env-var interpolation, includes, andextends:inheritance. More reliable than re-implementing the spec.docker compose up/downreuses 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
testcontainer: core container runnertestcontainer_formulas: pre-built formulas for Postgres, Redis, MySQL, RabbitMQ, Mongotestcontainer_dockerfile: build custom images from a Dockerfile at test timetestcontainer_formulas_builder: visual builder + codegen, also accepts Dockerfile / compose uploads
Full API docs: https://hexdocs.pm/testcontainer_compose
License
MIT.