Why self-host
Full control over data. Same platform features as shared cloud.
Data sovereignty
All data stays on your infrastructure. Nothing leaves your network. Full control over data residency and compliance.
Your security policies
Use your own firewall rules, VPN, and network policies. No shared infrastructure with other tenants.
Your database
Run your own MongoDB instance. Control backups, replication, disaster recovery, and data retention.
Central licensing only
BuildBase manages licensing and org management via the central server. Client, server, and auth portal run on your infrastructure. BuildBase never accesses your data.
One command setup
Public Docker image on Docker Hub. Copy the compose file, run one command, connect. Running in 30 seconds.
Run anywhere
AWS, GCP, Azure, DigitalOcean, Hetzner, on-premises, bare metal — anywhere Docker runs. No vendor lock-in.
Architecture
Split architecture. Data stays on your servers. Dashboard connects over HTTPS.
Your Infrastructure
Hosted by you — full control
• Tenant Server — users, emails, workflows, billing
• Client App — web dashboard for your team
• Auth Portal — login, signup, password reset
• MongoDB — your data, your backups
• Redis — sessions, cache, queues
Central Server Only
Licensing and org management — nothing else
• Org Management — create and manage organizations
• Admin Auth — Google OAuth for org admins
• ES256 Keys — secure token signing
• Installation Keys — per-instance API keys
• Licensing — installation verification
BuildBase never accesses your application data. The central server handles only licensing, org management, and authentication tokens.
All modules included
Every platform module works identically in self-hosted mode. Same SDK, same API, same dashboard.
Every feature available in shared cloud works identically when self-hosted.
Deploy in three steps
Sign up to a running server in under 5 minutes.
Create org and installation
Sign up and select "Self-Hosted" when creating your organization. The setup wizard generates an Installation with a unique API key, a docker-compose file, and an env template.
Deploy with Docker
Copy the compose file and .env template to your server. Run docker compose up -d to start the tenant server, client app, auth portal, MongoDB, and Redis.
Connect and manage
Enter your server URL in the dashboard and verify the connection. Manage users, billing, emails, and workflows from the central dashboard.
One file. One command.
Server, client, auth, MongoDB, and Redis — all included. Copy, paste, run.
# 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: bridgeShared Cloud vs Self-Hosted
Same platform, same features. Different hosting.
Shared Cloud | Self-Hosted | |
|---|---|---|
| All platform modules | ||
| User & audience data | Isolated per org | Your servers |
| Database | Per-org database | Your MongoDB |
| Redis / caching | Pre-configured | Your Redis |
| Dashboard UI | Pre-configured | Hosted by you |
| SDK & API | Same SDK | Same SDK |
| Authentication tokens | Central server | ES256 signed, verified locally |
| Emails & campaigns | Pre-configured | Your infrastructure |
| Workflows & jobs | Pre-configured | Your workers |
| File storage | Cloud storage | Your storage |
| SSL & networking | Pre-configured | You control |
| Updates | Automatic | docker compose pull |
| Setup time | Instant | ~5 minutes |
Security
Built for compliance-sensitive deployments.
- ES256 (ECDSA P-256) asymmetric token signing — private key never leaves central server
- Per-organization key pairs with key rotation support
- Audience-locked tokens with 2-minute expiry and replay protection (jti)
- Per-installation API keys with HMAC-SHA256 request signing
- Installation-scoped access — each instance only sees its assigned orgs
- All sensitive database fields encrypted at rest (AES-256-CBC)
- No user passwords or PII flow through the central server
- Central server URL pinned via read-only lock file in Docker images
- Read-only tenant-server filesystem prevents runtime code tampering
- Brute-force protection with rate limiting on all auth endpoints
- SSRF protection on outbound requests (blocks private IPs, cloud metadata)
- Non-root Docker container with dumb-init signal handling
- Network segmentation — databases on internal-only network, unreachable from frontends
- All capabilities dropped (cap_drop: ALL) with minimal cap_add where required
- PID limits on all containers to prevent fork bomb attacks
- CPU and memory limits on all containers to prevent resource exhaustion
- Docker socket mounted read-only for autoheal (production template)
- Privilege escalation blocked (no-new-privileges) on every container
Docker images
Three official images on Docker Hub. No source code, no secrets, no source maps.
| Image | Description | Port |
|---|---|---|
| buildbaseapp/tenant-server | Backend API server | 3000 |
| buildbaseapp/client | Web dashboard (Next.js) | 3000 |
| buildbaseapp/auth | Auth portal (Next.js) | 3000 |
Platform: linux/amd64, linux/arm64 · Base: Node.js 22 Alpine · Non-root user · Graceful shutdown via dumb-init
Requirements
Minimal requirements. The quick-start compose includes MongoDB and Redis.
Frequently Asked Questions
What are the requirements to self-host?
A Linux server with 2 GB RAM, Docker, and a public IP or domain. MongoDB and Redis are included in the quick-start compose file.
Does the dashboard still work?
Yes. The dashboard at console.buildbase.app connects to your self-hosted server over HTTPS. Your data stays on your server.
How does authentication work between central and my server?
The central server signs an ES256 token with your org's private key. Your server verifies it using the public key. No secrets travel over the network.
Can I use localhost for testing?
Yes. The setup wizard verifies connections from your browser, so localhost:3000, 192.168.x.x, and LAN IPs all work. Use a public domain with HTTPS for production.
How do I update to a new version?
Run `docker compose pull` and `docker compose up -d`. The production compose file with 2 replicas supports zero-downtime updates.
Can I run multiple organizations on one server?
Yes. One installation serves multiple organizations. They share the same server, database, and Redis while data remains isolated in separate databases.
Is the Docker image open source?
The image is published on Docker Hub at buildbaseapp/tenant-server. It contains compiled JavaScript only — no source code or source maps.
How do backups work?
You manage your own MongoDB backups. Use mongodump, MongoDB Atlas automated backups, or any preferred tool. Daily backups with point-in-time recovery are recommended.
Can I switch from cloud to self-hosted?
Yes. Create a self-hosted organization and migrate your data. Both shared and self-hosted orgs can run under the same account.
Is there an SLA for self-hosted?
Self-hosted uptime depends on your infrastructure. The dashboard has its own SLA. For production, use the production compose file with 2 replicas, Nginx, and auto-heal.