# Production Deployment

Deploy BuildBase for production with Nginx, SSL, replicas, and external MongoDB.

## Production Deployment

This guide covers a production-ready deployment with Nginx load balancer, multiple app replicas, external MongoDB, SSL, and health monitoring.

### Prerequisites

- Linux (Ubuntu 20.04+ recommended), 2 GB+ RAM, 2 vCPU+
- Docker Engine 20+ and Docker Compose v2
- MongoDB 6.0+ (managed like Atlas, or self-hosted)
- Domain name pointed to your server's public IP
- SSL certificate (Let's Encrypt or custom)

### Directory Structure

```
your-server/
  .env                    # Environment configuration
  docker-compose.yml      # Service definitions
  nginx-lb.conf           # Nginx load balancer config
```

### Step 1: Environment File

Create a `.env` file with your configuration. Generate secrets with `openssl rand -hex 32`.

```bash
# ═══════════════════════════════════════════════════════════════════
# Tenant Server — Production Environment
# Organization: Your Organization
# ═══════════════════════════════════════════════════════════════════

NODE_ENV=production
PORT=3000

# ── URLs (replace with your actual domain) ────────────────────────
SERVER_URL=https://api.yourcompany.com
APPLICATION_URL=https://app.yourcompany.com
CENTRAL_SERVER_URL=YOUR_CENTRAL_SERVER_URL

# ── Organization ──────────────────────────────────────────────────
ORG_IDS=YOUR_ORG_ID

# ── Database ──────────────────────────────────────────────────────
MONGO_CONNECTION_URL=mongodb://username:password@your-mongo-host:27017/tenant-db?authSource=admin

# ── Redis ─────────────────────────────────────────────────────────
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=

# ── Security (REQUIRED — run: openssl rand -hex 32) ──────────────
JWT_PASS=
DB_ENCRYPTION_KEY=
SECRET_KEY=
OAUTH2_SECRET=
INTERNAL_API_KEY=

# ── CORS ──────────────────────────────────────────────────────────
CORS_WHITELISTED_DOMAINS=YOUR_CENTRAL_SERVER_URL,https://app.yourcompany.com

# ── Optional services ─────────────────────────────────────────────
# Google OAuth client (for Google vendor + Gmail sender)
# GOOGLE_AUTH_CLIENT_ID=
# GOOGLE_AUTH_CLIENT_SECRET=
# GOOGLE_STORAGE_ASSETS_BUCKET_NAME=
# MAILGUN_API_KEY=
#
# Note: Stripe, LinkedIn, and Google OAuth redirect credentials
# are stored per-organization in the database, not as env vars.
```

Generate all secrets at once:

```bash
for i in JWT_PASS DB_ENCRYPTION_KEY SECRET_KEY OAUTH2_SECRET INTERNAL_API_KEY; do echo "$i=$(openssl rand -hex 32)"; done
```

### Step 2: Docker Compose

```yaml
# Production: external MongoDB, Nginx LB, 2 replicas
# Requires: .env file + nginx-lb.conf in same directory

services:
  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes --maxmemory-policy noeviction --maxmemory 256mb
    volumes:
      - redis_data:/data
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - internal

  app:
    image: buildbaseapp/tenant-server:latest
    platform: linux/amd64
    env_file: .env
    environment:
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      - NODE_OPTIONS=--max-old-space-size=768
    depends_on:
      redis:
        condition: service_healthy
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/api/ready"]
      interval: 15s
      timeout: 5s
      start_period: 30s
      retries: 3
    deploy:
      replicas: 2
      resources:
        limits:
          memory: 1024M
      update_config:
        parallelism: 1
        delay: 10s
        order: start-first
    labels:
      - "autoheal=true"
    networks:
      - internal

  nginx:
    image: nginx:alpine
    ports:
      - "3080:80"
    volumes:
      - ./nginx-lb.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - app
    restart: unless-stopped
    networks:
      - internal

  autoheal:
    image: willfarrell/autoheal
    environment:
      - AUTOHEAL_CONTAINER_LABEL=autoheal
      - AUTOHEAL_INTERVAL=30
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    restart: unless-stopped

volumes:
  redis_data:

networks:
  internal:
    driver: bridge
```

### Step 3: Nginx Configuration

Save as `nginx-lb.conf` in the same directory.

```nginx
events {
    worker_connections 1024;
}

http {
    upstream app_servers {
        server app:3000;
    }

    server {
        listen 80;
        client_max_body_size 500M;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        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;

        location /ready  { proxy_pass http://app_servers/api/ready; }
        location /health { proxy_pass http://app_servers/api/health; }

        location / {
            proxy_pass http://app_servers;
            proxy_next_upstream error timeout http_502 http_503;
            proxy_next_upstream_tries 2;
            proxy_buffering off;
        }
    }
}
```

### Step 4: Deploy

```bash
docker compose up -d
```

### Step 5: SSL with Let's Encrypt

```bash
sudo certbot --nginx -d api.yourcompany.com
```

Or use [Caddy](https://caddyserver.com/) for automatic HTTPS with zero configuration.

### Step 6: Connect

Enter your public URL (e.g. `https://api.yourcompany.com`) in the setup wizard and complete the setup.

## Health Checks

| Endpoint | Purpose |
|---|---|
| `GET /api/ready` | Readiness probe — returns `{"ready": true}` when DB and Redis are connected |
| `GET /api/health` | Full health check — DB status, Redis latency, worker status |

## Updating

Pull the latest image and restart:

```bash
docker compose pull app
docker compose up -d
```

The rolling update config ensures zero downtime — new containers start before old ones stop.
