---
title: "Fly.io ❤️ Kamal"
description: "Explorations of Kamal, from a Fly.io Perspective"
---

# 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.](https://fly.io/ruby-dispatch/flyio-heart-kamal/assets/fly-heart-kamal-cover.webp)

[Annie Ruygt](https://annieruygtillustration.com/)

*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](https://pragprog.com/titles/rails7/agile-web-development-with-rails-7/) for [Rails 8](https://github.com/rails/rails/milestone/87), and chapter 17 (Deployment and Production) will need be rewritten to focus on [Kamal](https://kamal-deploy.org/). This naturally lead me to spend a few hours [deploying](https://hetzner.intertwingly.net/showcase/) my [Showcase Rails](https://github.com/rubys/showcase?tab=readme-ov-file#showcase) app to [Hetzner](https://www.hetzner.com/). 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](https://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.](https://fly.io/ruby-dispatch/flyio-heart-kamal/assets/dhh-kamal-2-part-1.png)](https://twitter.com/dhh/status/1781052974816485673) [![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.](https://fly.io/ruby-dispatch/flyio-heart-kamal/assets/dhh-kamal-2-part-2.png)](https://twitter.com/dhh/status/1781053369693393245)

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.terraform.io/):

![deploying is a matter of running two commands: terraform apply; kamal setup](https://fly.io/ruby-dispatch/flyio-heart-kamal/assets/kamal-terraform-part-1.png) – [https://www.luizkowalski.net/production-grade-ish-deployment-on-hetzner-with-kamal/](https://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://fly.io/ruby-dispatch/flyio-heart-kamal/assets/kamal-terraform-part-2.png) – [https://glaucocustodio.github.io/2024/01/18/migrating-from-dokku-to-kamal-provisioning-with-terraform/](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](https://fly.io/blog/docker-without-docker/) containers, it actually runs your application (and what it calls [“accessories”](https://kamal-deploy.org/docs/configuration/accessories/) like databases and redis) in containers.

It also runs [Traefik](https://traefik.io/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! →](https://docs.fly.io/rails)

## Notes for the Book

While the [video demos](https://kamal-deploy.org/) 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](https://www.hetzner.com/) or [Digital Ocean](https://try.digitalocean.com/cloud/). Note the IP address.
- Obtain a DNS name from a place like [GoDaddy](https://www.godaddy.com/) or [Namecheap](https://www.namecheap.com/). [Associate it](https://www.godaddy.com/help/add-an-a-record-19238) with the IP address from the previous step.
- Install [Docker](https://www.docker.com/). Unless you are on Linux, this likely means [Docker Workstation](https://www.docker.com/get-started/), though I have heard good things about [OrbStack](https://orbstack.dev/). You need this even if you aren’t going to be building on your own machine.
- Sign up with a container registry like [DockerHub](https://hub.docker.com/signup). 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](https://kamal-deploy.org/docs/installation/) 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](https://kamal-deploy.org/docs/configuration/). 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](https://discord.gg/YgHVT7GCXS) 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 https://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 👍](https://fly.io/ruby-dispatch/flyio-heart-kamal/assets/dhh-praise-tweet.webp)](https://twitter.com/dhh/status/1632044101418745864)

But this is mere words. The proof is real: it is a demonstrable fact that Fly.io and Kamal can use the same [Dockerfile](https://github.com/rubys/showcase/blob/main/Dockerfile), and that the configuration files ([`fly.toml`](https://github.com/rubys/showcase/blob/main/fly.toml) and [`config/deploy.yml`](https://github.com/rubys/showcase/blob/main/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.
