Status page with uptime monitoring & API monitoring as code ๐ซ
docker pull openstatus/db-migrate
docker run -d --name openstatus -p 8080:8080 --restart always openstatus/db-migrate:latest
podman run -d --name openstatus -p 8080:8080 --restart always openstatus/db-migrate:latest
skopeo inspect docker://docker.io/openstatus/db-migrate:latest
oras pull openstatus/db-migrate:latest
docker compose up -d
networks:
openstatus:
driver: bridge
name: openstatus
volumes:
libsql-data:
name: openstatus-libsql-data
workflows-data:
name: openstatus-workflows-data
tinybird-clickhouse-data:
name: openstatus-tinybird-clickhouse-data
tinybird-redis-data:
name: openstatus-tinybird-redis-data
services:
# External services
libsql:
container_name: openstatus-libsql
image: ghcr.io/tursodatabase/libsql-server:latest
networks:
- openstatus
ports:
- "8080:8080"
- "5001:5001"
volumes:
- libsql-data:/var/lib/sqld
environment:
- SQLD_NODE=primary
# Increase limits to handle multiple services without connection pooling
- SQLD_MAX_CONCURRENT_CONNECTIONS=512
- SQLD_MAX_CONCURRENT_REQUESTS=1024
healthcheck:
test:
[
"CMD-SHELL",
'perl -e ''use IO::Socket::INET; exit(IO::Socket::INET->new(PeerAddr=>"127.0.0.1:8080",Timeout=>1) ? 0 : 1);''',
]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
restart: unless-stopped
memswap_limit: 512M
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 256M
tinybird-local:
container_name: openstatus-tinybird
image: tinybirdco/tinybird-local:latest
networks:
- openstatus
ports:
- "7181:7181"
environment:
- COMPATIBILITY_MODE=1
volumes:
- tinybird-clickhouse-data:/var/lib/clickhouse
- tinybird-redis-data:/redis-data
healthcheck:
test: ["CMD", "curl", "-f", "-sS", "-o", "/dev/null", "http://localhost:7181/tokens"]
interval: 15s
timeout: 5s
retries: 5
start_period: 20s
restart: unless-stopped
# Internal Services
# Idempotent: drizzle skips already-applied migrations on every up
db-migrate:
container_name: openstatus-db-migrate
build:
context: .
dockerfile: packages/db/Dockerfile
image: openstatus/db-migrate:latest
# rebuild on every up so a stale image never skips new migrations
pull_policy: build
networks:
- openstatus
env_file:
- .env.docker
environment:
- DATABASE_URL=http://libsql:8080
depends_on:
libsql:
condition: service_healthy
# retries absorb transient DB races; still settles to failure for the gates
restart: on-failure:3
workflows:
container_name: openstatus-workflows
build:
context: .
dockerfile: apps/workflows/Dockerfile
image: openstatus/workflows:latest
networks:
- openstatus
ports:
- "3000:3000"
volumes:
- workflows-data:/app/data
env_file:
- .env.docker
environment:
- DATABASE_URL=http://libsql:8080
- PORT=3000
depends_on:
db-migrate:
condition: service_completed_successfully
libsql:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/ping"]
interval: 15s
timeout: 10s
retries: 3
start_period: 30s
restart: unless-stopped
memswap_limit: 512M
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 256M
server:
container_name: openstatus-server
build:
context: .
dockerfile: apps/server/Dockerfile
image: openstatus/server:latest
networks:
- openstatus
ports:
- "3001:3000"
env_file:
- .env.docker
environment:
- DATABASE_URL=http://libsql:8080
- PORT=3000
depends_on:
db-migrate:
condition: service_completed_successfully
workflows:
condition: service_healthy
libsql:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/ping"]
interval: 15s
timeout: 10s
retries: 3
start_period: 30s
restart: unless-stopped
memswap_limit: 512M
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 256M
private-location:
container_name: openstatus-private-location
build:
context: apps/private-location
dockerfile: Dockerfile
image: openstatus/private-location:latest
networks:
- openstatus
ports:
- "8081:8080"
env_file:
- .env.docker
environment:
- DB_URL=http://libsql:8080
- TINYBIRD_URL=http://tinybird-local:7181
- WORKFLOWS_URL=http://workflows:3000
- GIN_MODE=release
- PORT=8080
depends_on:
server:
condition: service_healthy
healthcheck:
test:
[
"CMD",
"wget",
"--spider",
"-q",
"http://localhost:8080/health",
]
interval: 15s
timeout: 10s
retries: 3
start_period: 30s
restart: unless-stopped
memswap_limit: 256M
deploy:
resources:
limits:
memory: 256M
reservations:
memory: 128M
dashboard:
container_name: openstatus-dashboard
build:
context: .
dockerfile: apps/dashboard/Dockerfile
image: openstatus/dashboard:latest
networks:
- openstatus
ports:
- "3002:3000"
env_file:
- .env.docker
environment:
- DATABASE_URL=http://libsql:8080
- PORT=3000
- HOSTNAME=0.0.0.0
- AUTH_TRUST_HOST=true
depends_on:
db-migrate:
condition: service_completed_successfully
workflows:
condition: service_healthy
libsql:
condition: service_healthy
server:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/ || exit 1"]
interval: 15s
timeout: 10s
retries: 3
start_period: 45s
restart: unless-stopped
memswap_limit: 512M
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 256M
status-page:
container_name: openstatus-status-page
build:
context: .
dockerfile: apps/status-page/Dockerfile
image: openstatus/status-page:latest
networks:
- openstatus
ports:
- "3003:3000"
env_file:
- .env.docker
environment:
- DATABASE_URL=http://libsql:8080
- PORT=3000
- HOSTNAME=0.0.0.0
- AUTH_TRUST_HOST=true
- OPENSTATUS_API_URL=${OPENSTATUS_API_URL:-http://server:3000}
depends_on:
db-migrate:
condition: service_completed_successfully
workflows:
condition: service_healthy
libsql:
condition: service_healthy
server:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/ || exit 1"]
interval: 15s
timeout: 10s
retries: 3
start_period: 45s
restart: unless-stopped
memswap_limit: 512M
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 256M