Deploy an app with SQLite, ActiveStorage and Kamal

SQLite as a single or small development team database for Ruby on Rails projects ended out the year as all the rage. For in-depth action in this area follow Stephen Margheim, he's doing a TON of work for the Ruby on Rails community in this realm.

This article is meant as a reminder for myself, but, figured it may help someone out on the internet. I recently updated an old project where I utilized SQLite in production and configured it for deployment with Kamal.

This posed a few different problems that anyone with this setup will need to solve for:

  • Persisting the SQLite file on the host machine, this would allow us to be able to restart the container, destroy the container, rebuild the container and still have our data.
  • Connecting ActiveStorage to a long-lived file system to again, persist any container restarts, rebuilds, etc.

Firstly, we need to SSH into our target VM on whatever VPS provider you prefer. We'll create two directories at the top level of the file system. (You can place them wherever you'd like.) I put them here for easy access later.

$ mkdir /db
$ mkdir /storage

Then we'll need to change the permissions of these directories so our user within our container can access them. You can read more about volumes, docker, and permissions in containers here. I've done this with changing the ownership of the directories to the user with ID 1000, as that's our rails user in our container for the application.

$ chown 1000:1000 /db /storage

A simplified Kamal deploy file should look as follows:

# Name of your application. Used to uniquely configure containers.
service: web-app

# Name of the container image.
image: my-image

# Deploy to these servers.
servers:
  web:
    hosts:
      - MY_HOST_IP

volumes:
  # host path:container path
  - "/db:/rails/sqlite"
  - "/storage:/rails/storage"

# Credentials for your image host.
registry:
  # Specify the registry server, if you're not using Docker Hub
  # server: registry.digitalocean.com / ghcr.io / ...
  username: kamal

  # Always use an access token rather than real password when possible.
  password:
    - KAMAL_REGISTRY_PASSWORD

# Inject ENV variables into containers (secrets come from .env).
# Remember to run `kamal env push` after making changes!
env:
  secret:
    - RAILS_MASTER_KEY


# Configure builder setup.
builder:
  secrets:
    - RAILS_MASTER_KEY

The main thing to point out here is we're using volumes over Kamal's concept of files or directories. This means you could put your stage database and your production database in the same folder and mount the container in either environment on the same server if you wanted.

For data persistence and recovery you'd want to setup a way to offload your /db and /storage directories.

The configuration for Active Storage is simple, check your environment file is pointing config.active_storage.server = :local and be sure you have an entry in your storage.yml file similar to:

local:
  service: Disk
  root: <%= Rails.root.join("storage") %>

That's it, kamal deploy to your hearts content.