Deploy Roost on a VPS managed by Dokploy using docker-compose.dokploy.yml. The stack includes the full Supabase backend (Postgres, Auth, Realtime, Storage) and MinIO for file storage — everything self-hosted, no external accounts needed.
For failures and recovery, see Dokploy VPS Troubleshooting.
1. Generate Environment Variables
Open the Env Generator to create all required secrets and keys in your browser:
https://roost-env-generator.vercel.app (or open dokploy-template/env-generator.html locally)
The generator creates:
- POSTGRES_PASSWORD, JWT_SECRET, SECRET_KEY_BASE — random secrets
- ANON_KEY, SERVICE_ROLE_KEY — Supabase JWTs signed with your JWT_SECRET
- MINIO_ROOT_PASSWORD — MinIO storage credential
- Optional: SMTP, Stripe configuration
Copy the full output.
Manual Generation (Alternative)
If you prefer CLI:
# Generate secrets
POSTGRES_PASSWORD=$(openssl rand -base64 32 | tr -dc 'A-Za-z0-9' | head -c 32)
JWT_SECRET=$(openssl rand -base64 48 | tr -dc 'A-Za-z0-9' | head -c 64)
SECRET_KEY_BASE=$(openssl rand -base64 48 | tr -dc 'A-Za-z0-9' | head -c 64)
MINIO_ROOT_PASSWORD=$(openssl rand -base64 32 | tr -dc 'A-Za-z0-9' | head -c 32)
Then generate ANON_KEY and SERVICE_ROLE_KEY using your JWT_SECRET:
# Generate HS256 JWT (requires node or python)
node -e "
const crypto = require('crypto');
const header = Buffer.from(JSON.stringify({alg:'HS256',typ:'JWT'})).toString('base64url');
const payload = Buffer.from(JSON.stringify({role:'anon',iss:'supabase',iat:Math.floor(Date.now()/1000),exp:Math.floor(Date.now()/1000)+315360000})).toString('base64url');
const sig = crypto.createHmac('sha256','$JWT_SECRET').update(header+'.'+payload).digest('base64url');
console.log(header+'.'+payload+'.'+sig);
"
# Repeat with role:'service_role' for SERVICE_ROLE_KEY
2. Deploy in Dokploy
- Open your Dokploy dashboard.
- Create a new application.
- Choose Docker Compose deployment.
- Set the repository to
https://github.com/msrbuilds/roost(branch:main). - Set compose path to
docker-compose.dokploy.yml. - Paste the generated env vars into the app’s Environment section.
- Under Domains, bind your domain to the
nginxservice on port80. - Hit Deploy.
3. DNS and TLS
- Point your domain’s DNS A record to the VPS IP address.
- In Dokploy, enable HTTPS on the domain binding (Dokploy handles Let’s Encrypt).
- Wait for TLS certificate issuance (usually under a minute).
4. First Admin Setup
After deploying, sign up through the frontend at https://your-domain.com. Then promote that account to superadmin:
Option A: From your VPS terminal (SSH)
# Find the db container name
docker ps --format '{{.Names}}' | grep db
# Run the SQL (replace the container name and email)
docker exec -it <APP_PREFIX>-db-1 psql -U postgres -d postgres -c \
"SELECT setup_first_admin('your@email.com');"
Option B: From Dokploy dashboard
- Open your app in Dokploy.
- Click the db service.
- Go to the Terminal tab.
- Run:
psql -U postgres -d postgres -c "SELECT setup_first_admin('your@email.com');"
What it does
- Sets the user’s role to
superadminin theprofilestable. - Gives full access to the admin dashboard (
/admin). - The
setup_first_admin()function auto-deletes itself after first use — it’s a one-time bootstrap only.
Required Env Vars
| Variable | Description |
|---|---|
ROOST_DOMAIN |
Your domain (e.g. community.example.com) |
POSTGRES_PASSWORD |
Database password (32+ chars) |
JWT_SECRET |
Shared JWT signing key (64+ chars, alphanumeric only) |
ANON_KEY |
Supabase anon JWT (signed with JWT_SECRET) |
SERVICE_ROLE_KEY |
Supabase service_role JWT (signed with JWT_SECRET) |
SECRET_KEY_BASE |
Realtime session encryption (64+ chars) |
Storage
Option A: Private MinIO (default)
MinIO is included as the default S3-compatible storage backend. Dokploy generates its required credentials.
How it works:
miniocontainer runs the S3-compatible API.minio-initcreates private upload and backup buckets.- The Roost backend performs uploads and authorization checks.
- Public images and authorized private downloads are served through the backend API; MinIO is not exposed directly.
Required env vars (generated by the template):
MINIO_ROOT_USER=<generated>
MINIO_ROOT_PASSWORD=<generated>
MINIO_BUCKET=roost-uploads
Deployment fails closed if either credential is missing.
Data persistence: Stored in a minio-data Docker volume. Back up this volume to preserve uploaded files.
Option B: AWS S3
To use AWS S3, set these env vars and remove all MINIO_* vars:
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=your-secret
AWS_S3_BUCKET=your-bucket-name
AWS_REGION=us-east-1
S3_ENDPOINT=https://s3.us-east-1.amazonaws.com
S3_FORCE_PATH_STYLE=false
PUBLIC_API_URL=https://your-domain.com
Important:
S3_ENDPOINTmust be the AWS S3 regional URL (not empty) — this is what switches the stack from MinIO to AWS.S3_FORCE_PATH_STYLE=falseenables virtual-hosted-style URLs that AWS requires.PUBLIC_API_URLis used to create backend-proxied public media URLs.- MinIO will still run idle in the background (harmless, ~30MB) but won’t be used.
Post-Deploy Sanity Check
APP_PREFIX="your-app-prefix" # e.g. roost-roost-472jet
# 1. All containers running
docker ps --format 'table {{.Names}}\t{{.Status}}' | grep "$APP_PREFIX"
# 2. Backend health
curl -i https://your-domain.com/api/health
# 3. MinIO health
docker exec "${APP_PREFIX}-minio-1" curl -sf http://localhost:9000/minio/health/live && echo "OK"
# 4. Internal PostgREST via Kong
BACKEND="${APP_PREFIX}-backend-1"
docker exec "$BACKEND" sh -lc 'wget -S -qO- \
--header="apikey: $SUPABASE_SECRET_KEY" \
--header="Authorization: Bearer $SUPABASE_SECRET_KEY" \
"http://kong:8000/rest/v1/profiles?select=count&limit=1"; echo'
Expected: all return 200.
Normal vs Problem States
Normal (expected):
realtime-tenant-fixexits after running once.minio-initexits after creating the bucket.- A single
502right after deploy resolves on refresh.
Problem:
auth,rest, orstoragestuck restarting — check Postgres password and JWT_SECRET match.miniounhealthy —MINIO_ROOT_PASSWORDmust be at least 8 characters./api/healthreturns503— backend can’t reach Kong; check container network.- Persistent
403from Supabase — ANON_KEY/SERVICE_ROLE_KEY not signed with the correct JWT_SECRET. - File uploads fail — check backend logs for S3 connection errors.
See: Dokploy VPS Troubleshooting
Optional Env Vars
APP_NAME=Roost
GOTRUE_AUTOCONFIRM=false
# SMTP (email verification, password reset)
SMTP_HOST=
SMTP_PORT=587
SMTP_USER=
SMTP_PASS=
SMTP_FROM_EMAIL=noreply@example.com
# Stripe (paid memberships)
STRIPE_ENABLED=false
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
STRIPE_PRICE_ID=
If no SMTP is configured, set GOTRUE_AUTOCONFIRM=true to skip email verification during testing.
Related Guides
- Docker (VPS) — standalone Docker deployment with Traefik
- Self-Hosted Setup — complete self-hosted overview
- Dokploy VPS Troubleshooting — SQL recovery scripts and common fixes