Rails 8 on Kamal example setup with Sidekiq, Redis and Postgres

Rails 8 on Kamal example setup with Sidekiq, Redis and Postgres
Rails and Docker in the Kamal ocean happily avoiding the maintenance iceberg.

Finally, after years of Heroku, Render and fly.io, you make the switch to self-hosting. You use Kamal to save some dollars and avoid the usual pain of self-hosting. You hit good timing because Kamal has matured.

Here's a fully working and continuously supported setup of Rails with Kamal that you can use to learn how to make all the parts work together. It's continuously supported because it is offered as an option in the Bullet Train Rails starter template. This Kamal setup also powers the (upcoming) related open source app.socialgames.cc SaaS.

In this guide, I'll show you two ways of setting up Kamal:

  1. For a completely new Bullet Train app.
  2. For an existing (Bullet Train) app.

But you should be able to apply the concepts to your non-Bullet Train app and use the PR code as an example:

If you are not familiar with the Bullet Train starter template. It's basically Rails on steroids that gives you everything you need for your SaaS app development, so you spend less time reinventing the wheel and more time hacking out your core features.

Prerequisites

To follow this guide, you will need:

  • A server that you can SSH into. I've used Hetzner, but see disclaimer at the end of this section.
  • A domain that you own that points to your server. For example, if you will be using a subdomain for your app, you can just create an A Record in your domain registrar:
TYPE: A Record
Host: bubba
# Your IP from your SSHable server:
Value: 999.999.999.999
TTL: Automatic

For a domain like socialgames.cc, you will be able to hit your Kamal server with bubba.socialgames.cc.

  • Ruby and Docker installed locally.
  • Kamal installed locally:
> gem install kamal
Fetching net-scp-4.1.0.gem
...
8 gems installed

> cd code/github/app
app> kamal init
Created configuration file in config/deploy.yml
Created .kamal/secrets file
Created sample hooks in .kamal/hooks

  • You need Rails credentials to have been created via EDITOR="code --wait" bin/rails credentials:edit --environment production locally.
πŸ”
A note on your remote production server: To get started, it would be as easy as setting up a Hetzner server. There are 1-2 small decisions you need to make during purchase/setup, but you don't need anything else other than being able to do $ ssh root@999.999.999.999 from your local machine to login. However, you might want to harden your server afterward if you want to keep your app, your data and your users safe. I heard great things about the Kamal handbook where you can learn all about it, including a slick server setup script.

New Bullet Train apps

You are starting a new adventure!

You'll probably want to go through the app setup here and see your app running locally first, which includes stuff like cloning the repo, running bin/configure and running bin/setup. bin/configure will initiate some relevant name changes for the deploy.yml and secrets files.

GitHub - bullet-train-co/bullet_train: The Open Source Ruby on Rails SaaS Template
The Open Source Ruby on Rails SaaS Template. Contribute to bullet-train-co/bullet_train development by creating an account on GitHub.

The changes you will need to do manually at this point are here:

(the amount of manual changes needed might change in future PRs)

All the files that need changes:

Changes in config/deploy.yml

Basically, fixing all the FIXMEs:

Changes in .kamal/secrets

The below setup assumes that all the env vars are exported on your local machine, for example in your .zshrc or .bashrc files. A more recommended setup is to do this via kamal secrets extract and a secrets vault.

Changes in `config.database.yml

Deployment

Once you are done with the changes, you can run:

app> kamal setup
β–Ά kamal setup          
  INFO [c1d95a7c] Running /usr/bin/env mkdir -p .kamal on 999.999.999.999

... this can take a few minutes...
... but then it should be "Finished" πŸ‘‡

Releasing the deploy lock...
  Finished all in 128.0 seconds

Existing Rails projects

For an existing Rails project you may want to start by:

> cd app
app> kamal init

And then copy over have these files from the starter repo:

Docker-specific:

  • Dockerfile
  • docker-entrypoint

Kamal-specific:

  • config/deploy.yml
  • config/deploy.yml

Make manual changes as described in the previous setup section for new Bullet Train apps.

Making it past sign up

After you have things up and running, you might attempt to sign up. This will fail if you don't have a mail server configured yet because sending the sign up email will error.

You will need to:

  • Uncomment the Postmark gem for production:
group :production do
  # We suggest using Postmark for email deliverability.
  # gem "postmark-rails"
  • Create a Postmark account.
  • Create a new sending server.
  • Get the server API key.
  • Add it to your local secrets.
  • Replace support_email: change.me@localhost in the application.en.yml with the email address you signed up for Postmark (in case you are not approved, you can only use that sender as your From-email address and send to your allow-listed email addresses, like the-address@i-signed-up-with-in-postmark.com).
  • You also need to fix your production.rb mailer host here: config.action_mailer.default_url_options = { host: "example.com" }

Setting up CI

Check out Lars Corneliussen's GitHub Actions CI setup in this GitHub Gist.

Running a second app on the same server

This might be as simple as reusing the same setup but changing the Postgres and Redis ports as well as possibly some parts of the Dockerfile. According to this article, this is how it would work for Postgres and I'm also adding my assumption of how this would work for Redis:

# deploy.yml
env:
  clear:
    POSTGRES_DB: socialgames_cc_production
    DB_PORT: 5432 # <-- Internal port - stays the same.
    REDIS_HOST: socialgames-cc-redis
    REDIS_PORT: 6371 # <-- Changed to a different port.
...

accessories:
  db:
    image: postgres:14
    host: 999.999.999.999
    port: "127.0.0.1:5433:5432" # <-- Only change the exposed localhost port.
...
  redis:
    image: redis:7.0
    port: 6371 # <-- Changed to the same port as we expose in the env vars.

For the Dockerfile, make sure you don't have anything conflicting in case you operate on the host machine.

Gotchas and TODOs

  • The Dockerfile can be improved, to rather resemble this.
  • We might want to provide a BT-specific server setup/hardening script.

Cool people who helped and resources I used

Looks like it takes a village to raise a Kamal app, I've booked about 25 Pomodoro-like focus time. Which in a person's life who allocates about 1-2 hours per day to side projects, it's 3 weeks. Gotta be careful with your side quests! But this one was really needed for me to host my upcoming Open Source BT Sample SaaS app, so it's time well-spent.

I've also posted about this on socials and got support from incredible people.

Big thanks go to:

  • Josef (helped reviewing the earliest version! πŸ’ͺ)
  • Justin Marsh (helped a lot bouncing thoughts)
  • Lars Corneliussen (shared his working and well-looking setup)
  • CF platform guys (Commented on my first confused messages on LinkedIn and helped moving into the
  • swombat (Shared his Bullet Train Dockerfile)

Sorry, because I probably forgot someone.

In terms of articles and examples, I started with the Kamal official docs:

Deploy web apps anywhere
From bare metal to cloud VMs using Docker, deploy web apps anywhere with zero downtime.

I bumped early into these articles and learned quite a bit from them:

Hiccups While Putting a Rails 8 App in Production w/ Kamal 2 - Zack Gilbert
Pointing out a few of the hiccups I ran into while putting a simple Rails 8 app in production with Kamal 2: Choosing and setting up a server & getting the Docker registry working.
A detailed look at running Kamal setup/deploy
Now that we have our Sinatra app up and running I want to do a quick run-through to talk about the simplicity of Kamal and how it’s really just a nice layer on top of some great open-source tools like Docker and Traefik. Whenever running a Kamal command it
Kamal’s missing tutorial – how to deploy a Rails 8 app with Postgres
Rails 8 is out. And with it Kamal 2, the new default way of deploying Rails apps. But Kamal is hard for the uninitiated. This is a complete tutorial on how to get a Rails app fully in production using Kamal.

I also used other people's setups for inspiration:

BT docker setup
BT docker setup. GitHub Gist: instantly share code, notes, and snippets.
Comments
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to RichStone Input Output.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.