Skip to content

dannote/xamal

 
 

Repository files navigation

Xamal

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.

What's different from Kamal

  • 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.

Install

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}
  ]
end

Then fetch dependencies:

mix deps.get

Quick start

# Generate config stubs, sample hooks, release config, and helper aliases
mix xamal.init

# Edit config/xamal.exs and .xamal/secrets, then:
mix xamal.setup

Configuration

Xamal 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}
      ]
    ]
  ]
end

Without 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.

Commands

Run mix help | grep xamal to list every available task.

Deploy

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

App

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

Build, server, and lock

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

Config, docs, and secrets

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]

Hooks

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.

Destinations

Multi-environment deploys work the same as Kamal:

mix xamal.deploy -d staging
mix xamal.deploy -d production

With override files like config/xamal/staging.exs and secrets in .xamal/secrets.staging.

License

MIT

About

Deploy Elixir apps to bare metal servers. Like Kamal, but with native releases + Caddy instead of Docker.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • Elixir 99.5%
  • Shell 0.5%