Pago, the paguro mascot, carrying a Docker container on his shell

testcontainer_compose

Hex Package Hex Docs CI License: MIT Made with Gleam

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

Install

gleam add testcontainer_compose@1

API

Builder

Lifecycle

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

Parser (testable without Docker)

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:

  1. 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.
  2. 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

ScenarioTool
Pre-built public image, single containertestcontainer core, container.new
Multi-service Gleam-defined stacktestcontainer.with_network + multiple with_container
Existing docker-compose.yml, source of truththis package
Compose file rarely changesFormula Builder → codegen native
Custom image built inlinetestcontainer_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.

Search Document