Production SQLite powered by Litestream with Rails 8

Let's get into it. Ruby on Rails 8 and SQLite in production. Words only a couple years ago that would get you laughed out of the room. I've been leaning on SQLite since 2018 with alot of folks doing great work in the Ruby community around it.

Here's a plan for redundancy utilizing Kamal and Litestream. If you haven't heard of litestream before, its a tool that allows you to fully replicate your SQLite database. It comes with batteries included commands to replicate and restore!

Follow one of their excellent sources of documentation to replicate to your preferred service. For me, I'm using S3 (https://litestream.io/guides/s3/)

As far as the Kamal goes, I'm using an accessory service that runs litestream's provided docker container (https://hub.docker.com/r/litestream/litestream).

You'll want to be sure to add a config/litestream.yml file in your Rails application with this contents, the keys are interpolated by Litestream later so we need to ensure they're included in our secrets (https://litestream.io/reference/config/#auto-read-environment-variables):

dbs:
  - path: /data/production.sqlite3
    replica:
      type: s3
      path: db # this is the path in your bucket
      bucket: your-bucket
      region: your-region
      access-key-id: ${AWS_ACCESS_KEY_ID}
      secret-access-key: ${AWS_SECRET_ACCESS_KEY}

Once we have this setup we can move on to the config/deploy.yml for Kamal:

volumes:
  - "storage:/rails/storage"
  # - /host:/path/on/container
  
accessories:
  litestream:
    host: host-ip
    image: litestream/litestream
    env:
      secret:
        - LITESTREAM_ACCESS_KEY_ID
        - LITESTREAM_SECRET_ACCESS_KEY
    cmd: "replicate"
    files:
      - config/litestream.yml:/etc/litestream.yml
    volumes:
      - "storage:/data"

I've included the overall application volume setup in the Kamal file as this is important. The accessory needs to know this exists and maps the volume to the data folder for the Litestream image container.

Once you have these committed and run kamal accessory reboot litestream you should be able to tail the Litestream container and see the connection and replication happen to your S3 instance.

time=2025-12-31T00:00:00.380Z level=INFO msg="snapshot complete" txid=000000000000001e size=1170108
time=2025-12-31T03:15:00.226Z level=INFO msg="compaction complete" level=1 txid.min=000000000000001f txid.max=0000000000000020 size=368
time=2025-12-31T03:15:00.299Z level=INFO msg="compaction complete" level=2 txid.min=000000000000001f txid.max=0000000000000020 size=368
time=2025-12-31T04:00:00.215Z level=INFO msg="compaction complete" level=3 txid.min=000000000000001f txid.max=0000000000000020 size=368
time=2025-12-31T04:38:30.231Z level=INFO msg="compaction complete" level=1 txid.min=0000000000000021 txid.max=0000000000000022 size=1121