Deployment
HoMeeSuite is distributed as a set of Docker images and orchestrated with Docker Compose. This guide covers everything a system administrator needs to get a production instance running.
Prerequisites
- Docker 24+ and Docker Compose v2
- A server with at least 1 GB RAM and 10 GB disk space
- A domain name pointed at the server (required for TLS and email links)
- An SMTP server or relay (optional, but required for email notifications)
Quick start
1. Create the working directory
mkdir homeesuite && cd homeesuite
2. Create the docker-compose.yml
services:
nextjs:
image: ghcr.io/your-org/homeesuite:latest # replace with your image tag
restart: unless-stopped
environment:
DATABASE_URL: postgresql://homeesuite:${POSTGRES_PASSWORD}@postgres:5432/homeesuite
REDIS_URL: redis://redis:6379
BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET}
BETTER_AUTH_URL: https://your-domain.example.com
ENCRYPTION_KEY: ${ENCRYPTION_KEY}
NODE_ENV: production
volumes:
- uploads_data:/app/public/uploads
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:3000/api/health || exit 1"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
bullmq-worker:
image: ghcr.io/your-org/homeesuite:latest
restart: unless-stopped
command: node worker.js
environment:
DATABASE_URL: postgresql://homeesuite:${POSTGRES_PASSWORD}@postgres:5432/homeesuite
REDIS_URL: redis://redis:6379
BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET}
ENCRYPTION_KEY: ${ENCRYPTION_KEY}
NODE_ENV: production
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: homeesuite
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: homeesuite
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U homeesuite"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 5
volumes:
postgres_data:
redis_data:
uploads_data:
3. Create the .env file
# Generate BETTER_AUTH_SECRET:
# node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
BETTER_AUTH_SECRET=<32-char-random-hex>
# Generate ENCRYPTION_KEY the same way:
ENCRYPTION_KEY=<32-char-random-hex>
POSTGRES_PASSWORD=<strong-random-password>
Keep this file outside version control. Both secret values must be at least 32 characters of random data.
4. Start the services
docker compose up -d
5. Run database migrations
Migrations run automatically on first startup as part of the Next.js build entrypoint. Wait for the nextjs container to reach healthy status:
docker compose ps
6. Complete first-run setup
Open https://your-domain.example.com/setup in a browser. The setup wizard only appears when no Superadmin account exists yet. Create the first Superadmin user here — this is the account you will use to manage users, groups, and global settings.
Once saved, the /setup route redirects to login permanently.
Environment variables reference
| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
Yes | PostgreSQL connection string |
REDIS_URL |
Yes | Redis connection string for BullMQ |
BETTER_AUTH_SECRET |
Yes | Secret key for session signing (min 32 chars) |
BETTER_AUTH_URL |
Yes | Public URL of the application (used in email links) |
BETTER_AUTH_TRUSTED_ORIGINS |
No | Comma-separated list of additional trusted origins, e.g. https://homee.rocks,https://holtermeeting.de — needed when the same instance is reachable under multiple domains |
ENCRYPTION_KEY |
Yes | Key for encrypting sensitive fields (min 32 chars) |
SMTP_HOST |
No | SMTP server hostname — can also be set from Admin → Settings |
SMTP_PORT |
No | SMTP port (default: 587) |
SMTP_USER |
No | SMTP username |
SMTP_PASS |
No | SMTP password |
SMTP_FROM |
No | Sender address for outgoing email |
NODE_ENV |
No | Set to production in production |
SMTP settings can alternatively be configured through the admin UI at Admin → Settings → Communication after first login — the environment variables are a convenience for scripted deployments.
Serving multiple domains
BETTER_AUTH_URL is always trusted and should point at your primary domain. If the same instance must also accept logins from additional domains (for example holtermeeting.de and holter-meeting.de alongside homee.rocks), list them in BETTER_AUTH_TRUSTED_ORIGINS:
BETTER_AUTH_TRUSTED_ORIGINS=https://homee.rocks,https://holtermeeting.de,https://holter-meeting.de
Set this on both the nextjs and bullmq-worker services. Unset, only BETTER_AUTH_URL is trusted.
Updating HoMeeSuite
Pull the new image and recreate the containers:
docker compose pull
docker compose up -d
Migrations run automatically on startup. No manual migration step is needed.
Persistent data
All state lives in named Docker volumes:
| Volume | Contents |
|---|---|
postgres_data |
All application data — back this up |
redis_data |
BullMQ job queues — safe to lose |
uploads_data |
Event cover photos and file uploads |
Back up postgres_data regularly with pg_dump:
docker compose exec postgres pg_dump -U homeesuite homeesuite > backup-$(date +%F).sql
Reverse proxy / TLS
The nextjs container listens on port 3000. Put a reverse proxy (nginx, Caddy, Traefik) in front of it for TLS termination. A minimal nginx snippet:
server {
listen 443 ssl;
server_name your-domain.example.com;
ssl_certificate /etc/letsencrypt/live/your-domain.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.example.com/privkey.pem;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Health check endpoint
GET /api/health returns 200 OK when the app is ready. Use this for load balancer and monitoring probes.
Troubleshooting
Container exits immediately — check logs with docker compose logs nextjs. A missing BETTER_AUTH_SECRET or DATABASE_URL is the most common cause.
Database migration fails — the postgres container may not have been healthy yet. Run docker compose restart nextjs once the database is up.
Email not sending — configure SMTP under Admin → Settings → Communication and use the test-send button to verify delivery.
/setup redirects to login — a Superadmin already exists. Use the login page or reset credentials directly in the database if you are locked out.