Xamal is a Mix-first Elixir deployment tool for bare-metal Elixir releases over SSH. It is inspired by Kamal, but uses native releases, Caddy, and Elixir configuration instead of Docker, kamal-proxy, and YAML.
- Elixir 1.15+, OTP 26+
- Mix tasks (
mix xamal.*) are the public command surface - Elixir config via
config/xamal.exs - Destination overrides live under
config/xamal/<destination>.exs - SSH via Erlang
:sshstdlib - Tests:
mix test(ExUnit) - Quality checks:
mix ci
lib/mix/tasks/— public Mix task entrypoints, usuallyuse Xamal.MixTaskfor config-loading taskslib/xamal/deployment.ex— high-level deploy/redeploy/setup/rollback orchestrationlib/xamal/*_tasks.ex,prune.ex,audit.ex,details.ex,remove.ex,versions.ex— task implementations used by Mix taskslib/xamal/output.ex,hooks.ex,remote.ex,deploy_lock.ex,blue_green.ex,logs.ex,task_helpers.ex— runtime helpers for output, hooks, SSH execution, locking, blue-green boot, logs, and task concernslib/xamal/secrets/adapters/— secret-manager adapter implementations used byXamal.SecretTaskslib/xamal/commands/— pure functions returning command lists (["cmd", "arg1"]), composed withcombine/pipe/chainlib/xamal/configuration/— structs withnew/1constructors parsing Elixir config datalib/xamal/context.ex— explicit runtime context for config, host/role filters, verbosity, lock, and connection state
- Prefer Mix tasks over custom CLI dispatch.
- Do not add an escript entrypoint.
- Do not introduce
Xamal.CLI.*modules; command behavior belongs inXamal.*modules or Mix tasks. - Command builder modules return list-of-strings and never execute anything.
- Config structs are immutable and built from Elixir config.
- Prefer Mix aliases in host
mix.exsover custom command aliases in Xamal config. - Hooks run locally, not on remote servers.
- Run
mix cibefore considering a change complete.
- No AI attribution in commit messages — no "Co-Authored-By", no mentioning Claude, Copilot, ChatGPT, or any AI tool
- Keep messages short and descriptive
- Use imperative mood ("Add feature" not "Added feature")