The easy way to self-host orboto - Coolify wires domains, TLS and secrets for you.
Coolify is a popular self-hosted deployment platform.
If you already run it (or want the simplest possible orboto install), this
is the recommended path: point Coolify at the compose file and it wires
domains, TLS certificates and secrets for you.
Prefer full control, or no platform at all? Use the standalone
Docker guide - everything Coolify automates here is
done by hand there.
Coolify auto-populates every SERVICE_* magic variable in docker-compose.yml (credentials, random DB name, public hostnames) and keeps them stable across rebuilds.
⚠️ Volumes are external: true. The three persistent volumes (orboto_postgres_data, orboto_rustfs_data, orboto_backups) are declared external so Coolify cannot recreate them on redeploy (which would silently wipe the database). You must create them once on the host before the first deploy - see the pre-flight step below.
SSH to the Coolify host and run the migration helper:
git clone https://github.com/orboto/orboto /tmp/orboto-setupcd /tmp/orboto-setupsudo ./scripts/migrate-to-external-volumes.sh # existing install - copies from Coolify-managed volumes# or, for a brand-new install that has never deployed yet:sudo ./scripts/migrate-to-external-volumes.sh --fresh # creates empty externals, no copy
The script refuses to run while any orboto container is up; in Coolify, click Stop on the orboto app first, then run it.
After it completes you'll have three named volumes on the host - orboto_postgres_data, orboto_rustfs_data, orboto_backups - that the compose file references. Coolify deploys/redeploys from here on will reuse them.
Create a new resource → Docker Compose → point it at the orboto repository (branch main) → select docker-compose.yml.
Domains: Coolify auto-assigns a hostname from your wildcard domain for SERVICE_FQDN_WEB_80 (the only public FQDN - the api is reached via /api/* through the web's nginx). Optionally swap in your own custom domain.
Deploy. Database migrations run automatically on every container start - the API image's entrypoint is node dist/db/migrate.js && node dist/index.js, so a redeploy always applies pending migrations before the API accepts traffic. No manual step needed, even on upgrades. If a migration fails the container exits non-zero and Coolify won't route traffic to a half-migrated API.
That's it. Open the web FQDN in a browser and the setup wizard will prompt for the first admin account - see First-run setup.
Upgrade safety. Drizzle migrations are idempotent and additive - existing rows keep their values, NOT NULL columns are introduced with DEFAULT + backfill UPDATE in the same migration. The pg_backup sidecar (see below) plus Coolify's volume persistence mean that rolling back a bad deploy only requires redeploying the previous image tag. Before a major upgrade or before applying a migration from an unfamiliar branch, take a pre-migrate snapshot manually:
Email delivery → log in as admin → System Settings → Email delivery. Pick UseSend (recommended for EU / DSGVO - paste an API key from your UseSend admin UI plus the base URL; self-hostable via Docker / Coolify with AWS SES eu-central-1 underneath), Resend (paste an API key from resend.com), or SMTP (host/port/credentials). Click "Send test" to verify.
SENTRY_DSN (Coolify env tab) - ships unhandled exceptions to Sentry
IMAGE_TAG (Coolify env tab) - pin to a specific git SHA instead of latest
LOG_LEVEL, RATE_LIMIT_MAX, S3_BUCKET, S3_REGION, PG_BACKUP_RETENTION_DAYS (Coolify env tab) - overrides for the shipped defaults
On redeploy, volumes and auto-generated secrets are preserved. To rotate a secret (e.g. SERVICE_PASSWORD_64_JWT), clear the value in Coolify and the next deploy regenerates it - note that rotating JWT_SECRET invalidates all sessions and rotating GIT_ENC_SECRET makes existing encrypted git tokens unreadable.
My database is empty after a redeploy.
Cause: the persistent volumes weren't created as external before the
first deploy, so Coolify recreated them from scratch.
Fix: this should not happen if you ran the pre-flight step above first.
If it already has, restore from a backup - see Backups.
Going forward, always run the pre-flight migration helper before the very
first deploy on a Coolify host.
Deploy succeeds but the app shows a blank page or a CORS error.
Cause: the API and web hostnames don't match what the containers were
built/configured with.
Fix: confirm SERVICE_FQDN_WEB_80 and SERVICE_URL_API in the Coolify
env tab match the domain you're actually opening in the browser, then
redeploy.
A deploy exits non-zero right after "running migrations".
Cause: a pending migration failed - Coolify intentionally won't route
traffic to a half-migrated API.
Fix: check the deployment logs for the actual database error. The
previous, working deployment stays live until you fix it and redeploy.
I rotated a secret and now nobody can sign in, or git connections stopped working.
Cause: rotating JWT_SECRET invalidates every session by design; rotating
GIT_ENC_SECRET makes existing encrypted git access tokens unreadable by
design.
Fix: for JWT_SECRET, everyone simply signs in again - expected. For
GIT_ENC_SECRET, re-create each git connection with a fresh access token.