Xamal is an Elixir port of Kamal — Basecamp's tool for deploying web apps anywhere. It uses Mix tasks, Elixir configuration (config/xamal.exs), native releases, and Caddy instead of Docker containers and kamal-proxy.
If you're familiar with Kamal, you should feel right at home. The operational model, hook system, secrets management, and destination-based multi-environment workflow carry over.
- Elixir releases instead of Docker containers — built with
mix release, distributed as tarballs - Caddy instead of kamal-proxy — automatic TLS via Let's Encrypt, zero-downtime blue-green deploys via port switching
- Erlang SSH instead of shelling out to
ssh— connection pooling via GenServer - Mix tasks — deploy from the same toolchain that builds your release
Docker-specific configuration (image, registry, Dockerfile, build args, etc.) is intentionally omitted since releases replace containers entirely.
Add Xamal as a Mix dependency in the application you deploy:
# mix.exs
defp deps do
[
{:xamal, github: "dmkenney/xamal", only: [:dev, :test], runtime: false}
]
endThen fetch dependencies:
mix deps.get# Generate config stubs, sample hooks, release config, and helper aliases
mix xamal.init
# Edit config/xamal.exs and .xamal/secrets, then:
mix xamal.setupXamal reads Elixir config from config/xamal.exs:
import Config
config :xamal,
service: "my-app",
servers: [
web: ["192.168.0.1", "192.168.0.2"],
worker: [
hosts: ["192.168.0.3"],
cmd: ~s(bin/my_app eval "Worker.start()")
]
],
ssh: [
user: "deploy"
],
caddy: [
host: "app.example.com",
app_port: 4000
],
env: [
clear: [
PHX_HOST: "app.example.com"
],
secret: ["SECRET_KEY_BASE"]
],
release: [
name: "my_app",
mix_env: "prod"
],
health_check: [
path: "/health"
]Important: The release.name must match a named release in your mix.exs. Xamal runs mix release <name>, which requires an explicit release definition:
# mix.exs
def project do
[
...
releases: [
my_app: [
version: {:from_app, :my_app}
]
]
]
endWithout this, mix release my_app will fail with Unknown release :my_app.
Because this is Elixir config, normal Elixir expressions such as System.get_env/1 are available. Use regular Mix aliases in your application's mix.exs for command shortcuts.
Run mix xamal.docs <topic> for detailed reference on any config section.
Run mix help | grep xamal to list every available task.
mix xamal.setup # Bootstrap servers and deploy
mix xamal.deploy # Build, distribute, and boot
mix xamal.redeploy # Deploy without bootstrapping
mix xamal.rollback VERSION # Roll back to a previous version
mix xamal.remove # Remove remote release and proxy resources
mix xamal.app.boot # Zero-downtime restart
mix xamal.app.exec CMD # Run a command in the release context
mix xamal.app.logs -f # Tail application logs
mix xamal.app.maintenance # Enable maintenance mode (503)
mix xamal.app.live # Disable maintenance mode
mix xamal.app.stop # Stop application services
mix xamal.build.deliver # Build and upload release
mix xamal.build.push # Build release tarball
mix xamal.build.pull # Upload release tarball
mix xamal.build.details # Print build configuration
mix xamal.server.bootstrap # Bootstrap target servers
mix xamal.server.exec CMD # Run a shell command on servers
mix xamal.lock.status # Check deploy lock
mix xamal.lock.acquire # Acquire deploy lock
mix xamal.lock.release # Release deploy lock
mix xamal.config # Show merged configuration
mix xamal.docs hooks # Show hook documentation
mix xamal.secrets.print # Show secrets (redacted)
mix xamal.secrets.extract KEY # Print one secret value
mix xamal.secrets.fetch ADAPTER [OPTIONS]
Shell scripts in .xamal/hooks/ that run locally at lifecycle points:
| Hook | When |
|---|---|
pre-build |
Before building the release |
post-build |
After building the release |
pre-deploy |
Before deploying |
post-deploy |
After deploying |
pre-app-boot |
Before booting the app |
post-app-boot |
After booting the app |
pre-caddy-reload |
Before Caddy config reload |
post-caddy-reload |
After Caddy config reload |
Hooks receive environment variables like XAMAL_SERVICE, XAMAL_VERSION, XAMAL_HOSTS, XAMAL_PERFORMER, etc. Run mix xamal.docs hooks for the full list.
Multi-environment deploys work the same as Kamal:
mix xamal.deploy -d staging
mix xamal.deploy -d productionWith override files like config/xamal/staging.exs and secrets in .xamal/secrets.staging.
MIT