DEV Community

Cover image for Deploying a Rails App with Kamal on Hetzner: A Beginner's Guide
Adrien Poly
Adrien Poly

Posted on • Updated on

Deploying a Rails App with Kamal on Hetzner: A Beginner's Guide

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.

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
Enter fullscreen mode Exit fullscreen mode

Next, you'll need to update your routes.

# config/routes.rb
-  # root "articles#index"
+  root "posts#index"
Enter fullscreen mode Exit fullscreen mode

Create a VPS on Hetzner

Now that we have a basic app set up, let's create a VPS on Hetzner. 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

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

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:

pbcopy < ~/.ssh/id_rsa.pub
Enter fullscreen mode Exit fullscreen mode

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

ssh root@your_server_ip
Enter fullscreen mode Exit fullscreen mode

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:

gem install kamal
Enter fullscreen mode Exit fullscreen mode

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:

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
Enter fullscreen mode Exit fullscreen mode

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

kamal server bootstrap
Enter fullscreen mode Exit fullscreen mode

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

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:

volumes:
  - "storage:/rails/storage"
Enter fullscreen mode Exit fullscreen mode

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 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

Top comments (11)

Collapse
 
samuelodan profile image
Samuel O'Daniels

Wow! It's that simple? I heard DHH talk about it a while ago (if I remember correctly) and thought to myself, "this has to be pretty complicated."

I currently use Dokku + Digital Ocean and I'm happy with it, but I'd like to try this one out.
Thanks for making this post.

Collapse
 
lucasayb profile image
Lucas Yamamoto

Really nice. Never heard of MRSK before! Thanks for the article!

Collapse
 
raynawara profile image
Raymond J Nawara

No matter how many times I try, I can never get this to run.

Do I need a SSL Certificate? I always get:

`This site can’t be reached5.78.92.233 refused to connect.
Try:

Checking the connection
Checking the proxy and the firewall
ERR_CONNECTION_REFUSED`

I can connect to the server and docker ps shows everything up:

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c2e4f9fedad8 rjnawara/my-hertzner-app:f6fe46ed0ee6583762587f8038152fa2ebf728e7 "/rails/bin/docker-e…" 4 minutes ago Up 4 minutes (healthy) 3000/tcp my-hertzner-app-web-f6fe46ed0ee6583762587f8038152fa2ebf728e7
74a3188bb817 traefik:v2.9 "/entrypoint.sh --pr…" 4 minutes ago Up 4 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp traefik

Any ideas as to what I screwed up?

Thanks, Ray

Collapse
 
adrienpoly profile image
Adrien Poly

Are you able to run

ssh root@server-ip

Collapse
 
dotenv profile image
Dotenv

make sure force_ssl is set to false on your Rails app - until you are ready to wire up your domain and add ssl termination.

Collapse
 
nickisnoble profile image
Nick Noble

Is there an easy way to ask kamal to configure SSL?

(Does it happen on the hetzner server, or inside the image?)

Collapse
 
rpaweb profile image
Rafael Peña-Azar, M.Sc.Eng.

Excellent article!!! Everything very well explained and detailed. Kudos!!!

The only thing with me rn is that I try to connect via ssh root@ip and even with SSH key already configured and added into the server config in hetzner cloud, I only can enter the console inside hetzner webapp, but not in my local console from my mbp. Same pass. On hetzner's cloud app works, but on my mbp in local, doesn't. Still trying to process what can be the error or the mistake I'm making.

Collapse
 
skylerptp profile image
Skyler

It dosen't work actually if do as same as you did

Collapse
 
adrienpoly profile image
Adrien Poly

What are your specific errors? What dit you try ?

Collapse
 
dotenv profile image
Dotenv

good article. thanks.

Collapse
 
kozubenko profile image
Andrew Kozubenko

What is a right way to run migrations on each kamal deploy?