Provides functionality for creating and managing Elixir/Erlang container configurations — useful for testing distributed Erlang, remote deployment, clustering, and running Elixir releases inside containers.
Basic usage
{:ok, container} = TestcontainerEx.start_container(ElixirContainer.new())With a specific Elixir version
config =
ElixirContainer.new()
|> ElixirContainer.with_image("elixir:1.17-otp-27")
{:ok, container} = TestcontainerEx.start_container(config)With Erlang distribution enabled
config =
ElixirContainer.new()
|> ElixirContainer.with_cookie("my-secret-cookie")
|> ElixirContainer.with_node_name("app@192.168.1.100")
|> ElixirContainer.with_distribution_port(9100)
{:ok, container} = TestcontainerEx.start_container(config)Mount a local Mix project
config =
ElixirContainer.new()
|> ElixirContainer.with_project("/path/to/my_app")
{:ok, container} = TestcontainerEx.start_container(config)Connect from your host machine
After the container starts, the Erlang distribution port and EPMD port are exposed. From your local IEx session:
Node.connect(:"app@<host>")
Node.list() # => [:"app@<host>"]Copy a module into a running container and call it remotely
ElixirContainer.copy_module(container, MyModule, conn)
:rpc.call(:"app@<host>", MyModule, :hello, [])Run a release with remote shell
config =
ElixirContainer.new()
|> ElixirContainer.with_image("my_app:latest")
|> ElixirContainer.with_release("my_app")
|> ElixirContainer.with_cookie("release-cookie")
{:ok, container} = TestcontainerEx.start_container(config)
Summary
Functions
Connects from the host to the container's Erlang node.
Returns the full node name for connecting from the host.
Copies an Elixir module's source file into the container.
Returns the default distribution port (9100).
Returns the default image name without tag.
Returns the default EPMD port (4369).
Returns the mapped distribution port on the host machine.
Returns the mapped EPMD port on the host machine.
Creates a new ElixirContainer with default configuration.
Evaluates code inside the container's Erlang node via RPC.
Runs a Mix task inside the container.
Sets the check image regex used to validate the image name.
Sets a custom command to run inside the container.
Sets the Erlang distribution cookie.
Sets the fixed Erlang distribution port (default: 9100).
Sets environment variables inside the container.
Sets the container image (e.g. "elixir:1.17-otp-27" or a custom image
with Elixir/OTP pre-installed).
Sets the Erlang node name for distribution.
Bind-mounts a local Mix project directory into the container.
Configures the container to run a release.
Sets the arguments passed to the release binary (default: ["start"]).
Enables or disables container reuse.
Sets additional vm.args entries for the Erlang VM.
Sets the wait timeout in milliseconds.
Types
@type t() :: %TestcontainerEx.ElixirContainer{ check_image: term(), cmd: term(), cookie: term(), dist_port: term(), env_vars: term(), image: term(), node_name: term(), project_path: term(), release_args: term(), release_name: term(), reuse: term(), vm_args: term(), wait_timeout: term(), working_dir: term() }
Functions
@spec connect(TestcontainerEx.Container.Config.t(), Req.Request.t()) :: boolean()
Connects from the host to the container's Erlang node.
Requires the container to have been started with with_node_name/2
and with_cookie/2.
{:ok, container} = TestcontainerEx.start_container(
ElixirContainer.new()
|> ElixirContainer.with_cookie("secret")
|> ElixirContainer.with_node_name("app@192.168.1.100")
)
ElixirContainer.connect(container, conn)
# => true
@spec connection_node(TestcontainerEx.Container.Config.t()) :: String.t() | nil
Returns the full node name for connecting from the host.
Returns nil if no node_name is configured.
node_str = ElixirContainer.connection_node(container)
# => "app@localhost:9100"
Node.connect(String.to_atom(node_str))
@spec copy_module( TestcontainerEx.Container.Config.t(), module(), Req.Request.t(), String.t() | nil ) :: :ok | {:error, term()}
Copies an Elixir module's source file into the container.
The module is compiled inside the container and can then be called
via :rpc.call/4.
ElixirContainer.copy_module(container, MyApp.Helper, conn)
:rpc.call(:"app@localhost:9100", MyApp.Helper, :hello, [])
# => :worldThe source file is expected to be at lib/<module_path>.ex relative
to the project root, or at the given source_path.
@spec default_dist_port() :: 9100
Returns the default distribution port (9100).
@spec default_image() :: String.t()
Returns the default image name without tag.
@spec epmd_port() :: 4369
Returns the default EPMD port (4369).
@spec mapped_dist_port(TestcontainerEx.Container.Config.t()) :: integer() | nil
Returns the mapped distribution port on the host machine.
@spec mapped_epmd_port(TestcontainerEx.Container.Config.t()) :: integer() | nil
Returns the mapped EPMD port on the host machine.
@spec new() :: t()
Creates a new ElixirContainer with default configuration.
Defaults:
- Image:
elixir:latest - Wait timeout: 120s
- No distribution (cookie/node_name not set)
- No project mounted
@spec remote_eval(TestcontainerEx.Container.Config.t(), Req.Request.t(), String.t()) :: {:ok, term()} | {:error, term()}
Evaluates code inside the container's Erlang node via RPC.
ElixirContainer.remote_eval(container, conn, "IO.puts(:hello)")
# => "hello"
@spec run_mix(TestcontainerEx.Container.Config.t(), Req.Request.t(), String.t()) :: :ok | {:error, term()}
Runs a Mix task inside the container.
ElixirContainer.run_mix(container, conn, "test")
ElixirContainer.run_mix(container, conn, "ecto.migrate")
Sets the check image regex used to validate the image name.
Sets a custom command to run inside the container.
Overrides the default IEx or release command. Useful for running custom scripts or one-off commands.
config
|> ElixirContainer.with_cmd(["mix", "test"])
Sets the Erlang distribution cookie.
Required for node-to-node communication. Both nodes must use the same cookie.
config
|> ElixirContainer.with_cookie("my-secret-cookie")
@spec with_distribution_port(t(), pos_integer()) :: t()
Sets the fixed Erlang distribution port (default: 9100).
Erlang uses a random port by default, which is difficult with containers. Setting a fixed port makes it possible to expose it through Docker.
config
|> ElixirContainer.with_distribution_port(9100)
Sets environment variables inside the container.
These are merged with any distribution-related env vars set automatically.
config
|> ElixirContainer.with_env_vars(%{
"MIX_ENV" => "prod",
"DATABASE_URL" => "postgres://..."
})
Sets the container image (e.g. "elixir:1.17-otp-27" or a custom image
with Elixir/OTP pre-installed).
Sets the Erlang node name for distribution.
Use long names (--name) for cross-machine connections, short names
(--sname) for same-network connections.
config
|> ElixirContainer.with_node_name("app@192.168.1.100")When set, EPMD port 4369 and the distribution port are automatically exposed.
Bind-mounts a local Mix project directory into the container.
The project is mounted at /app inside the container. Useful for running
tests, starting a remote IEx session, or deploying code.
config
|> ElixirContainer.with_project("/path/to/my_app")
Configures the container to run a release.
When set, the container starts the release instead of an IEx session.
The release binary is expected to be at /app/bin/<release_name>.
config
|> ElixirContainer.with_release("my_app")
|> ElixirContainer.with_release_args(["start"])
Sets the arguments passed to the release binary (default: ["start"]).
config
|> ElixirContainer.with_release_args(["start_iex"])
Enables or disables container reuse.
Sets additional vm.args entries for the Erlang VM.
These are appended to the generated vm.args file inside the container. Useful for tuning distribution, heart, or other kernel settings.
config
|> ElixirContainer.with_vm_args([
"-kernel inet_dist_listen_min 9100",
"-kernel inet_dist_listen_max 9100"
])
@spec with_wait_timeout(t(), pos_integer()) :: t()
Sets the wait timeout in milliseconds.