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_HOSTis 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_HOSTis 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.