Compare commits

...
Author SHA1 Message Date
Narciso 77fd697d7d fix: resolve dependabot security alerts and fix templates for toml 4
Dependencies:
- app: react-router-dom ^7.18.0, vite ^6.4.3, yaml 2.8.3
- app (transitive, lockfile only): postcss 8.5.28, nanoid 3.3.19,
  picomatch 2.3.2/4.0.7, browserslist 4.29.2, rollup 4.63.5,
  @babel/core 7.29.7, @babel/runtime 7.29.7
- build-scripts: toml 4.2.0 (matches Dokploy/dokploy#5517), yaml 2.8.3

Templates rejected by toml 4's strict parsing (Dokploy fetches template.toml
from this repo at runtime, so they would fail there once #5517 ships):
- signoz, supabase: write invalid escape sequences (\{ \$ \( \s ...) as \\
  so the parsed value keeps the literal backslash exactly as toml 3 produced it
- timescaledb, valkey: drop `mounts = []` redefined by [[config.mounts]]

Parsed output is deep-equal to before under toml 3 for all 532 templates.
2026-09-29 11:12:00 -04: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
14 changed files with 696 additions and 317 deletions
+3 -3
View File
@@ -35,13 +35,13 @@
"next-themes": "^0.4.6",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-router-dom": "^7.9.4",
"react-router-dom": "^7.18.0",
"sonner": "^2.0.7",
"tailwind-merge": "^3.3.1",
"tailwindcss": "^4.1.16",
"tailwindcss-animate": "^1.0.7",
"vite-plugin-static-copy": "^2.3.2",
"yaml": "2.7.1",
"yaml": "2.8.3",
"zustand": "^5.0.8"
},
"devDependencies": {
@@ -51,6 +51,6 @@
"@vitejs/plugin-react": "^4.7.0",
"globals": "^15.15.0",
"typescript": "~5.7.3",
"vite": "^6.4.1"
"vite": "^6.4.3"
}
}
+344 -278
View File
File diff suppressed because it is too large Load Diff
+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 = ""
+6 -6
View File
@@ -472,7 +472,7 @@ content = """
<my_ad_server>
<host>localhost</host>
<port>389</port>
<bind_dn>EXAMPLE\{user_name}</bind_dn>
<bind_dn>EXAMPLE\\{user_name}</bind_dn>
<user_dn_detection>
<base_dn>CN=Users,DC=example,DC=com</base_dn>
<search_filter>(&amp;(objectClass=user)(sAMAccountName={user_name}))</search_filter>
@@ -624,14 +624,14 @@ content = """
To install and run JDBC bridge in background:
* [Debian/Ubuntu]
export MVN_URL=https://repo1.maven.org/maven2/ru/yandex/clickhouse/clickhouse-jdbc-bridge
export PKG_VER=$(curl -sL $MVN_URL/maven-metadata.xml | grep '<release>' | sed -e 's|.*>\(.*\)<.*|\1|')
export PKG_VER=$(curl -sL $MVN_URL/maven-metadata.xml | grep '<release>' | sed -e 's|.*>\\(.*\\)<.*|\\1|')
wget https://github.com/ClickHouse/clickhouse-jdbc-bridge/releases/download/v$PKG_VER/clickhouse-jdbc-bridge_$PKG_VER-1_all.deb
apt install --no-install-recommends -f ./clickhouse-jdbc-bridge_$PKG_VER-1_all.deb
clickhouse-jdbc-bridge &
* [CentOS/RHEL]
export MVN_URL=https://repo1.maven.org/maven2/ru/yandex/clickhouse/clickhouse-jdbc-bridge
export PKG_VER=$(curl -sL $MVN_URL/maven-metadata.xml | grep '<release>' | sed -e 's|.*>\(.*\)<.*|\1|')
export PKG_VER=$(curl -sL $MVN_URL/maven-metadata.xml | grep '<release>' | sed -e 's|.*>\\(.*\\)<.*|\\1|')
wget https://github.com/ClickHouse/clickhouse-jdbc-bridge/releases/download/v$PKG_VER/clickhouse-jdbc-bridge-$PKG_VER-1.noarch.rpm
yum localinstall -y clickhouse-jdbc-bridge-$PKG_VER-1.noarch.rpm
clickhouse-jdbc-bridge &
@@ -1068,11 +1068,11 @@ content = """
<query_masking_rules>
<rule>
<name>hide encrypt/decrypt arguments</name>
<regexp>((?:aes_)?(?:encrypt|decrypt)(?:_mysql)?)\s*\(\s*(?:'(?:\\'|.)+'|.*?)\s*\)</regexp>
<regexp>((?:aes_)?(?:encrypt|decrypt)(?:_mysql)?)\\s*\\(\\s*(?:'(?:\\'|.)+'|.*?)\\s*\\)</regexp>
<!-- or more secure, but also more invasive:
(aes_\w+)\s*\(.*\)
(aes_\\w+)\\s*\\(.*\\)
-->
<replace>\1(???)</replace>
<replace>\\1(???)</replace>
</rule>
</query_masking_rules>
+5 -5
View File
@@ -213,17 +213,17 @@ content = """#!/bin/bash
# and performs environment variable substitution in the declarative config.
if [ -n "$SUPABASE_SECRET_KEY" ] && [ -n "$SUPABASE_PUBLISHABLE_KEY" ]; then
export LUA_AUTH_EXPR="\$((headers.authorization ~= nil and headers.authorization:sub(1, 10) ~= 'Bearer sb_' and headers.authorization) or (headers.apikey == '$SUPABASE_SECRET_KEY' and 'Bearer $SERVICE_ROLE_KEY_ASYMMETRIC') or (headers.apikey == '$SUPABASE_PUBLISHABLE_KEY' and 'Bearer $ANON_KEY_ASYMMETRIC') or headers.apikey)"
export LUA_RT_WS_EXPR="\$((query_params.apikey == '$SUPABASE_SECRET_KEY' and '$SERVICE_ROLE_KEY_ASYMMETRIC') or (query_params.apikey == '$SUPABASE_PUBLISHABLE_KEY' and '$ANON_KEY_ASYMMETRIC') or query_params.apikey)"
export LUA_AUTH_EXPR="\\$((headers.authorization ~= nil and headers.authorization:sub(1, 10) ~= 'Bearer sb_' and headers.authorization) or (headers.apikey == '$SUPABASE_SECRET_KEY' and 'Bearer $SERVICE_ROLE_KEY_ASYMMETRIC') or (headers.apikey == '$SUPABASE_PUBLISHABLE_KEY' and 'Bearer $ANON_KEY_ASYMMETRIC') or headers.apikey)"
export LUA_RT_WS_EXPR="\\$((query_params.apikey == '$SUPABASE_SECRET_KEY' and '$SERVICE_ROLE_KEY_ASYMMETRIC') or (query_params.apikey == '$SUPABASE_PUBLISHABLE_KEY' and '$ANON_KEY_ASYMMETRIC') or query_params.apikey)"
else
export LUA_AUTH_EXPR="\$((headers.authorization ~= nil and headers.authorization:sub(1, 10) ~= 'Bearer sb_' and headers.authorization) or headers.apikey)"
export LUA_RT_WS_EXPR="\$(query_params.apikey)"
export LUA_AUTH_EXPR="\\$((headers.authorization ~= nil and headers.authorization:sub(1, 10) ~= 'Bearer sb_' and headers.authorization) or headers.apikey)"
export LUA_RT_WS_EXPR="\\$(query_params.apikey)"
fi
awk '{
result = ""
rest = $0
while (match(rest, /\$[A-Za-z_][A-Za-z_0-9]*/)) {
while (match(rest, /\\$[A-Za-z_][A-Za-z_0-9]*/)) {
varname = substr(rest, RSTART + 1, RLENGTH - 1)
if (varname in ENVIRON) {
result = result substr(rest, 1, RSTART - 1) ENVIRON[varname]
-1
View File
@@ -5,7 +5,6 @@ postgres_db = "timescaledb"
timescaledb_telemetry = "off"
[config]
mounts = []
[config.env]
POSTGRES_USER = "${postgres_user}"
-1
View File
@@ -5,7 +5,6 @@ valkey_password = "${password:32}"
env = [
"VALKEY_PASSWORD=${valkey_password}"
]
mounts = []
[[config.mounts]]
filePath = "valkey.conf"
+16 -10
View File
@@ -8,8 +8,8 @@
"name": "dokploy-templates-build-scripts",
"version": "1.0.0",
"dependencies": {
"toml": "^3.0.0",
"yaml": "2.7.1"
"toml": "4.2.0",
"yaml": "2.8.3"
},
"devDependencies": {
"@types/node": "^20.0.0",
@@ -553,10 +553,13 @@
}
},
"node_modules/toml": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz",
"integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==",
"license": "MIT"
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/toml/-/toml-4.2.0.tgz",
"integrity": "sha512-TvAJjbHZlYmI323+srtqHQFyJsoWy6mI09ppkuj9+iRsqsVKG9fvTcOP7FHF2UCb0QSYtjEavffrKzdd0XgClg==",
"license": "MIT",
"engines": {
"node": ">=20"
}
},
"node_modules/tsx": {
"version": "4.21.0",
@@ -600,15 +603,18 @@
"license": "MIT"
},
"node_modules/yaml": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz",
"integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==",
"version": "2.8.3",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
"integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==",
"license": "ISC",
"bin": {
"yaml": "bin.mjs"
},
"engines": {
"node": ">= 14"
"node": ">= 14.6"
},
"funding": {
"url": "https://github.com/sponsors/eemeli"
}
}
}
+2 -2
View File
@@ -9,8 +9,8 @@
"process-meta": "node process-meta.js"
},
"dependencies": {
"toml": "^3.0.0",
"yaml": "2.7.1"
"toml": "4.2.0",
"yaml": "2.8.3"
},
"devDependencies": {
"@types/node": "^20.0.0",
+12 -11
View File
@@ -9,11 +9,11 @@ importers:
.:
dependencies:
toml:
specifier: ^3.0.0
version: 3.0.0
specifier: 4.2.0
version: 4.2.0
yaml:
specifier: 2.7.1
version: 2.7.1
specifier: 2.8.3
version: 2.8.3
devDependencies:
'@types/node':
specifier: ^20.0.0
@@ -202,8 +202,9 @@ packages:
resolve-pkg-maps@1.0.0:
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
toml@3.0.0:
resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==}
toml@4.2.0:
resolution: {integrity: sha512-TvAJjbHZlYmI323+srtqHQFyJsoWy6mI09ppkuj9+iRsqsVKG9fvTcOP7FHF2UCb0QSYtjEavffrKzdd0XgClg==}
engines: {node: '>=20'}
tsx@4.21.0:
resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==}
@@ -218,9 +219,9 @@ packages:
undici-types@6.21.0:
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
yaml@2.7.1:
resolution: {integrity: sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==}
engines: {node: '>= 14'}
yaml@2.8.3:
resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==}
engines: {node: '>= 14.6'}
hasBin: true
snapshots:
@@ -345,7 +346,7 @@ snapshots:
resolve-pkg-maps@1.0.0: {}
toml@3.0.0: {}
toml@4.2.0: {}
tsx@4.21.0:
dependencies:
@@ -358,4 +359,4 @@ snapshots:
undici-types@6.21.0: {}
yaml@2.7.1: {}
yaml@2.8.3: {}