Deployment

Dokploy · full stack

Self-host Roost, Supabase, and private file storage together.

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

  1. Open your Dokploy dashboard.
  2. Create a new application.
  3. Choose Docker Compose deployment.
  4. Set the repository to https://github.com/msrbuilds/roost (branch: main).
  5. Set compose path to docker-compose.dokploy.yml.
  6. Paste the generated env vars into the app’s Environment section.
  7. Under Domains, bind your domain to the nginx service on port 80.
  8. Hit Deploy.

3. DNS and TLS

  1. Point your domain’s DNS A record to the VPS IP address.
  2. In Dokploy, enable HTTPS on the domain binding (Dokploy handles Let’s Encrypt).
  3. 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

  1. Open your app in Dokploy.
  2. Click the db service.
  3. Go to the Terminal tab.
  4. Run:
psql -U postgres -d postgres -c "SELECT setup_first_admin('your@email.com');"

What it does

  • Sets the user’s role to superadmin in the profiles table.
  • 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:

  • minio container runs the S3-compatible API.
  • minio-init creates 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_ENDPOINT must be the AWS S3 regional URL (not empty) — this is what switches the stack from MinIO to AWS.
  • S3_FORCE_PATH_STYLE=false enables virtual-hosted-style URLs that AWS requires.
  • PUBLIC_API_URL is 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-fix exits after running once.
  • minio-init exits after creating the bucket.
  • A single 502 right after deploy resolves on refresh.

Problem:

  • auth, rest, or storage stuck restarting — check Postgres password and JWT_SECRET match.
  • minio unhealthy — MINIO_ROOT_PASSWORD must be at least 8 characters.
  • /api/health returns 503 — backend can’t reach Kong; check container network.
  • Persistent 403 from 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.


These docs live with the source code.Improve this page ↗

Search the docs

Find your next step.