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 -dThe stack includes four services: postgres, redis, backend, and frontend. You'll need your own reverse proxy for HTTPS.
Environment variables
Required (no defaults)
| Variable | Example | Notes |
|---|---|---|
DOMAIN | mail.yourdomain.com | Interpolated in docker-compose.yml to build APP_URL, NEXT_PUBLIC_API_URL, NEXT_PUBLIC_WS_URL |
PUBLIC_URL | https://mail.yourdomain.com | Resend webhook callback base URL. Compose fails if unset. |
ENCRYPTION_KEY | openssl rand -base64 32 | Must decode to exactly 32 bytes. Encrypts stored Resend API keys. If lost, stored keys are unrecoverable. |
SESSION_SECRET | openssl rand -hex 32 | Must be at least 32 characters. |
POSTGRES_PASSWORD | openssl rand -hex 16 | No default. Compose fails if unset. |
Optional
| Variable | Default | Notes |
|---|---|---|
POSTGRES_USER | inboxes | |
POSTGRES_DB | inboxes | |
RESEND_SYSTEM_API_KEY | — | Only needed for system emails (invites, password resets) in self-hosted mode |
API_PORT | 8080 | Backend listen port inside the container |
TRASH_COLLECTOR_ENABLED | false | Enable automatic purging of expired trash |
EVENT_RETENTION_DAYS | 90 | Days 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_URLDocker resource limits
| Service | Memory | CPU |
|---|---|---|
| postgres | 512M | 0.5 |
| redis | 128M | 0.25 |
| backend | 256M | 0.5 |
| frontend | 256M | 0.5 |
Adjust these in docker-compose.yml if your workload requires more.
Health checks
| Service | Endpoint | Interval | Retries |
|---|---|---|---|
| backend | curl -sf http://localhost:8080/api/health | 30s | 3 |
| frontend | curl -sf http://localhost:3000/ | 30s | 3 |
| postgres | pg_isready | 5s | 5 |
| redis | redis-cli ping | 5s | 5 |
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
- Create a Docker Compose resource from the GitHub repo
- In Advanced Settings, deselect “Strip Prefixes”
- Set the frontend service domain to
mail.yourdomain.com(leave backend blank) - Set all environment variables in the Coolify panel
- Deploy — Coolify handles TLS automatically
Post-deploy setup
- Open
https://mail.yourdomain.com - Sign up — creates your org + admin user (no email verification in self-hosted mode)
- Complete onboarding — enter your Resend API key, select domains, configure aliases
- 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.shsetup.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
| Symptom | Fix |
|---|---|
Compose fails with POSTGRES_PASSWORD error | Set POSTGRES_PASSWORD in .env |
| “ENCRYPTION_KEY must decode to 32 bytes” | Use openssl rand -base64 32 |
| Webhook emails not arriving | Verify PUBLIC_URL is externally reachable |
| Login doesn't persist | Ensure APP_URL matches the browser URL |
| WebSocket won't connect | Ensure DOMAIN matches and TLS is working |
| Backend OOM killed | Increase memory limit in docker-compose.yml |