This guide documents breaking changes and the steps required to upgrade between major Xamal versions.
0.2.0 → 0.3.0
Version 0.3.0 changes how Xamal is installed, how it is invoked, and how it is
configured. There is no compatibility shim — the old escript binary and
config/deploy.yml are gone — so every project needs to migrate.
The deploy model itself is unchanged: the same servers, hooks, secrets, and destination workflow carry over. Only the install method, command surface, and config format change.
1. Replace the escript install with a Mix dependency
Previously Xamal was installed as a standalone escript (via install.sh,
mix escript.install, or a copied binary on your $PATH).
Remove the old binary:
rm -f ~/.local/bin/xamal # or wherever you copied it
mix escript.uninstall xamal # if you used mix escript.install
Then add Xamal as a dev/test dependency in the application you deploy:
# mix.exs
defp deps do
[
{:xamal, "~> 0.3", only: [:dev, :test], runtime: false}
]
endmix deps.get
2. Use mix xamal.<task> instead of the xamal binary
The command surface moved from the escript to Mix tasks. Subcommands are now dotted task names:
| 0.2.0 (escript) | 0.3.0 (Mix task) |
|---|---|
xamal init | mix xamal.init |
xamal setup | mix xamal.setup |
xamal deploy | mix xamal.deploy |
xamal redeploy | mix xamal.redeploy |
xamal rollback VER | mix xamal.rollback VER |
xamal app boot | mix xamal.app.boot |
xamal app exec CMD | mix xamal.app.exec CMD |
xamal app logs -f | mix xamal.app.logs -f |
xamal app maintenance | mix xamal.app.maintenance |
xamal app live | mix xamal.app.live |
xamal lock status | mix xamal.lock.status |
xamal secrets print | mix xamal.secrets.print |
xamal config | mix xamal.config |
xamal docs hooks | mix xamal.docs hooks |
Flags carry over unchanged, including the -d <destination> flag. Run
mix help | grep xamal to list every available task.
Update any deploy scripts, CI jobs, Makefiles, or Mix aliases that called the
xamal binary.
3. Convert config/deploy.yml to config/xamal.exs
Configuration is now Elixir config instead of YAML. Translate the structure
key-for-key: YAML maps become keyword lists, and the top-level keys move under
config :xamal.
Before — config/deploy.yml:
service: my-app
servers:
web:
- 192.168.0.1
- 192.168.0.2
worker:
hosts:
- 192.168.0.3
cmd: 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: /healthAfter — 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"
]If you would rather start from a fresh stub and copy your values over, delete
config/deploy.yml and run mix xamal.init, which generates config/xamal.exs
(along with .xamal/secrets, sample hooks, and .gitignore entries) without
overwriting files that already exist.
4. Replace EEx templating with Elixir expressions
config/deploy.yml supported EEx interpolation. Because config/xamal.exs is
plain Elixir, use normal Elixir expressions instead:
| 0.2.0 (EEx in YAML) | 0.3.0 (Elixir in .exs) |
|---|---|
<%= System.get_env("KEY") %> | System.get_env("KEY") |
<%= env["KEY"] %> | System.get_env("KEY") |
For example:
config :xamal,
caddy: [
host: System.get_env("APP_HOST") || "app.example.com",
app_port: String.to_integer(System.get_env("APP_PORT") || "4000")
]5. Rename destination override files
Destination overrides moved from config/deploy.<destination>.yml to Elixir
files. Both of these locations are recognized:
config/xamal/<destination>.exs(preferred), orconfig/xamal.<destination>.exs
Each override file is a standalone config/xamal.exs-style file
(import Config + config :xamal, ...) that is deep-merged onto the base
config. Convert each YAML override the same way as the base file:
# Before
config/deploy.staging.yml
config/deploy.production.yml
# After
config/xamal/staging.exs
config/xamal/production.exs
Destination secrets (.xamal/secrets.staging, etc.) are unchanged.
6. Verify
mix xamal.config -d staging # confirm the merged config looks right
mix xamal.deploy -d staging