Skip to content
Deploying / Runwell

Runwell

Installing the Runwell template -- agency project management with a client portal, on SQLite -- and making the owner account.

Runwell is agency project management with a client portal and approvals. It runs on SQLite, so there is no database accessory: everything lives in one volume.

Image ghcr.io/martin-business-consultants/runwell:2.1.2
Port 80 inside the container, behind kamal-proxy
Health check /up
Volumes storage, backed up with the container stopped
Singleton yes

Port 80 is Thruster’s, not Puma’s 3000, so gzip and asset caching happen inside the container.

Singleton means one container at a time: Runwell writes to SQLite, and two writers during a rollover are worse than a short blip, so KamalCloud sets Kamal’s drain_timeout to 0 and the old container goes before the new one boots. Expect a few seconds of downtime on every deploy. That is deliberate.

Before you start

  • A server that is registered, and a ready runner
  • A hostname. APP_HOST is set from it at install, and it is where links in mail point
  • Somewhere off the machine for backups
  • Optionally, SMTP details. Without them Runwell sends no mail, and invitations and portal links have to be copied by hand

Installing

Templates → Runwell → Install. Give it a name, a runner, an environment, the server and the domain. Two optional fields are asked for as well:

Field
SMTP server Optional. Without one, Runwell sends no mail
SMTP username Optional, and only meaningful with an SMTP server

Both land as ordinary variables you can change later. Install creates the app and does not deploy it. Point an A record at the server, then press Deploy.

What the install sets

Variable Value
APP_HOST the environment’s domain where links in mail point
ASSUME_SSL true kamal-proxy has already terminated TLS
SOLID_QUEUE_IN_PUMA true mail and in-place updates run inside the web process

And four generated secrets:

Secret
SECRET_KEY_BASE 64 hex characters
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY 32 letters and digits
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY 32 letters and digits
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT 32 letters and digits

There is no RAILS_MASTER_KEY. The image’s credentials.yml.enc is encrypted with a key no install has, so these four stand in for it.

Keep a copy of the three ACTIVE_RECORD_ENCRYPTION_* values somewhere else. They are generated once, at install. A restored database cannot read the API keys and tokens stored in it without them, and no backup of the volume contains them.

Making the owner

Open the domain after the first deploy and go to /signup. The first person to sign up becomes the owner; everyone after them joins by invitation. Runwell works the way KamalCloud itself does here – one install, one team.

Sending email

Add these as variables on the environment and deploy:

Variable Example
SMTP_ADDRESS smtp.example.com plain; asked for at install
SMTP_USERNAME plain; asked for at install
SMTP_PORT 587 plain; 587 if left out
SMTP_PASSWORD secret

Or use the Outsend plugin inside Runwell instead, and leave all four alone.

Use a relay or your mail provider’s submission port. Sending straight out on port 25 is blocked on new Hetzner, DigitalOcean and AWS accounts.

Data and backups

Everything is in the storage volume, <app name>-storage on the host: four SQLite databases and their journals, uploaded files, plugins, and the releases an in-place update downloads.

The volume is registered for backup at install and marked to stop containers, because a live tar would catch a database mid-write. Each backup and each restore is therefore consistent, and each is a short outage. Set a schedule and a bucket on the volume.

The image backs up any database with migrations still to run before migrating it on boot, so a deploy that brings a schema change already leaves its own copy inside the volume.

Upgrading

Two routes, and they reach the same place:

  • From KamalCloud – change the image tag on the app and deploy
  • From inside Runwell – Settings → Updates updates it in place

The container runs whichever is newer, that downloaded release or this image. Bumping the tag here and deploying is the same upgrade done from KamalCloud, so neither route undoes the other.

Troubleshooting

  • A few seconds of downtime on every deploy – expected. Runwell is a singleton; see above.
  • Invitations and portal links never arrive – no SMTP is configured. Add the variables above, or use the Outsend plugin.
  • Mail links point at the wrong host – APP_HOST is stale after a domain change. Settings → Address inside Runwell wins over it, so check there too.
  • A restored database cannot read stored API keys – the three ACTIVE_RECORD_ENCRYPTION_* values differ from the ones in use when the backup was taken. They are not in the backup.