BuildBaseBuildBase

Quick Start

Get a self-hosted BuildBase stack running in minutes with Docker Compose.

Quick Start

This guide gets you from zero to a running stack in minutes. Everything is bundled — MongoDB, Redis, tenant server, client app, and auth portal.

Prerequisites

  • Docker and Docker Compose installed
  • An amd64 or arm64 host — images run natively on both, including Apple Silicon
  • A BuildBase account (console.buildbase.app)

Step 1: Create an Organization & Installation

  1. Log in to console.buildbase.app
  2. Create a new organization with Self-Hosted hosting mode
  3. The setup wizard will guide you to create an Installation
  4. Copy your Installation API Key and Installation ID

Step 2: Create your environment file

Save this as .env.selfhost:

# ═══════════════════════════════════════════════════════════════════
# Self-Hosted — Production Environment
# ═══════════════════════════════════════════════════════════════════

# ── Installation (from BuildBase dashboard) ──────────────────────
INSTALLATION_API_KEY=<INSTALLATION_API_KEY>
INSTALLATION_ID=<INSTALLATION_ID>

# ── Database (REQUIRED for production) ───────────────────────────
# The production compose uses an external MongoDB (Atlas or
# self-managed). Without this, the server falls back to localhost
# inside the container and never becomes ready.
# The quick-start compose bundles MongoDB and ignores this value.
MONGO_CONNECTION_URL=mongodb+srv://user:[email protected]/

# ── Public URLs ──────────────────────────────────────────────────
# For local testing use http://localhost:4100, :4101, :4103
CLIENT_URL=https://app.yourcompany.com
TENANT_SERVER_URL=https://api.yourcompany.com
AUTH_URL=https://auth.yourcompany.com

# ── Ports ────────────────────────────────────────────────────────
CLIENT_PORT=4100
TENANT_SERVER_PORT=4101
AUTH_PORT=4103

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

# ── Optional services ────────────────────────────────────────────
# GOOGLE_AUTH_CLIENT_ID=
# GOOGLE_AUTH_CLIENT_SECRET=
# GOOGLE_STORAGE_ASSETS_BUCKET_NAME=
# MAILGUN_API_KEY=

Generate all secrets at once:

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

Step 3: Save the Docker Compose file

Save this as docker-compose.selfhost.yml:

# Self-Hosted: MongoDB + Redis + Server + Client + Auth
# Save as docker-compose.selfhost.yml
# Usage: docker compose -f docker-compose.selfhost.yml --env-file .env.selfhost up -d

services:
  # ── Infrastructure ──────────────────────────────────────────
  mongodb:
    image: mongo:7.0
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    cap_add:
      - SETUID
      - SETGID
      - DAC_OVERRIDE
    volumes:
      - mongodb_data:/data/db
    networks:
      - db
    healthcheck:
      test: ['CMD', 'mongosh', '--eval', "db.adminCommand('ping')"]
      interval: 10s
      timeout: 5s
      start_period: 20s
      retries: 3
    deploy:
      resources:
        limits:
          memory: 1024M
          cpus: '1.0'
          pids: 256
    logging:
      driver: json-file
      options:
        max-size: '100m'
        max-file: '3'

  redis:
    image: redis:7.4-alpine
    restart: unless-stopped
    read_only: true
    cap_drop:
      - ALL
    cap_add:
      - SETUID
      - SETGID
    command: redis-server --appendonly yes --maxmemory-policy noeviction --maxmemory 256mb --requirepass ${REDIS_PASSWORD:-redispass}
    tmpfs:
      - /tmp
    volumes:
      - redis_data:/data
    networks:
      - db
    healthcheck:
      test: ['CMD', 'redis-cli', '-a', '${REDIS_PASSWORD:-redispass}', 'ping']
      interval: 10s
      timeout: 5s
      retries: 3
    deploy:
      resources:
        limits:
          memory: 256M
          cpus: '0.5'
          pids: 64
    logging:
      driver: json-file
      options:
        max-size: '50m'
        max-file: '3'

  # ── Backend ─────────────────────────────────────────────────
  tenant-server:
    image: buildbaseapp/tenant-server:latest
    restart: unless-stopped
    read_only: true
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    tmpfs:
      - /tmp
      - /var/log
    ports:
      - '${TENANT_SERVER_PORT:-4101}:3000'
    environment:
      - NODE_ENV=production
      - PORT=3000
      # Keep the Node heap below the 1024M container limit — the image
      # default is sized for larger hosts and would get OOM-killed here.
      - NODE_OPTIONS=--max-old-space-size=768
      - SERVER_URL=${TENANT_SERVER_URL:-http://localhost:4101}
      - APPLICATION_URL=${CLIENT_URL:-http://localhost:4100}
      - AUTH_SERVER_URL=${AUTH_URL:-http://localhost:4103}
      - MONGO_CONNECTION_URL=mongodb://mongodb:27017/
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      - REDIS_PASSWORD=${REDIS_PASSWORD:-redispass}
      - INSTALLATION_API_KEY=${INSTALLATION_API_KEY:-}
      - INSTALLATION_ID=${INSTALLATION_ID:-}
      - JWT_PASS=${JWT_PASS:-localdev_jwt_secret_do_not_use_in_production}
      - OAUTH2_SECRET=${OAUTH2_SECRET:-localdev_oauth2_secret_do_not_use_in_production}
      - DB_ENCRYPTION_KEY=${DB_ENCRYPTION_KEY:-localdev_db_encryption_key_do_not_use_in_production}
      - SECRET_KEY=${SECRET_KEY:-localdev_secret_key_do_not_use_in_production}
      - CORS_WHITELISTED_DOMAINS=${CLIENT_URL:-http://localhost:4100},${AUTH_URL:-http://localhost:4103}
    depends_on:
      mongodb:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - db
      - app
    healthcheck:
      test: ['CMD', 'wget', '-qO-', 'http://127.0.0.1:3000/api/ready']
      interval: 15s
      timeout: 5s
      start_period: 45s
      retries: 3
    deploy:
      resources:
        limits:
          memory: 1024M
          cpus: '1.0'
          pids: 256
    logging:
      driver: json-file
      options:
        max-size: '100m'
        max-file: '5'

  # ── Frontend ────────────────────────────────────────────────
  client:
    image: buildbaseapp/client:latest
    restart: unless-stopped
    # NOTE: read_only must NOT be set on client or auth. Their entrypoints
    # rewrite __NEXT_PUBLIC_*__ URL placeholders in the JS bundles at
    # startup; a read-only filesystem makes that rewrite fail silently and
    # the app calls the literal placeholder string instead of your URL.
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    tmpfs:
      - /tmp
      - /var/cache/nginx
      - /var/run
    ports:
      - '${CLIENT_PORT:-4100}:3000'
    environment:
      - NEXT_PUBLIC_SERVER_URL=${TENANT_SERVER_URL:-http://localhost:4101}
      - NEXT_PUBLIC_DEFAULT_TENANT_SERVER_URL=${TENANT_SERVER_URL:-http://localhost:4101}
      - NEXT_PUBLIC_INSTALLATION_ID=${INSTALLATION_ID:-}
    networks:
      - app
    healthcheck:
      test: ['CMD', 'wget', '-qO-', 'http://127.0.0.1:3000/']
      interval: 15s
      timeout: 5s
      start_period: 30s
      retries: 3
    deploy:
      resources:
        limits:
          memory: 256M
          cpus: '0.5'
          pids: 128
    logging:
      driver: json-file
      options:
        max-size: '50m'
        max-file: '3'

  auth:
    image: buildbaseapp/auth:latest
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    tmpfs:
      - /tmp
      - /var/cache/nginx:uid=1001,gid=1001
      - /var/run:uid=1001,gid=1001
    ports:
      - '${AUTH_PORT:-4103}:3000'
    environment:
      - NEXT_PUBLIC_SERVER_URL=${TENANT_SERVER_URL:-http://localhost:4101}
    networks:
      - app
    healthcheck:
      test: ['CMD', 'wget', '-qO-', 'http://127.0.0.1:3000/health']
      interval: 15s
      timeout: 5s
      start_period: 30s
      retries: 3
    deploy:
      resources:
        limits:
          memory: 256M
          cpus: '0.5'
          pids: 128
    logging:
      driver: json-file
      options:
        max-size: '50m'
        max-file: '3'

volumes:
  mongodb_data:
  redis_data:

# Network segmentation: databases isolated from frontends.
# Only tenant-server bridges both networks.
networks:
  db:
    driver: bridge
    internal: true
  app:
    driver: bridge

Step 4: Start the stack

docker compose -f docker-compose.selfhost.yml --env-file .env.selfhost up -d

Step 5: Verify

# Check all services are running
docker compose -f docker-compose.selfhost.yml ps

# Server should return {"ready": true}
curl http://localhost:4101/api/ready

Step 6: Connect

Go back to the setup wizard in the dashboard, enter your server URL (http://localhost:4101 for local testing), click Test Connection, then Complete Setup.

Your services are now running at:

What's Next