Installing and moving
Deploy in one click
No server of your own yet? Three ways to a running instance without setting up Docker by hand. All of them use the same image as the Compose file below.
6 min read
| Method | Data | HTTPS and address |
|---|---|---|
| Render | SQLite on a persistent disk | Automatic, https://<name>.onrender.com or your domain |
| DigitalOcean App Platform | PostgreSQL (dev database), files in a Spaces bucket | Automatic, https://<name>.ondigitalocean.app or your domain |
| Any server (Hetzner, Netcup, IONOS …) | SQLite in a Docker volume on the server | Caddy with a Let's Encrypt certificate for your domain |
Render
Deploy to Render → (opens in a new tab)
The button reads the Blueprint render.yaml (opens in a new tab) from the repository: a web service from the image ghcr.io/yannicschuller/flowplan:latest in Frankfurt, a 5 GB disk on /app/data and the health check on /api/health.
- Click the button, sign in to Render and confirm the Blueprint. The SMTP fields can stay empty (see E-mail).
- Render generates
FLOWPLAN_SETUP_TOKEN. Once the deploy is done, copy it from the service's Environment tab and openhttps://<name>.onrender.com/register?setup=<token>– this first account administers the instance.
Costs: persistent disks need a paid instance – the Blueprint uses Starter, the smallest one with a disk; the disk is billed per GB on top (see Render's pricing). For more than a handful of people choose a larger instance (Sizing). On the free instance the data would be lost on every restart.
Address: without APP_URL, Flowplan uses Render's own address (RENDER_EXTERNAL_URL, or else https:// with RENDER_EXTERNAL_HOSTNAME). For your own domain, add it under Settings → Custom Domains and set APP_URL=https://flowplan.example.com under Environment – ideally before the first account, because passkeys are bound to the address.
Persistence and updates: everything lives on the disk – Render runs a service with a disk as exactly one instance and stops it briefly on every deploy. Render keeps snapshots of the disk; for backups away from Render, add S3 (Storage, S3 and backups). To update, start a Manual Deploy: Render pulls the :latest image again.
DigitalOcean App Platform
Deploy to DigitalOcean → (opens in a new tab)
The button reads .do/deploy.template.yaml (opens in a new tab): a service from the image, a PostgreSQL 16 dev database and the health check on /api/health, in Frankfurt (fra).
App Platform has no persistent volumes: the container's disk is reset on every deploy and restart. That is why the template uses PostgreSQL instead of SQLite (DATABASE_URL points at the database) and expects a bucket for files:
- Uploads: create a Spaces bucket and a Spaces access key, and fill in
S3_BUCKET,S3_ACCESS_KEY_IDandS3_SECRET_ACCESS_KEY(S3_ENDPOINTis preset for Frankfurt,https://fra1.digitaloceanspaces.com). Flowplan then puts every file into the bucket and fetches missing ones back after a deploy. Without a bucket, uploaded files are lost on the next deploy. FLOWPLAN_SETUP_TOKEN(generate withopenssl rand -hex 24): the first account only via<address>/register?setup=<token>.FLOWPLAN_ENCRYPTION_KEY(generate withopenssl rand -base64 32): encrypts the files in the bucket and protects stored secrets such as API keys. It must be set here, because asecrets.keyon the container's disk would be replaced on every deploy. Keep a copy in a password manager.- Optional
WEB_PUSH_PUBLIC_KEYandWEB_PUSH_PRIVATE_KEY(npx web-push generate-vapid-keys): without them, new keys are made on every deploy and push in the browser stops until people turn it on again. Push to the apps is not affected.
App Platform cannot generate secrets itself, so you enter these values when creating the app (or later under the component's Settings → Environment Variables). APP_URL is App Platform's ${APP_URL}: the ondigitalocean.app address, or your domain once you add it under Settings → Domains – then deploy again.
Database: a dev database has no backups and is meant for trying out. For real use, upgrade it to a managed database cluster, or attach an existing one with PostgreSQL 16 or newer (PostgreSQL). The image ships pg_dump and pg_restore, so Flowplan's own scheduled backups work on PostgreSQL too – store them in the bucket (Storage, S3 and backups). The connection is encrypted; uselibpqcompat=true in DATABASE_URL accepts DigitalOcean's own certificate authority, as psql does with sslmode=require. If the log reports permission denied for schema public, the dev database's role may not create tables – use a managed database cluster, where the database owner can (Role without superuser rights).
Updates: start a new deploy (Actions → Force Rebuild and Deploy); App Platform pulls the :latest image again.
Any server: cloud-init or install script
For a virtual server (VPS) with Ubuntu 24.04 or Debian 12 at any provider. Most of them – Hetzner Cloud, Netcup, IONOS, DigitalOcean Droplets, Vultr, Scaleway, OVHcloud – accept a cloud-init file as "user data" or "cloud config" when you create the server.
- Point your domain's DNS record (A, and AAAA for IPv6) to the server – with a new server, as soon as you know its address.
- Copy
deploy/cloud-init.yaml(opens in a new tab), set the one lineFLOWPLAN_DOMAIN=flowplan.example.comand paste it as user data when creating the server. - After a few minutes, sign in with SSH and read
/root/flowplan-README.txt: it holds the address and the link for the first account. Progress is logged in/var/log/flowplan-install.log.
On a server that already runs, the install script does the same:
curl -fsSL https://raw.githubusercontent.com/yannicschuller/flowplan/main/deploy/install.sh | sudo bash -s -- flowplan.example.comWhat the script sets up:
- Docker with the Compose plugin from Docker's own package repository (skipped when it is already there).
/opt/flowplanwithcompose.yaml(Flowplan and Caddy (opens in a new tab), which fetches and renews the HTTPS certificate itself), theCaddyfileand a.envreadable only by root. Its secrets are generated on the server withopenssl:FLOWPLAN_SETUP_TOKENandFLOWPLAN_ENCRYPTION_KEY– keep a copy of the key in a password manager, encrypted backups cannot be read without it.- The data in the Docker volume
flowplan-data. It survives updates and restarts; for backups off the server add S3 or SFTP (Storage, S3 and backups). - Firewall (ufw): only SSH, 80 and 443 stay open. On a server with a firewall of its own, run the script with
FLOWPLAN_FIREWALL=off. Ports 80 and 443 must be free – if another web server already holds them, put Flowplan behind it instead (Coolify and reverse proxies). - Automatic security updates of the system (unattended-upgrades).
- Image updates are off by default: you update when it suits you with
cd /opt/flowplan && docker compose pull && docker compose up -d– the administration tells you about new releases. For an automatic weekly update (Sunday night), setFLOWPLAN_AUTO_UPDATE=weeklyin the cloud-init file (or beforebashin the command), or later runsystemctl enable --now flowplan-update.timer.
Without a domain (FLOWPLAN_DOMAIN= left empty, or the script without an argument), Flowplan starts at http://<server address> without a certificate – fine for a test, see Without HTTPS. To switch to a domain later, point its DNS record to the server and run the script again with the domain: it sets APP_URL and the Caddyfile, and keeps the data and secrets. Do it before the first account if you can, because passkeys are bound to the address.
Further settings (e-mail, single sign-on, S3) go into /opt/flowplan/.env, followed by docker compose up -d in /opt/flowplan – see Configuration.