Deployment

Quick start (Docker Compose)

git clone https://github.com/inboxesnet/inboxes.git && cd inboxes
cp .env.example .env
# Edit .env - at minimum set: DOMAIN, ENCRYPTION_KEY, SESSION_SECRET, PUBLIC_URL, POSTGRES_PASSWORD
docker compose up -d

The stack includes four services: postgres, redis, backend, and frontend. You'll need your own reverse proxy for HTTPS.


Environment variables

Required (no defaults)

VariableExampleNotes
DOMAINmail.yourdomain.comInterpolated in docker-compose.yml to build APP_URL, NEXT_PUBLIC_API_URL, NEXT_PUBLIC_WS_URL
PUBLIC_URLhttps://mail.yourdomain.comResend webhook callback base URL. Compose fails if unset.
ENCRYPTION_KEYopenssl rand -base64 32Must decode to exactly 32 bytes. Encrypts stored Resend API keys. If lost, stored keys are unrecoverable.
SESSION_SECRETopenssl rand -hex 32Must be at least 32 characters.
POSTGRES_PASSWORDopenssl rand -hex 16No default. Compose fails if unset.

Optional

VariableDefaultNotes
POSTGRES_USERinboxes
POSTGRES_DBinboxes
RESEND_SYSTEM_API_KEYOnly needed for system emails (invites, password resets) in self-hosted mode
API_PORT8080Backend listen port inside the container
TRASH_COLLECTOR_ENABLEDfalseEnable automatic purging of expired trash
EVENT_RETENTION_DAYS90Days to keep WebSocket events

How env vars flow

.env file
  |
  v
docker-compose.yml interpolation
  |
  |-- postgres container
  |     POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB
  |
  |-- backend container
  |     DATABASE_URL, REDIS_URL, ENCRYPTION_KEY,
  |     SESSION_SECRET, APP_URL, PUBLIC_URL, API_PORT
  |
  |-- frontend container
        NEXT_PUBLIC_API_URL, NEXT_PUBLIC_WS_URL

Docker resource limits

ServiceMemoryCPU
postgres512M0.5
redis128M0.25
backend256M0.5
frontend256M0.5

Adjust these in docker-compose.yml if your workload requires more.


Health checks

ServiceEndpointIntervalRetries
backendcurl -sf http://localhost:8080/api/health30s3
frontendcurl -sf http://localhost:3000/30s3
postgrespg_isready5s5
redisredis-cli ping5s5

Reverse proxy

Neither container exposes host ports. You need a reverse proxy to route external traffic:

  • /api/* (including /api/ws) → backend on port 8080
  • Everything else → frontend on port 3000
  • WebSocket upgrade must be supported for /api/ws

Coolify setup

  1. Create a Docker Compose resource from the GitHub repo
  2. In Advanced Settings, deselect “Strip Prefixes”
  3. Set the frontend service domain to mail.yourdomain.com (leave backend blank)
  4. Set all environment variables in the Coolify panel
  5. Deploy — Coolify handles TLS automatically

Post-deploy setup

  1. Open https://mail.yourdomain.com
  2. Sign up — creates your org + admin user (no email verification in self-hosted mode)
  3. Complete onboarding — enter your Resend API key, select domains, configure aliases
  4. Verify Resend domain DNS (MX, SPF, DKIM) in the Resend dashboard

Minimum env vars

DOMAIN=mail.yourdomain.com
PUBLIC_URL=https://mail.yourdomain.com
SESSION_SECRET=$(openssl rand -hex 32)
ENCRYPTION_KEY=$(openssl rand -base64 32)
POSTGRES_PASSWORD=$(openssl rand -hex 16)

Everything else has working defaults or is only needed for commercial mode.


Local development

# Automated setup (macOS)
./scripts/setup.sh

# Start dev servers
./scripts/dev.sh

setup.sh installs dependencies via Homebrew, starts Postgres and Redis, creates the database, generates .env with random secrets, and runs npm install. dev.sh starts the backend on :8080 and frontend on :3000.


Troubleshooting

SymptomFix
Compose fails with POSTGRES_PASSWORD errorSet POSTGRES_PASSWORD in .env
“ENCRYPTION_KEY must decode to 32 bytes”Use openssl rand -base64 32
Webhook emails not arrivingVerify PUBLIC_URL is externally reachable
Login doesn't persistEnsure APP_URL matches the browser URL
WebSocket won't connectEnsure DOMAIN matches and TLS is working
Backend OOM killedIncrease memory limit in docker-compose.yml