Fly.io ❤️ Kamal

Kamal is named after the ancient Arab navigational tool used by sailors to keep course by determining their latitude via the Pole Star.  This picture shows a sailor using Kamal to spot Frankie the Fly baloon.
Image by Annie Ruygt

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:

DHH on X: Rails 8 is going to be such a leap forward in production deployment ease. Kamal 2 will get auto-configuring SSL via Let's Encrypt + multi apps on a single host. Thruster will give Puma HTTP/2, X-Sendfile, and asset caching. Both will be included by default. DHH on X: The goal is to get Heroku-like ergonomics (the gold standard!) for all Rails apps, regardless of where you deploy them. Cloud or on-prem, it'll be all the same. No more cloud lock-in.

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:

deploying is a matter of running two commands: terraform apply; kamal setuphttps://www.luizkowalski.net/production-grade-ish-deployment-on-hetzner-with-kamal/

Even those who aren’t familiar with terraform:

"Since I would be creating new servers on Hetzner, I asked ChatGPT to help me doing that with Terraform so the whole process could be easily repeatable."https://glaucocustodio.github.io/2024/01/18/migrating-from-dokku-to-kamal-provisioning-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.

Fly.io ❤️ Rails

Fly.io is a great way to run your Rails HotWired apps. It’s really easy to get started. You can be running in minutes.

Deploy a Rails app today!

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 and config/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 suggested kamal traefik reboot once I updated the configuration, but traefik 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:

DHH on X: I know I've been down on cloud lately, but I really like what  http://Fly.io is doing. The HTTP verb-based region distribution is brilliant, it's built on stock Dockerfiles that are easy to migrate, and they engage with the ecosystem 👍

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.