Compare commits

...
Author SHA1 Message Date
Mauricio SiuandClaude Fable 5 571fa42323 Add OpenStatus template
Self-hosted OpenStatus (status pages + uptime monitoring) following the
official docker-compose.github-packages.yaml setup: local libSQL database,
one-shot migration runner built from the pinned upstream commit, workflows,
API server, dashboard, status-page, and the private-location ingest server.
All app images pinned to upstream commit e1a9825; Tinybird analytics run in
no-op mode by default.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 14:03:32 -06:00
5 changed files with 308 additions and 0 deletions
+197
View File
@@ -0,0 +1,197 @@
services:
libsql:
image: ghcr.io/tursodatabase/libsql-server:v0.24.33
restart: unless-stopped
expose:
- 8080
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
# One-shot migration runner (idempotent — drizzle skips applied migrations).
# No published image exists for it upstream, so it is built from the pinned
# commit the ghcr.io app images were built from.
db-migrate:
build:
context: https://github.com/openstatusHQ/openstatus.git#e1a98255e39c9cc9f6d4853dbce30c3f6c42514a
dockerfile: packages/db/Dockerfile
restart: on-failure:3
environment:
- DATABASE_URL=http://libsql:8080
- DATABASE_AUTH_TOKEN=
depends_on:
libsql:
condition: service_healthy
workflows:
image: ghcr.io/openstatushq/openstatus-workflows:e1a9825
restart: unless-stopped
expose:
- 3000
volumes:
- workflows-data:/app/data
environment:
- NODE_ENV=production
- PORT=3000
- DATABASE_URL=http://libsql:8080
- DATABASE_AUTH_TOKEN=
- CRON_SECRET=${CRON_SECRET}
- RESEND_API_KEY=${RESEND_API_KEY:-re_selfhost_placeholder}
- SITE_URL=https://${DASHBOARD_HOST}
- TINYBIRD_NOOP=true
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
server:
image: ghcr.io/openstatushq/openstatus-server:e1a9825
restart: unless-stopped
expose:
- 3000
environment:
- NODE_ENV=production
- PORT=3000
- DATABASE_URL=http://libsql:8080
- DATABASE_AUTH_TOKEN=
- CRON_SECRET=${CRON_SECRET}
- RESEND_API_KEY=${RESEND_API_KEY:-re_selfhost_placeholder}
- SUPER_ADMIN_TOKEN=${SUPER_ADMIN_TOKEN}
- SELF_HOST=true
- FLY_REGION=self-hosted
- TINYBIRD_NOOP=true
- UPSTASH_REDIS_REST_URL=http://localhost:6379
- UPSTASH_REDIS_REST_TOKEN=placeholder
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
dashboard:
image: ghcr.io/openstatushq/openstatus-dashboard:e1a9825
restart: unless-stopped
expose:
- 3000
environment:
- NODE_ENV=production
- PORT=3000
- HOSTNAME=0.0.0.0
- AUTH_TRUST_HOST=true
- DATABASE_URL=http://libsql:8080
- DATABASE_AUTH_TOKEN=
- AUTH_SECRET=${AUTH_SECRET}
- SELF_HOST=true
- NEXT_PUBLIC_URL=https://${DASHBOARD_HOST}
- CRON_SECRET=${CRON_SECRET}
- RESEND_API_KEY=${RESEND_API_KEY:-re_selfhost_placeholder}
- TINYBIRD_NOOP=true
- UPSTASH_REDIS_REST_URL=http://localhost:6379
- UPSTASH_REDIS_REST_TOKEN=placeholder
depends_on:
db-migrate:
condition: service_completed_successfully
workflows:
condition: service_healthy
libsql:
condition: service_healthy
server:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/"]
interval: 15s
timeout: 10s
retries: 3
start_period: 45s
status-page:
image: ghcr.io/openstatushq/openstatus-status-page:e1a9825
restart: unless-stopped
expose:
- 3000
environment:
- NODE_ENV=production
- PORT=3000
- HOSTNAME=0.0.0.0
- AUTH_TRUST_HOST=true
- DATABASE_URL=http://libsql:8080
- DATABASE_AUTH_TOKEN=
- AUTH_SECRET=${AUTH_SECRET}
- SELF_HOST=true
- NEXT_PUBLIC_URL=https://${DASHBOARD_HOST}
- CRON_SECRET=${CRON_SECRET}
- RESEND_API_KEY=${RESEND_API_KEY:-re_selfhost_placeholder}
- TINYBIRD_NOOP=true
depends_on:
db-migrate:
condition: service_completed_successfully
workflows:
condition: service_healthy
libsql:
condition: service_healthy
server:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/"]
interval: 15s
timeout: 10s
retries: 3
start_period: 45s
# Ingest server for private-location probes (the probe itself,
# ghcr.io/openstatushq/private-location, runs wherever you monitor from).
private-location:
image: ghcr.io/openstatushq/openstatus-private-location:e1a9825
restart: unless-stopped
expose:
- 8080
environment:
- PORT=8080
- GIN_MODE=release
- DB_URL=http://libsql:8080
- WORKFLOWS_URL=http://workflows:3000
- CRON_SECRET=${CRON_SECRET}
- TINYBIRD_URL=${TINYBIRD_URL}
- TINYBIRD_TOKEN=${TINYBIRD_TOKEN}
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
volumes:
libsql-data:
workflows-data:
+60
View File
@@ -0,0 +1,60 @@
# OpenStatus
OpenStatus is deployed here in self-hosted mode following the official Docker
setup (`DOCKER.md` / `docker-compose.github-packages.yaml` upstream), with all
app images pinned to the same upstream commit (`e1a9825`).
## Services
| Service | Purpose |
|---|---|
| dashboard | Admin UI (your first domain) |
| status-page | Public status pages (your second domain, pages served at `/<slug>`) |
| server | API backend (tRPC / public API) |
| workflows | Background jobs / alerting |
| libsql | Local libSQL (Turso) database — no cloud account needed |
| db-migrate | One-shot migration runner (built from the pinned upstream commit, exits after applying) |
| private-location | Ingest server that receives check results from your monitoring probes |
Database migrations run automatically on every deploy (the one-shot
`db-migrate` service is idempotent).
Note: `db-migrate` is built from source (upstream publishes no image for it),
so the first deployment takes a few extra minutes while it builds.
## First login
1. Open the dashboard domain and choose **magic link** login with your e-mail.
2. If you did not configure `RESEND_API_KEY`, the magic link is not e-mailed —
it is printed to the **dashboard service logs**. Open the dashboard
container logs in Dokploy and copy the `>>> Magic Link: ...` URL into your
browser. If the printed link starts with `http://0.0.0.0:3000`, replace that
host with your dashboard domain before opening it. (Optionally, once HTTPS
is enabled on your dashboard domain, you can add
`AUTH_URL=https://<your-dashboard-domain>` to the dashboard service in the
compose file so links are generated with the right host — do not set it to
an empty value or a plain-HTTP domain, as that breaks the auth flow.)
3. Create a workspace and a status page with a slug; it becomes available at
`https://<status-page-domain>/<slug>`.
## Running checks (probes)
Self-hosted OpenStatus runs monitors through **private location probes**
(`ghcr.io/openstatushq/private-location`) that you deploy wherever you want to
monitor from. Point the probe at this stack's ingest server:
- Expose the `private-location` service (port 8080) on a domain in Dokploy, and
- set `OPENSTATUS_INGEST_URL` on the probe to that URL, together with the
`OPENSTATUS_KEY` you create in the dashboard.
See the upstream guide: https://www.openstatus.dev/docs (Guides → How to
Self-Host openstatus).
## Analytics (optional)
Tinybird analytics are disabled by default (`TINYBIRD_NOOP=true`), so charts
show no data but everything else works. To enable analytics, run a Tinybird
Local container or use Tinybird Cloud, deploy the `packages/tinybird` project
with the `tb` CLI, and set `TINYBIRD_URL` / `TINYBIRD_TOKEN` in the
environment (plus `TINY_BIRD_API_KEY` and remove `TINYBIRD_NOOP`) — see the
upstream self-hosting guide.
+17
View File
@@ -0,0 +1,17 @@
{
"id": "openstatus",
"name": "OpenStatus",
"version": "e1a9825",
"description": "OpenStatus is an open-source synthetic monitoring platform with beautiful status pages, uptime and API monitoring as code, and alerting.",
"logo": "openstatus.png",
"links": {
"github": "https://github.com/openstatusHQ/openstatus",
"website": "https://www.openstatus.dev",
"docs": "https://www.openstatus.dev/docs"
},
"tags": [
"monitoring",
"status-page",
"uptime"
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

+34
View File
@@ -0,0 +1,34 @@
[variables]
dashboard_domain = "${domain}"
status_page_domain = "${domain}"
auth_secret = "${base64:32}"
cron_secret = "${password:32}"
super_admin_token = "${password:32}"
[config]
# Dashboard (admin UI — log in here)
[[config.domains]]
serviceName = "dashboard"
port = 3000
host = "${dashboard_domain}"
# Public status pages (served at /<page-slug>)
[[config.domains]]
serviceName = "status-page"
port = 3000
host = "${status_page_domain}"
[config.env]
DASHBOARD_HOST = "${dashboard_domain}"
STATUS_PAGE_HOST = "${status_page_domain}"
AUTH_SECRET = "${auth_secret}"
CRON_SECRET = "${cron_secret}"
SUPER_ADMIN_TOKEN = "${super_admin_token}"
# Optional: Resend API key for outgoing e-mails. Without it, magic-link
# login URLs are printed to the dashboard container logs instead.
RESEND_API_KEY = ""
# Optional: Tinybird analytics for the private-location ingest server
# (see instructions — analytics are disabled/no-op by default)
TINYBIRD_URL = ""
TINYBIRD_TOKEN = ""