Complete guide for running OpenStatus with Docker
# 1. Copy environment file
cp .env.docker.example .env.docker
# 2. Configure required variables (see Configuration section)
vim .env.docker
# 3. Build and start services (the db-migrate one-shot applies migrations before apps start)
export DOCKER_BUILDKIT=1
docker compose up -d
# 4. Check service health
docker compose ps
# 5. (Optional) Seed database with test data (uses libsql's host-published
# port — seed refuses non-localhost databases by design)
docker run --rm --network host \
-e DATABASE_URL=http://localhost:8080 \
--entrypoint deno openstatus/db-migrate:latest task seed
# 6. (Optional) Deploy Tinybird local - requires tb CLI
cd packages/tinybird
tb --local deploy
# 7. Access the application
open http://localhost:3002 # Dashboard
open http://localhost:3003 # Status Page Theme Explorer
# Note: Status pages are accessed via subdomain/slug (e.g., http://localhost:3003/status)# Remove stopped containers
docker compose down
# Remove volumes
docker compose down -v
# Clean build cache
docker builder prune| Service | Port | Purpose |
|---|---|---|
| db-migrate | — | One-shot DB migrations (exits after applying) |
| workflows | 3000 | Background jobs |
| server | 3001 | API backend (tRPC) |
| dashboard | 3002 | Admin interface |
| status-page | 3003 | Public status pages |
| private-location | 8081 | Monitoring agent |
| libsql | 8080 | Database (HTTP) |
| libsql | 5001 | Database (gRPC) |
| tinybird-local | 7181 | Analytics |
┌─────────────┐ ┌─────────────┐
│ Dashboard │────▶│ Server │
│ (Next.js) │ │ (Bun) │
└─────────────┘ └─────────────┘
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Status Page │ │ Workflows │
│ (Next.js) │ │ (Bun) │
└─────────────┘ └─────────────┘
│ │
└────────┬───────────┘
▼
┌─────────────┐
│ LibSQL │
│ (Database) │
└─────────────┘
Migrations run automatically when you start the stack with docker compose up -d: the db-migrate one-shot service applies all drizzle migrations and exits before the app services boot (they wait on its service_completed_successfully condition). Re-running docker compose up -d is safe — drizzle records applied migrations in the __drizzle_migrations table and skips them on subsequent runs.
Verifying migrations:
docker compose logs db-migrate
# Should show:
# openstatus-db-migrate | Running migrations
# openstatus-db-migrate | Migrated successfullyManual migration:
If you need to re-run migrations or troubleshoot:
docker compose run --rm db-migrateTroubleshooting no such table errors:
A SQLITE_UNKNOWN: no such table error from dashboard or status-page means the database is unmigrated. Check docker compose logs db-migrate for the failure — common causes are a wrong DATABASE_AUTH_TOKEN in .env.docker or libsql not being healthy. (DATABASE_URL is pinned to http://libsql:8080 by the compose files, so it cannot be misconfigured via .env.docker.) Fix the cause, then run docker compose up -d again.
Note: Migrations run automatically, but seeding does not. You must manually seed the database if you want test data.
After migrations complete, seed the database with sample data:
# Uses libsql's host-published port 8080 — seed refuses non-localhost
# databases by design, so it cannot run over the compose-internal network
docker run --rm --network host \
-e DATABASE_URL=http://localhost:8080 \
--entrypoint deno openstatus/db-migrate:latest task seedThis creates:
- 3 workspaces (
love-openstatus,test2,test3) - Sample monitors and 2 status pages (slugs
statusandacme) - Test user account:
ping@openstatus.dev - Sample incidents, status reports, and maintenance windows
Verifying seeded data:
# Check table counts via libsql HTTP API
curl -s http://localhost:8080/ -H "Content-Type: application/json" \
-d '{"statements":["SELECT COUNT(*) FROM page"]}' | jq -r '.[0].results.rows[0][0]'
# Should output: 2Accessing Seeded Data:
After seeding, you can access the test data:
Dashboard:
- Navigate to http://localhost:3002/login
- Use magic link authentication with email:
ping@openstatus.dev - Check your console/logs for the magic link (with
SELF_HOST=truein.env.docker) - After logging in, you'll see the
love-openstatusworkspace with all seeded monitors and status page
Status Page:
- The seeded status page has slug
status - Access it via subdomain routing: http://status.localhost:3003
- Or view theme explorer at: http://localhost:3003
If you use a different email address, the system will create a new empty workspace for you instead of showing the seeded data. To access seeded data with a different account, you must add your user to the seeded workspace using SQL:
# First, find your user_id
curl -X POST http://localhost:8080/ -H "Content-Type: application/json" \
-d '{"statements":["SELECT id, email FROM user"]}'
# Then add association (replace USER_ID with your id)
curl -X POST http://localhost:8080/ -H "Content-Type: application/json" \
-d '{"statements":["INSERT INTO users_to_workspaces (user_id, workspace_id, role) VALUES (USER_ID, 1, '\''owner'\'')"]}'Tinybird is used for analytics and monitoring metrics. The application will work without it, but analytics features will be unavailable.
If you want to enable analytics, you can:
- Use Tinybird Cloud and configure
TINY_BIRD_API_KEYin.env.docker - Manually configure Tinybird Local (requires additional setup beyond this guide)
Edit .env.docker and set:
# Authentication
AUTH_SECRET=your-secret-here
# Database
DATABASE_URL=http://libsql:8080
DATABASE_AUTH_TOKEN=basic:token
# Email
RESEND_API_KEY=testConfigure these for full functionality:
# Redis
UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=
# Analytics
TINY_BIRD_API_KEY=
# OAuth providers
AUTH_GITHUB_ID=
AUTH_GITHUB_SECRET=
AUTH_GOOGLE_ID=
AUTH_GOOGLE_SECRET=See .env.docker.example for complete list.
# View logs
docker compose logs -f [service-name]
# Restart service
docker compose restart [service-name]
# Rebuild after code changes
docker compose up -d --build [service-name]
# Stop all services
docker compose down
# Reset database (removes all data)
docker compose down -v
docker compose up -d
# Migrations run automatically on startupMagic Link:
Set SELF_HOST=true in .env.docker to enable email-based magic link authentication. This allows users to sign in without configuring OAuth providers.
OAuth Providers:
Configure GitHub/Google OAuth credentials in .env.docker and set up callback URLs:
- GitHub:
http://localhost:3002/api/auth/callback/github - Google:
http://localhost:3002/api/auth/callback/google
Via Dashboard (Recommended):
- Login to http://localhost:3002
- Create a workspace
- Create a status page with a slug
- Access at http://localhost:3003/[slug]
Via Database (Testing):
# Insert test data
curl -s http://localhost:8080/v2/pipeline \
-H 'Content-Type: application/json' \
--data-raw '{
"requests":[{
"type":"execute",
"stmt":{
"sql":"INSERT INTO workspace (id, slug, name) VALUES (1, '\''test'\'', '\''Test Workspace'\'');"
}
}]
}'Add to docker-compose.yaml:
services:
dashboard:
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
reservations:
cpus: '0.5'
memory: 512MAll services have automated health checks:
# View health status
docker compose ps
# Inspect specific service
docker inspect openstatus-dashboard --format='{{.State.Health.Status}}'- Documentation: docs.openstatus.dev
- Discord: openstatus.dev/discord
- GitHub Issues: github.com/openstatusHQ/openstatus/issues