Rails 8 on Kamal example setup with Sidekiq, Redis and Postgres
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:
- For a completely new Bullet Train app.
- 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.
$ 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.
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 theapplication.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, likethe-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:
I bumped early into these articles and learned quite a bit from them:
I also used other people's setups for inspiration: