
Package your Elixir applications as 100% self-contained executables. No Erlang/Elixir installation required on the target machine.
Features
- Self-contained binaries: Single executable with your app + ERTS embedded
- Smart ERTS provisioning: Auto-detects platform or force specific target
- Cross-compilation: Build for Linux (glibc/musl), macOS from any platform
- Zstandard compression: Optimal balance between size and speed
- Multiple execution modes: CLI, TUI, and Daemon support
- Relativized releases: Portable binaries with no absolute paths
Requirements
- Erlang/OTP 25+
- Elixir 1.15+
- Rust (cargo)
- Zstandard (zstd)
Banner Dependencies (Optional)
When show_banner: true (default), the build process displays a banner image in the terminal. To enable full image support across all terminal emulators, install these dependencies:
macOS
# For Sixel support (Alacritty, Ghostty, other terminals)
brew install libsixel
# Optional: for ASCII art fallback
# img2txt is included in libsixel
Linux
# Ubuntu/Debian
sudo apt install libsixel-tools
# Arch Linux
sudo pacman -S libsixel
# Fedora
sudo dnf install libsixel
Terminal Compatibility
| Terminal | Protocol | Requires |
|---|---|---|
| iTerm2 | Inline Images | Built-in |
| Ghostty | Kitty protocol | Built-in |
| WezTerm | Kitty protocol | Built-in |
| Alacritty | Kitty protocol | Built-in |
| Kitty | Kitty protocol | Built-in |
| VS Code | Sixel | libsixel |
| foot | Sixel | libsixel |
| Other terminals | ASCII fallback | None |
If no image support is detected, the banner falls back to text-only mode.
Quick Start
1. Add Dependency
# mix.exs
def deps do
[{:batamanta, "~> 1.0", runtime: false}]
end2. Configure (Auto-detect)
def project do
[
app: :my_app,
version: "0.1.0",
batamanta: [
erts_target: :auto, # Auto-detect host platform (RECOMMENDED)
execution_mode: :cli, # :cli | :tui | :daemon
compression: 3, # 1-19 (zstd level)
binary_name: "my_app", # Optional: custom binary name
show_banner: true # Optional: show build banner
]
]
endConfiguration Options
| Option | Type | Default | Description |
|---|---|---|---|
erts_target | atom | :auto | Target platform (see below) |
execution_mode | atom | :cli | :cli, :tui, or :daemon |
compression | integer | 3 | Zstd compression level (1-19) |
binary_name | string | app name | Custom binary name |
show_banner | boolean | true | Show build banner |
force_os | string | nil | Force OS: "linux", "macos", "windows" |
force_arch | string | nil | Force arch: "x86_64", "aarch64" |
force_libc | string | nil | Force libc: "gnu", "musl" (Linux only) |
3. Build
mix batamanta
This generates: my_app-0.1.0-x86_64-linux (or appropriate target)
ERTS Target System
Batamanta uses a unified ERTS target system for platform specification.
Supported Targets
| Target Atom | OS | Arch | Libc | Use Case |
|---|---|---|---|---|
:auto | - | - | - | Auto-detect host (default) |
:ubuntu_22_04_x86_64 | Linux | x86_64 | glibc | Debian, Ubuntu, Arch, CachyOS |
:ubuntu_22_04_arm64 | Linux | aarch64 | glibc | ARM servers, Raspberry Pi 4 |
:alpine_3_19_x86_64 | Linux | x86_64 | musl | Alpine Linux, containers |
:alpine_3_19_arm64 | Linux | aarch64 | musl | Alpine on ARM |
:macos_12_x86_64 | macOS | x86_64 | - | Intel Mac |
:macos_12_arm64 | macOS | aarch64 | - | Apple Silicon (M1/M2/M3) |
:windows_x86_64 | Windows | x86_64 | msvc | Coming soon |
Manual Override
Force a specific target regardless of host:
batamanta: [
erts_target: :alpine_3_19_x86_64, # Force Alpine musl
execution_mode: :cli
]Or use individual overrides:
batamanta: [
force_os: "linux",
force_arch: "x86_64",
force_libc: "musl"
]CLI Override
# Auto-detect (default)
mix batamanta
# Force specific target
mix batamanta --erts-target alpine_3_19_x86_64
# Force individual components
mix batamanta --force-os linux --force-arch aarch64 --force-libc musl
Execution Modes
| Mode | Description | Platform |
|---|---|---|
:cli | Standard CLI with inherited stdin/stdout/stderr | All |
:tui | Text UI with raw terminal mode, arrow key navigation | Unix only |
:daemon | Runs in background, no terminal I/O | Unix only |
Compatibility Matrix
Operating Systems
| OS | Architectures | Modes | Status |
|---|---|---|---|
| macOS 11+ | x86_64, aarch64 | CLI, TUI, Daemon | ✅ Full Support |
| Linux (glibc) | x86_64, aarch64 | CLI, TUI, Daemon | ✅ Full Support |
| Linux (musl) | x86_64, aarch64 | CLI, Daemon | ✅ Supported |
| Windows 10+ | x86_64 | CLI | 🔲 Coming Soon |
OTP / Elixir Versions
| OTP | Elixir | Status |
|---|---|---|
| 25 | 1.15 | ✅ Minimum Supported |
| 26 | 1.15, 1.16 | ✅ Supported |
| 27 | 1.15, 1.16, 1.17 | ✅ Supported |
| 28 | 1.16, 1.17, 1.18+ | ✅ Latest |
Restrictions
- ❌ Windows + TUI mode (requires Unix terminal)
- ❌ Windows + Daemon mode (requires Unix process management)
- ❌ OTP < 25 (missing required BEAM features)
- ❌ Elixir < 1.15 (missing required language features)
Troubleshooting: Linux musl/glibc
Problem: "libc mismatch detected" Warning
If you see a warning like:
⚠️ libc mismatch detected!
Expected: glibc (Debian/Ubuntu/Arch/Fedora)
Detected: musl libc (Alpine)This means your system's libc type doesn't match the expected ERTS target.
Solution 1: Let Batamanta auto-detect (recommended)
batamanta: [
erts_target: :auto # Auto-detects musl vs glibc
]Solution 2: Force specific target
batamanta: [
erts_target: :alpine_3_19_x86_64 # Force musl
]Solution 3: Use CLI override
mix batamanta --erts-target alpine_3_19_x86_64
Problem: ERTS download fails on Alpine/musl
If ERTS download fails with 404 error on musl systems:
Solution: Use custom ERTS
batamanta: [
custom_erts: "/path/to/musl-erts.tar.gz"
]You can build custom ERTS for musl:
# On Alpine Linux
apk add erlang-dev
cd /tmp
git clone https://github.com/erlang/otp.git
cd otp
./otp_build autoconf
./configure --prefix=/usr/local
make
make install
tar -czf musl-erts.tar.gz /usr/local/lib/erlang
Problem: Binary doesn't run on target system
If the binary works on build machine but fails on target:
Check libc compatibility:
# On build machine
ldd --version
# On target machine
ldd --version
# They should match (both glibc or both musl)
Solution: Build for oldest supported glibc version
# Use Ubuntu 22.04 target (most compatible glibc)
batamanta: [
erts_target: :ubuntu_22_04_x86_64
]Problem: Cross-compilation from macOS to Linux
Install Rust targets:
rustup target add x86_64-unknown-linux-gnu
rustup target add aarch64-unknown-linux-gnu
Build with explicit target:
mix batamanta --erts-target ubuntu_22_04_x86_64
How libc Detection Works
Batamanta uses multiple methods in order:
ldd --version- Most reliable, checks output for "musl" or "glibc"- Dynamic loader files - Checks
/lib/ld-musl-*.sovs/lib64/ld-linux-*.so /etc/os-release- ChecksID=alpine,ID=void, etc./proc/self/maps- Advanced, checks loaded libraries
Detection always falls back to glibc if uncertain (90%+ of systems use glibc).
ERTS Download Fallback
Batamanta attempts to download pre-compiled ERTS from Hex.pm builds. If the download fails:
⚠️ Could not download ERTS, using system ERTS instead.The build continues using the system ERTS (similar to Bakeware). This means:
- ✅ Build succeeds - Your application compiles
- ⚠️ Binary requires ERTS - Target machine needs compatible Erlang/Elixir
- ✅ Portable within same OS - Works on machines with same libc type
For production self-contained binaries:
- Ensure network access during build
- Use specific ERTS version:
batamanta: [otp_version: "26.2.5"] - Or provide custom ERTS:
batamanta: [custom_erts: "/path/to/erts.tar.gz"]
CLI Options
Override configuration via command line:
# Use auto-detection (default)
mix batamanta
# Force ERTS target
mix batamanta --erts-target alpine_3_19_x86_64
# Force individual components
mix batamanta --force-os linux --force-arch aarch64 --force-libc musl
# Adjust compression level
mix batamanta --compression 9
# Combine options
mix batamanta --erts-target ubuntu_22_04_arm64 --compression 5
Available CLI Flags
| Flag | Description |
|---|---|
--erts-target | Override ERTS target atom |
--force-os | Force OS: linux, macos, windows |
--force-arch | Force architecture: x86_64, aarch64 |
--force-libc | Force libc: gnu, musl (Linux only) |
--compression | Zstd compression level (1-19) |
For CLI Applications
Use Erlang's :init to read arguments:
defmodule MyApp do
use Application
@impl true
def start(_type, _args) do
args =
:init.get_plain_arguments()
|> Enum.map(&to_string/1)
|> Enum.reject(&(&1 == "--"))
case args do
["hello", name] -> IO.puts("Hello, #{name}!")
_ -> IO.puts("Usage: my_app hello <name>")
end
System.halt(0)
end
endDon't forget System.halt/1 when your CLI finishes!
How ERTS Provisioning Works
Auto-detection: Batamanta detects your host platform using:
:os.type()for OS identification:erlang.system_info(:system_architecture)for architectureldd --versionfor libc detection on Linux (glibc vs musl)
Download: Fetches pre-compiled ERTS from Hex.pm builds or from the Batamanta ERTS Repository
Cache: Stores in
~/.cache/batamanta/for reusePackage: Bundles your release + ERTS into a single compressed tarball
Compile: Rust dispenser embeds the payload and handles extraction at runtime
ERTS Repository
Batamanta uses a separate repository for pre-compiled ERTS binaries:
This repository hosts pre-compiled Erlang Run-Time System (ERTS) binaries for:
- macOS: aarch64 (Apple Silicon)
- Linux (glibc): x86_64 & aarch64
- Linux (musl): x86_64 & aarch64
The binaries are compiled from official Erlang/OTP sources and are subject to the Apache License 2.0 (see the repository for details).
Troubleshooting
Linux: "ERTS not found" or wrong ERTS downloaded
Batamanta auto-detects using ldd --version. If this fails:
# Check what ldd reports
ldd --version
# Force specific target
mix batamanta --erts-target ubuntu_22_04_x86_64
macOS: Binary doesn't run on older macOS versions
Ensure you're building with the correct deployment target:
batamanta: [
erts_target: :macos_12_x86_64 # or :macos_12_arm64
]Cross-compilation from macOS to Linux
Install Rust targets:
rustup target add x86_64-unknown-linux-gnu
rustup target add aarch64-unknown-linux-gnu
Then build:
mix batamanta --erts-target ubuntu_22_04_x86_64
Alpine/musl: "Library not found"
Ensure musl development headers are installed:
# Alpine
apk add musl-dev
# Or use the Alpine Docker image
docker run --rm -v $(pwd):/app -w /app elixir:1.18-alpine ...
Architecture
- Detect: Auto-detect or resolve manual target configuration
- Fetch: Download ERTS from Hex.pm builds
- Release: Compile your Elixir code with
mix release - Package: Bundle release + ERTS with Zstd compression
- Compile: Build Rust dispenser that embeds the payload
- Run: Dispenser extracts payload and spawns Erlang VM
Testing
Run the test matrix locally:
# Test across Linux distributions (requires Docker)
./docker_matrix.sh
# Run smoke tests manually
cd smoke_tests/test_cli && mix batamanta && ./test_cli-* arg1 arg2
cd smoke_tests/test_tui && mix batamanta && ./test_tui-*
cd smoke_tests/test_daemon && mix batamanta && ./test_daemon-* &
License
MIT