DockerWrapper

CIHex.pmHex DocsLicense

A typed Elixir wrapper for the Docker CLI. Struct-based commands, pipeline composition, and BEAM-native extensions for supervised containers and streaming output.

Elixir port of docker-wrapper (Rust).

Features

Quick start

# Run a container
import Docker.Commands.Run

{:ok, container_id} =
  "redis:7-alpine"
  |> new()
  |> name("my-redis")
  |> port(6379, 6379)
  |> detach()
  |> Docker.run()

# List running containers
{:ok, containers} = Docker.ps()

# Exec into a container
{:ok, output} =
  Docker.Commands.Exec.new("my-redis", ["redis-cli", "ping"])
  |> Docker.exec_cmd()

# Stop and remove
{:ok, _} = Docker.stop("my-redis")
{:ok, _} = Docker.rm("my-redis")

Supervised containers

Run containers under OTP supervision with automatic health checks and cleanup on termination:

run_cmd =
  Docker.Commands.Run.new("redis:7-alpine")
  |> Docker.Commands.Run.name("supervised-redis")
  |> Docker.Commands.Run.port(6379, 6379)

{:ok, pid} = Docker.Supervised.start_link(run_cmd,
  health_interval: 5_000,
  rm_on_terminate: true
)

Docker.Supervised.container_id(pid)  #=> "abc123..."
Docker.Supervised.healthy?(pid)      #=> :healthy

# Or under a supervisor
children = [
  {Docker.Supervised, {run_cmd, name: MyApp.Redis}}
]
Supervisor.start_link(children, strategy: :one_for_one)

Streaming

Stream output from long-running commands via Port:

{:ok, stream} =
  Docker.Commands.Logs.new("my-container")
  |> Docker.Commands.Logs.follow()
  |> Docker.Stream.start_link(subscriber: self())

receive do
  {:docker_stream, ^stream, {:stdout, line}} -> IO.puts(line)
  {:docker_stream, ^stream, {:exit, 0}} -> :done
end

Compose

import Docker.Commands.Compose.Up

new()
|> file("docker-compose.yml")
|> detach()
|> wait()
|> service("redis")
|> service("web")
|> Docker.compose_up()

Images and builds

# Pull an image
{:ok, _} = Docker.pull("nginx:alpine")

# Build with buildx for multiple platforms
import Docker.Commands.Builder.Build

"."
|> new()
|> tag("myapp:latest")
|> platform("linux/amd64")
|> platform("linux/arm64")
|> push()
|> Docker.generic()  # or use the builder facade

Networks and volumes

# Create a network
{:ok, _} = Docker.network_create("my-net")

# Create a volume
{:ok, _} = Docker.volume_create("my-data")

# Run a container on the network with the volume
"postgres:16"
|> Docker.Commands.Run.new()
|> Docker.Commands.Run.name("my-pg")
|> Docker.Commands.Run.network("my-net")
|> Docker.Commands.Run.volume("my-data", "/var/lib/postgresql/data")
|> Docker.Commands.Run.env("POSTGRES_PASSWORD", "secret")
|> Docker.Commands.Run.detach()
|> Docker.run()

Configuration

The Docker binary, working directory, timeout, and environment can be configured per-call:

config = Docker.Config.new(
  binary: "/usr/local/bin/podman",
  working_dir: "/my/project",
  timeout: 60_000,
  env: [{"DOCKER_HOST", "tcp://remote:2375"}]
)

Docker.ps(config: config)

Or set DOCKER_PATH to override the binary globally.

Installation

Add docker_wrapper to your dependencies in mix.exs:

def deps do
  [
    {:docker_wrapper, "~> 0.1"}
  ]
end

License

MIT