This is the first part of a four-series post in which I’ll explore how we can host all of our rails apps on one Hetzner virtual machine using Kamal. Why? Because we're cheap and it's a great exercise to get to know Kamal and Docker.
I've see a bunch of posts around the web that talk about the basecamp's Kamal. Here I'd like to show one way of how you can use Kamal to deploy not only one, but many of your hobby rails applications on one single Hetzner VM... That's right, for the price of around EUR 4 per month, you'd be surprised how much you can host.
Please note that this setup is only recommended for hobby apps or experiements. But it should be easy enough to switch from this setup to a more scalable one, once your requirements grow..
Kamal automatically installs Docker, builds your containers, pushes them to a container repository. Zero-downtime deployment is done by starting up the new version before switching over to the new one. Kamal uses Traefik to direct internet traffic to our app's containers (which are like isolated environments for running our app). It also helps us get security certificates from a service called Let's Encrypt, which proves our website is safe. We’ll create a setup for a typical Ruby on Rails application, with a PostgreSQL relational database, Sidekiq, and a Redis key-value store. But I'll talk more about these components in the sections below.
I've structured this article into four seperate posts:
This is the first part. Let's do it 🚀
First of all you'll need to install Docker Desktop
(For Mac, Windows or Linux) and create an account with Docker Hub if you don't have one already.
Once you've created your account, on your dashboard you'll find your docker username, looking something like this:
Also under your account settings, you can find your Access Token under Security. You'll later need both the username and the access token to setup Kamal, so note them down somewhere. We'll come back to Docker later to finish our setup. In the meantime, just leave your Docker desktop running in the background.
Create a cloud account with Hetzner and then go to your console. It should look something like this:
Go ahead and create a new project. Within that project, click Add Server
. Choose your favourite location, add a Ubuntu
server and go for a shared vCPU, with an Arm64 (Ampere)
architecture. Then choose the CAX11
, the cheapest machine available ✌️. Leave the Networking as it is (Public IPv4 and 6) and go to SSH Keys.
This is an important step, as it identifies your local machine as trustworthy, meaning you'll be able to interact with your server directly form your terminal, without any additional passwords, authentication, etc. Hetzner has an article about how to do it. Or do this in your terminal:
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
Then you should be able to read your SSH Key
:
cat ~/.ssh/id_rsa.pub
You should see a very long Hash. Copy the whole thing into your clipboard and paste it into your Hetzner server (SSH Keys
). If all is right, the Name field should be automtically filled with your email address. Leave the rest of the setup form as it is and jump the end to give your virtual machine a name (Can be anything).
On your right you should see a summary of your order. At the time of writing this, it was EUR 4.08 per month 🤯
Go ahead and Create & Buy now
Now you're able to go into that newly-created server. You'll immediately see on your overview page the Public IP
address. Write that down in a note next to your Docker credentials.
But before we continue, we need to set up a firewall. For that, find the Firewalls
link inside the sidebar on the left or wherever in your project. Click Create Firewall
and add the following rules to ensure your server can only be accessed via specific endpoints:
Awesome. If everything is set up correctly, you now should be able to access your server from your terminal, like this:
ssh root@YOUR-HETZNER-IP-ADDRESS
If it works, you've now set up your VM to host your hobby app, for a very reasonable price. Let's move over to your domain setup. If not, make sure you're able to access your server via CLI. Check if the SSH Key is stored properly on Hetzer or ask ChatGPT 😅
First of all, set up an account with Cloudflare if you don't already have one. Cloudflare is a content delivery network (CDN) and security platform. As a CDN, it caches static content (like images, CSS, and JavaScript files) from your website on servers around the world, so that content is delivered to users from a location geographically closer to them, reducing load times. On the security front, Cloudflare provides protection against DDoS attacks, which are attempts to make a website unavailable by overwhelming it with traffic. It also offers a Web Application Firewall (WAF) to help block malicious requests and can manage DNS settings for your domain, including providing SSL/TLS for secure connections. We'll route out traffic through Cloudflare for additional security.
Once you've bought your domain on a registrar, proceed on Cloudflare:
DNS
-> records
and get your Cloudflare Nameservers
@
. This one points to the bare url (e.g. my-app.com). The other one should be www
, which points to the www. subdomain (e.g.www.my-app.com). Content for both should be your Hetzner IP address (from the Hetzner setup above). Leave the rest as is.3a02:5d7:1k1e:574c::/64
. Copy that number, paste it into the content field on cloudflare and remove the last /64
.SSL/TLS
on the sideboad and set your SSL/TLS encryption
to Full. That's it, your traffic will now be directed through Cloudflare.
Let's move over to part 2, where we're setting up our Rails app.