Skip to content

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.

  • Docker 20.10 or later
  • Docker Compose v2 (docker compose, not docker-compose)
Terminal window
# Verify versions
docker --version
docker compose version
Terminal window
git clone https://github.com/baan-press/baan
cd baan
Terminal window
cp .env.example .env # or create .env from scratch

Edit .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...
Terminal window
docker compose up -d

The first run builds the image and starts the container. This takes 2–3 minutes. Once ready, the startup URL is printed in the logs:

Terminal window
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:

Terminal window
# macOS / Linux — find your IP
ipconfig getifaddr en0 # macOS (Wi-Fi)
ip route get 1 | awk '{print $7; exit}' # Linux

Open your browser at:

http://localhost:3000/baan-admin/setup

Complete the 4-step wizard:

  1. Environment — select Self-hosted
  2. Account — create your admin username and password
  3. Storage — confirm your storage provider
  4. 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:

Terminal window
docker compose exec baan sqlite3 /app/data/app.db \
"SELECT json_extract(value, '$.adminUrlPath') FROM settings WHERE key = 'security';"

Navigate to your secret admin URL:

http://localhost:3000/baan-admin/[your-secret-path]/login

MountPurposeRequired
./data:/app/dataSQLite database (app.db) — users, settings, tokensAlways
./content:/app/contentArticle files for the local filesystem providerLocal filesystem only
./.env:/app/.envSecrets and credentialsAlways

The container exposes a health check endpoint:

Terminal window
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.

Terminal window
git pull
docker compose build --no-cache
docker compose up -d

Database migrations run automatically on the first request after the container starts.

If port 3000 is already in use on your server, set PORT in your .env file:

PORT=8080

The default docker-compose.yml reads this value automatically:

ports:
- "${PORT:-3000}:3000"

No need to edit docker-compose.yml directly.

The default docker-compose.yml already includes restart: unless-stopped. To also start Docker itself on boot:

Terminal window
sudo systemctl enable docker

Add to the service in docker-compose.yml:

deploy:
resources:
limits:
memory: 1G
reservations:
memory: 512M

Container won’t start:

Terminal window
docker compose logs app
docker compose ps

Port already in use:

Terminal window
lsof -i :3000 # find what's using the port

Rebuild after code changes:

Terminal window
docker compose build --no-cache
docker compose up -d