Trying Kamal
I tried for the first time to deploy a Rails+Postgresql web application using Kamal. I’ve had some issues, let’s try to document them here for future reference.
The application had a a fairly normal Rails 8 + Ruby 3.4 + PG setup.
Step 1: spin up a box to run it
I created a cheap instance on Hetzner. To harden it a bit, I added a firewall (accept only ports 22, 80 and 443) using Hetzner’s UI and made sshd only accept public key authentication.
vi /etc/sshd/sshd_config
=> PasswordAuthentication no
systemctl restart ssh
Step 2: DNS
I added a new subdomain, A
record pointing to the IP of the instance.
Step 3: Container registry
Kamal builds docker images. I needed a container registry and decided to use Github’s. I had to create a personal access token (classic) allowed to read and create packages and saved it aside.
Step 4: Adapting kamal configuration
Most examples are for MySQL. In the end I’ve got this setup for PG. Most issues are connected to the various environment variables required by the official image (documentation here), kamal and the rails application.
In .kamal/secrets
:
# ...other settings...
POSTGRES_PASSWORD=$POSTGRES_PASSWORD
# ...other settings...
In config/deploy.yml
env:
secret:
- RAILS_MASTER_KEY
- POSTGRES_PASSWORD
clear:
DB_HOST: foobar-db
POSTGRES_USER: foobar
POSTGRES_DB: foobar_production
DB_PORT: 5432
# ...other stuff...
accessories:
db:
image: postgres:17
host: my-host.pzac.net
port: "127.0.0.1:5432:5432"
env:
clear:
POSTGRES_USER: foobar
POSTGRES_DB: foobar_production
secret:
- POSTGRES_PASSWORD
files:
# Initialization script automatically executed after initdb
- db/production_setup.sql:/docker-entrypoint-initdb.d/setup.sql
directories:
- data:/var/lib/postgresql/data
The db/production_setup.sql
file is automatically executed by docker when starting the PG image. That’s where you’ll create the databases.
CREATE DATABASE foobar_production;
CREATE DATABASE foobar_production_cache;
CREATE DATABASE foobar_production_queue;
CREATE DATABASE foobar_production_cable;
Step 5: starting the app
kamal deploy
Issue #1: docker builds
Build was failing when installing the gems. After some lengthy investigations the problem seemed linked to building the images locally (I’m on a M1 mac and the target platform is amd64). I fixed this by building the images on the app instance itself with
builder:
arch: amd64
local: false
remote: ssh://root@my-instance
Issue #2: solid queue
My docker container was shutting down because Puma had some troubles with SolidQueue. After some investigations the problem boiled down to missing the SolidQueue tables. I though they would have been created automatically. The ugly pragmatic solution (I couldn’t find anything better, there must be) was to:
- create a migration on my local developement db to create the SolidQueue tables (I basically copied the content of
db/queue_schema.rb
) - run the migration locally
- take the corresponding SQL from
db/structure.sql
(BTW: I alwaysconfig.active_record.schema_format = :sql
) - copy and paste it on a psql on the postgresql container
Useful commands
kamal accessory {boot|stop|start} db
# shell on the db container
kamal accessory exec db --interactive --reuse "bash"
kamal redeploy
Useful links
- [https://hub.docker.com//postgres](https://hub.docker.com//postgres)
- https://rameerez.com/kamal-tutorial-how-to-deploy-a-postgresql-rails-app/
- https://kamal.wiki/accessory/deploying-rails-application-with-kamal-and-postgres/