Deploy with Docker
Docker is the recommended way to self-host Baan. The official image is a multi-stage build based on node:24-alpine, runs as a non-root user (UID 1001), and includes a health check endpoint. Docker Compose handles volumes, environment variables, and restart policies in a single file.
Prerequisites
Section titled “Prerequisites”- Docker 20.10 or later
- Docker Compose v2 (
docker compose, notdocker-compose)
# Verify versionsdocker --versiondocker compose versionStep 1 — Clone the Repository
Section titled “Step 1 — Clone the Repository”git clone https://github.com/baan-press/baancd baanStep 2 — Create Your Environment File
Section titled “Step 2 — Create Your Environment File”cp .env.example .env # or create .env from scratchEdit .env with the values for your setup:
# ── Storage ──────────────────────────────────────────────# The storage provider type is selected in the setup wizard (Step 4) and saved to the database.# Only add credentials for your chosen provider here.
# GCS:# GOOGLE_CLOUD_PROJECT_ID=your-project-id# GCS_BUCKET_NAME=your-bucket-name# GOOGLE_CLOUD_CREDENTIALS_BASE64=base64-encoded-service-account-json
# S3:# AWS_REGION=ap-northeast-1# AWS_S3_BUCKET_NAME=your-bucket-name# AWS_ACCESS_KEY_ID=your-key# AWS_SECRET_ACCESS_KEY=your-secret
# R2:# CF_ACCOUNT_ID=your-account-id# CF_R2_BUCKET_NAME=your-bucket# CF_R2_ACCESS_KEY_ID=your-key# CF_R2_SECRET_ACCESS_KEY=your-secret
# ── AI (optional) ────────────────────────────────────────# ANTHROPIC_API_KEY=sk-ant-...# OPENAI_API_KEY=sk-...# GEMINI_API_KEY=AI...Step 3 — Start the Container
Section titled “Step 3 — Start the Container”docker compose up -dThe first run builds the image and starts the container. This takes 2–3 minutes. Once ready, the startup URL is printed in the logs:
docker compose logs -f┌─────────────────────────────────────────────────────┐│ Baan is ready ││ ││ http://localhost:3000 ││ http://<your-server-ip>:3000 ││ ││ First time? Open the setup wizard: ││ http://localhost:3000/baan-admin/setup │└─────────────────────────────────────────────────────┘Replace <your-server-ip> with your machine’s IP address to access from other devices on the same network:
# macOS / Linux — find your IPipconfig getifaddr en0 # macOS (Wi-Fi)ip route get 1 | awk '{print $7; exit}' # LinuxStep 4 — Run the Setup Wizard
Section titled “Step 4 — Run the Setup Wizard”Open your browser at:
http://localhost:3000/baan-admin/setupComplete the 4-step wizard:
- Environment — select Self-hosted
- Account — create your admin username and password
- Storage — confirm your storage provider
- Done — the wizard saves your admin secret path and user account directly to
data/app.db(your mounted volume) and redirects you to the login page
No restart and no .env edit are required after setup — application settings live in the database.
If you ever lose the admin URL, recover it from the DB:
docker compose exec baan sqlite3 /app/data/app.db \ "SELECT json_extract(value, '$.adminUrlPath') FROM settings WHERE key = 'security';"Step 5 — Log In
Section titled “Step 5 — Log In”Navigate to your secret admin URL:
http://localhost:3000/baan-admin/[your-secret-path]/loginVolume Mounts
Section titled “Volume Mounts”| Mount | Purpose | Required |
|---|---|---|
./data:/app/data | SQLite database (app.db) — users, settings, tokens | Always |
./content:/app/content | Article files for the local filesystem provider | Local filesystem only |
./.env:/app/.env | Secrets and credentials | Always |
Health Check
Section titled “Health Check”The container exposes a health check endpoint:
curl http://localhost:3000/api/health# → { "status": "healthy", "timestamp": "2026-03-18T12:16:49.909Z", "uptime": 1169.252034167 }Docker Compose is configured with a 30-second interval, 10-second timeout, 3 retries, and 40-second start grace period. Use this endpoint with your load balancer or monitoring tool.
Updating Baan
Section titled “Updating Baan”git pulldocker compose build --no-cachedocker compose up -dDatabase migrations run automatically on the first request after the container starts.
Production Tips
Section titled “Production Tips”Run on a Different Port
Section titled “Run on a Different Port”If port 3000 is already in use on your server, set PORT in your .env file:
PORT=8080The default docker-compose.yml reads this value automatically:
ports: - "${PORT:-3000}:3000"No need to edit docker-compose.yml directly.
Auto-restart on Reboot
Section titled “Auto-restart on Reboot”The default docker-compose.yml already includes restart: unless-stopped. To also start Docker itself on boot:
sudo systemctl enable dockerLimit Memory Usage
Section titled “Limit Memory Usage”Add to the service in docker-compose.yml:
deploy: resources: limits: memory: 1G reservations: memory: 512MTroubleshooting
Section titled “Troubleshooting”Container won’t start:
docker compose logs appdocker compose psPort already in use:
lsof -i :3000 # find what's using the portRebuild after code changes:
docker compose build --no-cachedocker compose up -d