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

testcontainer_dockerfile

Hex PackageHex DocsCILicense: MITMade with Gleam

Build custom Docker images from a Dockerfile and use them as typed formulas with testcontainer. Pago handles the build; you get the container.

testcontainer is a container runner: it does not build images. This package fills the build-on-test gap by shelling out to docker build and wrapping the resulting image in a Formula.

let cfg =
  testcontainer_dockerfile.new("./Dockerfile")
  |> testcontainer_dockerfile.with_context(".")
  |> testcontainer_dockerfile.with_build_arg("BUILD_VERSION", "dev")
  |> testcontainer_dockerfile.with_expose_port(port.tcp(3000))
  |> testcontainer_dockerfile.with_env("LOG_LEVEL", "info")
  |> testcontainer_dockerfile.with_wait(wait.health_check())

let assert Ok(formula) = testcontainer_dockerfile.formula(cfg)
use container <- testcontainer.with_formula(formula)
// the image is built once, the container is created from it,
// and torn down on scope exit.

Why use it

Install

gleam add testcontainer_dockerfile@1

API

Build configuration

Container shape (applied AFTER build)

Mirror testcontainer/container builders, but on the built image:

Lifecycle

Errors (testcontainer_dockerfile/error)

pub type Error {
  DockerNotFound
  DockerfileNotFound(path: String)
  BuildFailed(path: String, reason: String)
}

BuildFailed.reason includes full docker build output (stdout + stderr merged), so you see exactly which RUN step broke.

How it works

docker build --quiet --no-cache -f <path> [--build-arg ...] <context> is invoked via erlang:open_port with {spawn_executable, ...} (no shell, no escape risk). With --quiet, Docker prints only the final image ID on stdout — that's what formula/1 returns.

After build:

  1. The image ID becomes a regular container.new(image_id) spec.
  2. Any with_expose_port, with_env, with_wait etc. accumulated on DockerfileConfig are applied to that spec via a chain of transform functions.
  3. The configured spec is wrapped in a Formula(DockerImage) that returns the image ID as the typed output.

When to use this vs alternatives

Workflow Tool
Pre-built public image (postgres:16, redis:7) testcontainer core, container.new
Custom image, pre-built in CI testcontainer core with image tag
Custom image, built inline at test time this package
docker-compose.yml orchestrating multiple services testcontainer_compose

Development

gleam run -m testcontainer_dockerfile_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_dockerfile

License

MIT.