Compare commits

...
Author SHA1 Message Date
dosubot[bot]andGitHub 737c5df8ad docs: clarify template image versioning for digest pins 2026-10-01 06:42:19 +00:00
830d6bbc8d Add OpenStatus template (#1126)
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:05:48 -06:00
6 changed files with 315 additions and 4 deletions
+7 -4
View File
@@ -162,7 +162,10 @@ This document provides essential context for AI models interacting with this pro
- **Required fields:** `id`, `name`, `version`, `description`, `links` (with `github`), `logo`, `tags` (array)
- **Tags:** Lowercase strings (e.g., `["monitoring", "database"]`)
- **Version:** MUST match Docker image version in docker-compose.yml
- **Version:** MUST match Docker image version in docker-compose.yml. Image references can use either:
- **Version tags:** `ghost:6-alpine` → set `version` to `"6"` or the semantic version visible in the tag
- **SHA256 digests:** `softinator/feedbacks@sha256:f7d759e...` → set `version` to the release version that the digest points to (e.g., `"0.2.1"`)
- Digest pinning is recommended for production deployments to ensure immutability
- **Logo:** Filename only (e.g., `"ghost.jpeg"`), file must exist in blueprint folder
- **Frontend Development:**
@@ -183,8 +186,8 @@ This document provides essential context for AI models interacting with this pro
- **Dependencies:**
- When adding new templates, ensure Docker images are pinned to specific versions
- Update meta.json with exact version matching Docker Compose image version
- When adding new templates, ensure Docker images are pinned to specific versions (using version tags or immutable SHA256 digests)
- Update meta.json with exact version matching Docker Compose image version (for digest-pinned images, use the release version the digest points to)
- For frontend dependencies: Use `pnpm add <package>` in `app/` directory
- For root dependencies: Use `npm install <package>` in root directory
@@ -196,4 +199,4 @@ This document provides essential context for AI models interacting with this pro
3. Including `ports`, `container_name`, or `networks` in docker-compose.yml
4. Using object syntax for env vars in template.toml (must be array of strings)
5. Logo file missing or filename mismatch in meta.json
6. Version mismatch between meta.json and docker-compose.yml image tag
6. Version mismatch between meta.json and docker-compose.yml image reference (remember: for digest-pinned images, set version to the release version the digest points to, not the digest itself)
+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 = ""