Run the server your phone and computers meet at on a machine you control. One command sets up the control plane, the web app and automatic HTTPS.
Helmlane has two halves. The agent runs on your computers and does the work. The control plane is where your phone and your computers meet — accounts, projects, chats, the event feed and notifications. The public one is https://api.helmlane.com; this guide runs your own, so nothing about your work touches a server you do not control. Either way, your files stay on your computers: the control plane relays only what the app asks to see.
Or, from a checkout, ./scripts/selfhost/install.sh. It asks for:
Your domain. It checks that the name resolves and that ports 80 and 443 are free.
An email address for certificate expiry notices (optional).
Optionally, a DNS provider for a wildcard preview domain and an SMTP relay for account email. Both can be added later.
Then it generates every secret the server needs, builds the images, starts everything, and waits until https://<your domain>/health answers with a real certificate. The first build takes a few minutes. Running the installer again later is safe: it keeps the configuration and brings the stack up to date. Unattended:
Open https://<your domain> in a browser — the web app is served there — or enter your server’s address on the phone app’s sign-in screen, and create your account.
Verify your email. Without an SMTP relay the link is in the server log:Terminal
./scripts/selfhost/manage.sh mail-links
Open it within 30 minutes.
In the app, Computers → Add computer. The command it shows already points at your server (--origin https://<your domain>). Linux, WSL and Docker are covered in the Linux guide; on a Mac, sign in to the Helmlane app with your server’s address.
The log, otherwise (HOSTED_EMAIL_TRANSPORT=log, the default here). Links are written to the server log and manage.sh mail-linksshows them. Fine for a server only you use; add SMTP before inviting anyone.
Previews — a dev server on one of your computers, opened on your phone — are each served at their own origin under a wildcard domain,*.preview.<your domain>, so a dev server’s code can never reach the control plane or another preview. Without that domain the apps say previews are unavailable instead of offering them. A wildcard certificate can only be issued through a DNS challenge, so the installer asks for your DNS provider and an API token:
Provider
Token needs
cloudflare
An API token with Zone → DNS → Edit on the zone
digitalocean
A personal access token with write scope
hetzner
A DNS API token
duckdns
Your DuckDNS token
Also add a DNS record *.preview.<your domain> pointing at the server. The installer builds Caddy with the matching plugin and writesdocker/caddy/sites/preview.caddy; delete that file and restartcaddy to turn it off again.
Push reaches a phone through Apple (APNs) and Google (FCM) with keys that belong to the app’s publisher. The published Helmlane apps only accept pushes signed with Helmlane’s own keys, which a self-hosted server does not have. On a self-hosted server the apps therefore show notifications while they are open and report push as unavailable, rather than promising notifications that cannot arrive.
If you build the iOS or Android app yourself under your own identifiers, give the server your keys: place them in docker/secrets/(owner-only, chmod 600) and set in docker/.env:
./scripts/selfhost/manage.sh status # containers, and whether HTTPS answers./scripts/selfhost/manage.sh logs # follow the control plane (or: logs caddy)./scripts/selfhost/manage.sh mail-links # account links from the log transport./scripts/selfhost/manage.sh update # pull the latest source, rebuild, restart./scripts/selfhost/manage.sh backup # database + configuration + secrets./scripts/selfhost/manage.sh restore FILE # onto a fresh server
Updates apply database migrations on start. Update the server before the agents on your computers: a newer agent talking to an older server is refused until the server catches up. The reverse is fine.
Backups are a single tar.gz in docker/backups/ with a database dump, docker/.env and docker/secrets/. It contains every secret of the server — store it encrypted and off the machine. Images and attachments live in the blobs volume and are not included; back that volume up with your usual tooling if you need them.
Restoring onto a new server: install Docker, clone the repository, copy the backup over, run ./scripts/selfhost/manage.sh restore helmlane-….tar.gz, and point DNS at the new server. Computers reconnect on their own; nothing needs to be enrolled again.
docker/docker-compose.yml runs four services on a private network:
Service
Role
Exposed
caddy
HTTPS with automatic certificates, the web app, reverse proxy
80, 443
hosted
The control plane
Only to caddy
postgres
Accounts, projects, chats, machines
No
blobs
Images and attachments (S3-compatible, TLS with a private CA)
No
Where
What
docker/.env
Configuration and generated passwords (0600)
docker/secrets/
Signing key, object-store certificates, your push keys
Volume helmlane_postgres
The database
Volume helmlane_blobs
Uploaded images
Volume helmlane_caddy-data
TLS certificates
To use a managed bucket instead of the bundled store, setHOSTED_S3_ENDPOINT (HTTPS only), HOSTED_S3_REGION,HOSTED_S3_BUCKET and the keys in docker/.env.
HOSTED_SECRET_KEY is the root secret. Machine credentials and enrollment tokens derive from it. If it is lost, every computer must be enrolled again; if it leaks, rotate it with a new install and re-enroll.
Only Caddy listens on the internet. The control plane, database and object store are reachable only on the Compose network.
Enrollment tokens are single-use, expire within fifteen minutes, and are stored only as a hash.
The control plane never stores your files. It relays what the app requests from a computer you enrolled, and each computer serves only the folders you shared on it.
Keep the server patched (unattended-upgrades or your distribution’s equivalent) and update Helmlane with manage.sh update.