Docs · Deployment

Production Database and Storage

Running SQLite in production, the storage volume, and S3-compatible file storage.

One Shot runs SQLite in production, not just in development. That's a deliberate choice for a kit meant to run on a single small server, not a limitation you're expected to migrate away from the moment you launch.

SQLite in production

Rails 8's SQLite adapter is production-ready, and it's what backs the database along with Solid Queue, Solid Cache, and Solid Cable, none of which need a separate Redis or Postgres service. The database files live in a Docker volume Kamal manages (oneshot_storage in this kit's own deployment), which persists across deploys since the container itself is disposable but the volume isn't.

Back this volume up, off the server, on a schedule. It's the one piece of state a redeploy can't recreate: your entire database and any locally stored uploaded files live inside it. A server failure without an off-server backup means losing everything in that volume, not just the running container.

When SQLite stops being enough

SQLite in this configuration comfortably handles the traffic most single-server apps see. If you outgrow a single server's disk and CPU for the database specifically, the standard path is moving to a managed Postgres instance and swapping the adapter, which is a deliberate, plannable migration rather than something you need to anticipate on day one. Most apps built on this kit won't hit that ceiling before other constraints do.

File storage: local disk versus S3

Active Storage, by default, writes uploaded files to local disk inside the same volume as the database. That works, and it's the zero-configuration default. For production, configuring S3-compatible object storage is worth doing before you have real user uploads to migrate:

  • AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, S3_BUCKET, S3_REGION
  • S3_ENDPOINT, if you're using an S3-compatible provider other than AWS itself (Cloudflare R2 and similar services work through this same configuration)

See Optional Integrations for where these are set. Until you configure them, the local-disk volume is the fallback, and it works correctly; it just means files live and get backed up alongside your database rather than in dedicated object storage.

Migrations

Run a one-off migration against the running production container directly through Kamal:

bin/kamal app exec "bin/rails db:migrate"

Next

You've now covered the whole deployment picture. See what every environment variable in the app actually does: Bring Your Own Keys.

Common questions

Can I run SQLite in production for a Rails app?

Yes, for one server. Rails 8 ships SQLite configured for production use, and One Shot runs on it: the database is a file on a mounted volume, backups are a file copy, and there is no second service to operate. The limit is real though. Two application servers cannot write to the same SQLite file, so horizontal scaling is where it stops.

How do I back up a SQLite database on a live server?

Run sqlite3 storage/production.sqlite3 ".backup /tmp/out.sqlite3" inside the container, then copy the result off the host. The .backup command takes a consistent snapshot while writes continue; a plain cp of a live database gives you a file that looks fine and fails on restore. Store it somewhere the server cannot reach.