Deploying a Rails App with Kamal on Hetzner: A Beginner's Guide
Tags: rails, kamal, hetzner
Published: 2023-06-09 19:37:31

Hello there!
 
I was curious about Kamal and wanted to give it a try to host a simple application on a cheap VPS. With this guide, I'll walk you through the process step by step that I used to create and deploy your first Rails application with KAMAL on a Hetzner VPS. 

For this tutorial, we'll keep things as simple as possible. We'll deploy an application that uses an SQLite database.

What you will need first:
- a Docker account with an Api key
- an Hetzner account 

Assuming you have that ready you should be able to deploy you first app with `Kamal` in less than 10 minutes

## Scaffold a Rails Application

First, let's scaffold an application. At the time of writing this guide, it's important to use the `--main` flag to use the Rails 7.1 alpha version.

```bash
new rails_kamal_hetzner -a propshaft --main -c tailwind -j esbuild
cd rails_kamal_hetzner
bundle add tailwindcss-rails
bin/rails tailwindcss:install
git add .
git commit -m "rails new"
rails g scaffold post title:string content:text
rails db:create db:migrate
```

Next, you'll need to update your routes.

```diff
# config/routes.rb
-  # root "articles#index"
+  root "posts#index"
```

## Create a VPS on Hetzner

Now that we have a basic app set up, let's create a VPS on [Hetzner](https://hetzner.cloud/?ref=gyPLk7XJthjg). You'll need to navigate through the sign-up and confirmation process, after which you can create a new project from your account.


![Create an Hetzner project with a new VPS](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1muxkiyh5b4cs7lwe61q.png)


For this tutorial, we'll select the CAX11, one of the most affordable VPS options with 2vCPU and 4GB RAM.

![Selecting the Hetzner VPS for Kamal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d6yf84klofienjh0z5hi.png)

Please note that at the time of writing, KAMAL does not support IPv6, so you need to retain IPv4. 

**You'll also need to add an SSH key to your server.** Here's how to copy and paste the key:

```bash
pbcopy < ~/.ssh/id_rsa.pub
```

Then test your SSH access to the server with this command:

```bash
ssh root@your_server_ip
```

If you encounter the prompt, "Are you sure you want to continue connecting (yes/no/[fingerprint])?", answer 'yes'. 

If you're unable to SSH into the server, you'll need to resolve this issue before proceeding further.

## Deploying with KAMAL

With our application and server ready, it's time to put KAMAL into action. First, install it with the following command:

```bash
gem install kamal
```

Then run `kamal init`. This will create several files, including `config/deploy.yml` and `.env`.

In the `.env` file, update the `RAILS_MASTER_KEY` value and the `KAMAL_REGISTRY_PASSWORD` with a key created in your Docker account.

Next, update the `config/deploy.yml` file as follows:

```yaml
service: rails_hetzner_kamal

image: adrienpoly/rails_hetzner_kamal

servers:
  - hetzner_ip_v4

registry:
  username: adrienpoly
  password:
    env: KAMAL_REGISTRY_PASSWORD
env

:
  secret:
    - RAILS_MASTER_KEY
ssh:
  user: root # important to change it here as by default Kamal use app and Hetzner root
```

You can omit the other files for now. Commit your changes and then run the command to bootstrap servers with curl and Docker.

```bash
kamal server bootstrap
``` 

Once this operation is successful, you can execute your first deploy with `kamal deploy`. The first deployment might take a few minutes and should conclude with a successful health check message.

At this point, you should be able to visit the IP address in a browser and see your Rails app! Et voilà!


![Kamal app live](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/37ktankjk8v5jec6zs8f.png)

## Persisting the Data

Well well well, not really voilà yet. If you create a `Post` and make a new deployment, you'll notice that the data is lost. This happens because the SQLite database is running inside the Docker image by default. Each time a new container is built, it erases the data.

To solve this, you need to add a volume to the Docker container. In the `config/deploy.yml` file, add this line:

```yaml
volumes:
  - "storage:/rails/storage"
```

Now, the data is written outside of the container and persists across deploys.

And that's it! You've deployed your first Rails application with KAMAL on a [Hetzner](https://hetzner.cloud/?ref=gyPLk7XJthjg) VPS. 

Happy deploying!


[15-11-2023] Updated to reflect the name change from MRSK to Kamal

PS : Here is my referral link if you want to test Hetzner https://hetzner.cloud/?ref=gyPLk7XJthjg it gives you a 20€ credit to test