If you love somebody, let them go, for if they return, they were always yours . If they don’t, they never were.
I’ve been scouting out what it will take to update Agile Web Development with Rails 7 for Rails 8, and chapter 17 (Deployment and Production) will need be rewritten to focus on Kamal. This naturally lead me to spend a few hours deploying my Showcase Rails app to Hetzner. Up to this point, I had been following the Kamal project closely, but never had actually used it.
Overall, I will say that if I didn’t have Fly.io available to me I would be inclined to use Kamal. Actually, I’ll go a bit further: I will continue to use it - I’ll maintain a server that serves primarily as a staging area but can also be commandeered at any time to be used a hot backup should the need arise. I still have my original mac mini, and it still runs the application, but not in a docker container, so the deployment and startup are quite different.
Setting the Stage
The goal that DHH has set for Kamal is to provide what amounts to an “on-prem” version of fly.io that can also deploy applications onto a $5/month VPS:
Neither Rails 8 nor Kamal 2 are out yet. But everything that DHH describes can be done with Rails 7.1 and Kamal 1. Presumably Rails 8 and Kamal 2 will make more things pre-configured for you.
Preparing the Machines
Note that Kamal is just the deploy related parts of flyctl; everything else is “an exercise left for the student”. That being said, people seem to be filling in the gaps with terraform:
– https://www.luizkowalski.net/production-grade-ish-deployment-on-hetzner-with-kamal/
Even those who aren’t familiar with terraform:
Daily Usage
Once set up, I found the experience to be very fly.io-like. Examples of equivalents to commands I reach for every day:
Fly | Kamal |
---|---|
fly deploy | kamal deploy |
fly logs | kamal app logs |
fly ssh console | kamal app exec -i –reuse bash |
fly console | kamal app exec -i bin/rails console |
Deploying is pretty much the same experience as with Fly.io. Type one command, watch output scroll by as your Dockerfile is being built, uploaded to a registry, and then pulled down, and then your ENTRYPOINT and CMD are executed on the new container. It is not as pretty (we do pty
and ANSI cursor right - kamal simply scrolls), but the end result is the same.
I’ll likely build shell aliases (or more likely, a small shell script) to make these commands easier to remember and to type.
Under the Covers
Kamal doesn’t transmogrify containers, it actually runs your application (and what it calls “accessories” like databases and redis) in containers.
It also runs Traefik in a separate container. This application was designed to be a load balancer; but Rails uses it to do what amounts to “blue/green” deployment of applications – even applications with volumes.
Volumes can be docker volumes or bind mounts to the underlying machine or VPS.
Notes for the Book
While the video demos all show you getting up and running in minutes, the reality is different. Considering that my book literally starts off with instructions on how to install Ruby and an introduction to the language itself, my readers are going to need a bit more handholding.
A proper description of what it would take to start from zero would be something along the lines of:
Obtain a VPS from a place like Hetzner or Digital Ocean. Note the IP address.
Obtain a DNS name from a place like GoDaddy or Namecheap. Associate it with the IP address from the previous step.
Install Docker. Unless you are on Linux, this likely means Docker Workstation, though I have heard good things about OrbStack. You need this even if you aren’t going to be building on your own machine.
Sign up with a container registry like DockerHub. Select your username in the top right corner and select My Account. Then go to Security on the left hand side. Click New Access Token. Provide a description (perhaps your application’s name). Leave the Access permissions alone. Click Generate. IMPORTANT: Save this token now as it cannot be retrieved later.
If you are running on a Mac with an Apple chip (M1, M2, M3), you probably want to get another machine running Linux on amd64 to run your builds on. Install docker on that machine too, and make sure that you can access that machine via ssh. Others claim to have success with Rosetta; I haven’t been so lucky.
- Many ISP connections are asymmetric with higher download speeds than upload. Your first deploy is going to be very slow as it will involve uploading an entire Docker image. A remote builder is generally a much better way to go.
Install Kamal and run
kamal init
. Edit the.env
andconfig/deploy.yml
files. There are plenty of blog posts that you can steal from. The thing that took the longest for me was getting https set up; the blog posts suggestedkamal traefik reboot
once I updated the configuration, buttraefik
looks for docker labels so if you added or modified these you need to redeploy your app too.- If you are running a single machine, Traefik can be configured to obtain an SSL certificate using LetsEncrypt.
- If you are running on multiple machines, you will want a load balancer, and it will take care of SSL. This generally is a paid feature.
IMPORTANT: make sure your
.env
file is listed in both your.dockerignore
and.gitignore
files.
Kamal’s documentation is still a work in progress. As an example, try to find information on how to configure volumes in the documentation. Volumes are confusing (which path comes first?) and deserve coverage.
The community isn’t quite there yet. When I get an error message, I am used to being able to google that message with the product name to find solutions. I tried that with several error messages along the way, and never found what I needed. That being said, they have a Discord community where you can get answers if you are really stuck.
Conclusion
As the saying goes, a rising tide lifts all boats. We’ve proudly been a part of this journey from the start:
But this is mere words. The proof is real: it is a demonstrable fact that Fly.io and Kamal can use the same Dockerfile, and that the configuration files
(fly.toml
and config/deploy.yml
)
can sit side-by-side in the same repository. This means that you can migrate in either direction merely by running kamal init
or fly launch
. And you can even run both simultaneously, as I am now doing.
The future for Dockerfile based deployments is bright.