From 7296cfc43902af2f843c54f7e5e5685458a3f0ee Mon Sep 17 00:00:00 2001 From: Andrew McCallum Date: Mon, 2 Nov 2026 20:35:26 +0000 Subject: [PATCH] Add a guide to hosting openRun on Fly.io A fly.toml for a real instance: the 0.3 release line, data on a volume, one always-on machine, invite-only and TRUST_PROXY. docs/FLY.md covers setup, a custom domain, upgrades, backups and password resets, linked from the README. Co-Authored-By: Claude Opus 5.5 --- README.md | 2 ++ deploy/fly/fly.toml | 50 +++++++++++++++++++++++++++++ docs/FLY.md | 78 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+) create mode 100644 deploy/fly/fly.toml create mode 100644 docs/FLY.md diff --git a/README.md b/README.md index 467fc0c..452b211 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,8 @@ docker compose up -d Open the URL. The first account you create becomes the admin. Everything is stored in `./data` (see [Backups](#backups)). +No server of your own? [docs/FLY.md](docs/FLY.md) runs it on Fly.io for a few dollars a month. + Passkeys need HTTPS (or `localhost`), so put openRun behind a reverse proxy such as Caddy or Traefik for anything beyond local use. When you do, set `TRUST_PROXY=true`. All settings are listed in [.env.example](.env.example). To run a public demo with sample data instead, see [docs/DEMO.md](docs/DEMO.md). ### Upgrading diff --git a/deploy/fly/fly.toml b/deploy/fly/fly.toml new file mode 100644 index 0000000..db42202 --- /dev/null +++ b/deploy/fly/fly.toml @@ -0,0 +1,50 @@ +# openRun on Fly.io, with your data on a volume. See docs/FLY.md. +# +# Change `app`, `ORIGIN` and `primary_region` before deploying. + +app = 'my-openrun' +primary_region = 'lhr' + +[build] + # A release line: deploys pick up its fixes, not the next minor release, + # since database updates can't be undone. Bump it when you choose to upgrade. + image = 'ghcr.io/stretch0/openrun:0.3' + +[env] + # The address people use. Passkeys are tied to its hostname, so settle on + # it before anyone adds one. + ORIGIN = 'https://my-openrun.fly.dev' + # Fly's proxy sets X-Forwarded-For, so the sign-in rate limit may trust it. + TRUST_PROXY = 'true' + # It's on the internet: people sign up with invites you make in the app. + # The first account doesn't need one and becomes the admin. + INVITE_ONLY = 'true' + INSTANCE_NAME = 'openRun' + # How members can reach you, shown in the app. + # INSTANCE_CONTACT = 'you@example.com' + +# Accounts, runs, original files, club logos and push keys. Without a volume +# they'd be lost on every restart. +[mounts] + source = 'openrun_data' + destination = '/data' + +[http_service] + internal_port = 3000 + force_https = true + # Always on, or syncs, weather updates and run reminders don't happen. + auto_stop_machines = 'off' + auto_start_machines = true + min_machines_running = 1 + + [[http_service.checks]] + grace_period = '30s' + interval = '30s' + timeout = '5s' + method = 'GET' + path = '/api/health' + +[[vm]] + memory = '512mb' + cpu_kind = 'shared' + cpus = 1 diff --git a/docs/FLY.md b/docs/FLY.md new file mode 100644 index 0000000..0591851 --- /dev/null +++ b/docs/FLY.md @@ -0,0 +1,78 @@ +# Hosting openRun on Fly.io + +No server of your own? [Fly.io](https://fly.io) runs openRun as one small, always-on machine with your data on a volume. It takes about ten minutes and costs a few dollars a month: one shared-CPU machine with 512 MB, and a 1 GB volume (check [Fly's pricing](https://fly.io/docs/about/pricing/)). + +You need a Fly account and [`flyctl`](https://fly.io/docs/flyctl/install/), signed in with `fly auth login`. + +## Set it up + +1. Download [`deploy/fly/fly.toml`](../deploy/fly/fly.toml) into an empty folder: + + ```bash + curl -O https://raw.githubusercontent.com/stretch0/openRun/main/deploy/fly/fly.toml + ``` + +2. In `fly.toml`, change: + - `app`, to a name of your own. App names are shared across all of Fly, so pick something unlikely to be taken. + - `ORIGIN`, to `https://.fly.dev`, or your own domain (see [Your own domain](#your-own-domain)). + - `primary_region`, to the region nearest your members (`fly platform regions` lists them). + +3. Create the app, its volume and its secret, then deploy. Replace `my-openrun` and `lhr` with your app name and region: + + ```bash + fly apps create my-openrun + fly volumes create openrun_data -a my-openrun --region lhr --size 1 + fly secrets set -a my-openrun BETTER_AUTH_SECRET="$(openssl rand -base64 32)" + fly deploy --ha=false + ``` + + `--ha=false` keeps it to one machine. Don't add a second: each would have its own database. + +4. Open your `ORIGIN` and create your account. The first account becomes the admin. Then invite people from **Admin**: `INVITE_ONLY` is on, since the app is on the internet. + +`BETTER_AUTH_SECRET` signs everyone's sessions and encrypts their intervals.icu keys. Fly keeps it, but you can't read it back, so if you might ever move off Fly, set it from a value you've saved somewhere safe instead. + +## Your own domain + +```bash +fly certs add run.example.com -a my-openrun +``` + +Add the DNS records it prints, then set `ORIGIN` to `https://run.example.com` and run `fly deploy` again. Passkeys are tied to the hostname, so do this before anyone adds a passkey; after a change, everyone adds theirs again. + +## Upgrading + +`fly.toml` pins a release line, such as `0.3`. Back up first (below), then: + +- **Fixes in the same line:** `fly deploy`. +- **The next release line:** change the image tag in `fly.toml` (for example `:0.3` to `:0.4`), then `fly deploy`. + +Database updates are applied when openRun starts (`fly logs` shows "Applied 1 database update"), and can't be undone except by restoring a backup. + +## Backups + +Fly takes a snapshot of the volume every day and keeps it for 5 days (`fly volumes snapshots list `; `fly volumes list` shows the id). A volume lives on one server, so if that server fails, a snapshot is what you get back: up to a day old. To restore one, see Fly's guide to [restoring from a snapshot](https://fly.io/docs/volumes/snapshots/). + +For a copy you keep yourself, back up the database while openRun runs, then download it: + +```bash +fly ssh console -a my-openrun -C "node backup.js /data/backups/openrun.db" +fly ssh sftp get /data/backups/openrun.db openrun-$(date +%F).db -a my-openrun +fly ssh console -a my-openrun -C "rm /data/backups/openrun.db" +``` + +That's the database: accounts, runs, clubs and settings. Original FIT and GPX files are in `/data/files`, and each person can download theirs from the run page or export everything in Settings. + +## Forgotten passwords + +```bash +fly ssh console -a my-openrun -C "node reset-password.js someone@example.com" +``` + +It prints a temporary password and signs that person out everywhere. Send it to them privately. + +## Things to know + +- **Keep it always on.** `auto_stop_machines = 'off'` in `fly.toml` matters: a stopped machine doesn't sync from intervals.icu, update the weather or send run reminders. +- **Logs:** `fly logs -a my-openrun`. +- **Every other setting** in [.env.example](../.env.example) can go under `[env]` in `fly.toml`.