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:

Useful commands

kamal accessory {boot|stop|start} db

# shell on the db container
kamal accessory exec db --interactive --reuse "bash"

kamal redeploy