Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/kamal: Deploy web apps anywhere. (Basecamp’s Docker deployment tool), but replaces Docker containers with Elixir releases and kamal-proxy with Caddy.
If you’ve used Kamal, the workflow is the same - same YAML config structure, same CLI commands, same hook system and secrets management. The key differences:
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 - uses the :ssh stdlib with connection pooling
Escript CLI - single binary, no Ruby or Docker required on the deploying machine
Or build from source with mix escript.build. Requires Erlang/OTP 26+ on the machine running xamal.
Quick start
xamal init # generates config/deploy.yml and sample hooks
xamal setup # bootstraps servers and deploys
xamal deploy # subsequent deploys
xamal rollback # roll back to previous version
I’m also curious about this. IIRC in the Kamal 2 conference talk they explain why they replaced Traeffic with their own proxy to use an imperative model (instead of declarative – Kamal being imperative itself), fixing some bugs and moving part of the deploy logic to the tailor-made proxy.
First it just seemed like an extra unnecessary layer - which it is, but admittedly does have some nice benefits probably why it was on my todo list for so long.
One of my applications needs ffmpeg which roughly tripled the Docker image size. Not the end of the world, but it just bothered me a bit. I did try to install ffmpeg on the underlying OS and shell out from the application running inside Docker to it (so it wasn’t included in the image), but couldn’t get it to work.
So mostly just an itch I had for a while to simplify things since we really don’t need Docker with Elixir.
I still have to test deploying multiple apps to a single server (all of my apps have 1 per server) - so depending how that goes maybe the Kamal proxy will make more sense (or not!).
Just like they made a custom proxy that understands their needs (Rails), I can see a world where a custom proxy (or an off the shelve proxy configured appropriately) that understands the BEAM, Phoenix and LiveView could be beneficial.
Cool project but please, this has nothing to do with bare metal. To deploy something on bare metal would mean for it to run directly on the CPU which is definitely not the case here.
What’s the other definition than software that’s close to the hardware, like an OS itself, a bootloader, a unikernel? Not running in a container, but running on a high-level OS, maybe even in a virtual server is not software running on bare metal.
You can use this to deploy to a bare metal server. IE no hypervisor layer, no shared CPU, etc. That is “bare metal”. Could be a server in your basement, or a Hetzner Dedicated instance.
Agreed that running this in a VPS (which works fine as well) would not be considered bare metal.
I agree, the application runs at too high level. Recently, I built an app in Elixir and found that containers bring more trouble than benefit. Elixir is stable enough to run standalone.
Docker would, in addition, allow you to “declare” and provision additional system-level dependencies that are not tracked in mix.exs and not packaged in a BEAM release. An example was already given earlier, ffmpeg.
Docker also allows you to upgrade OS packages more frequently without requiring a server reboot. And you can test those changes before moving forward with a zero-downtime deploy. Upgrades on the server directly either would go untested, or requires you to maintain a copy of the production environment that you can perform the test upgrade (this tends to be much harder to maintain and do correctly than Docker images, in my experience).
Do we necessarily need Docker? No, I don’t think so. But if you want to express those other dependencies, then you need other tooling. Old-school config management system like Ansible, building OS images like with Packer, or a combination of those and/or other tools.
If your project only ever depends on the BEAM release and you guarantee your BEAM matches the OS well (e.g. compatible libc, etc) then going without the extra abstractions could be long term viable.
A long time ago, I worked with Golang (web services). I found that Kubernetes (K8s) is perfect for backend development—handling deployment, security, and being simply easy to use for developers.
Recently, when I built a distributed system using Elixir, I spent a lot of time solving problems and troubleshooting. This was quite challenging because K8s uses dynamic hostnames and IPs, which presents a difficulty for those who want to run an Elixir cluster on K8s and use NIFs for low-level tasks.
Yeah you are 100% spot on. I’m using Ansible currently. And funny enough for one of my projects I needed to use a Docker container to actually build the release to ensure compatible libraries with the VPS.
Certainly trade-offs no matter which way you slice it.
I don’t think there will be accessory support within Xamal.
If you are using this you are probably already using something to manage your server (IE Ansible, Chef, Puppet, etc). And it would be handled there. That is how I do it.
I think @Dmk is using the term correctly - it commonly refers to deploying to a dedicated or stand-alone server. I’m also familiar with it’s use in reference to unikernels and library operating systems, where it’s more as you describe, but I think both are valid.
The problem with using a reverse proxy like caddy, is that you’re going to reduce the concurrency options as you’re now terminating the connection before forwarding it, running into socket pair limitations and a CPU hit for Caddy to be running. Maybe if Caddy supported DSR the socket limitation would be avoidable, but putting the BEAM VM behind a Golang router doesn’t feel great to me.
If the goal is to be on bare metal, why not use eBPF with a user-land configurable API for deploys that changes the traffic routing? This avoids the socket limitations while providing superior performance and retaining imperative configuration change options during deploys. It seems a little young, but there’s Honey Potion that could make that pretty simple, and nowadays it feels like the big players in scalability and routing are all using eBPF based solutions.