Self-hosted deployment dashboard for the Teploy CLI, plus uptime monitoring — in one Go binary.
The CLI writes deployment state to /deployments/{app}/state.json. Dash
reads those files (read-only) and shells out to teploy for actions
(deploy, rollback, env edits, logs). Same source of truth whether you
deploy from the terminal, the UI, or a webhook — no SSH-vs-UI desync.
Optional uptime monitoring runs HTTP / TCP checks on configurable intervals, stores history in Nucleus or a local JSONL file, and fires webhook / SMTP alerts on state transitions.
One binary. ~17MB. Default port 3456.
brew install useteploy/tap/teploy-dashdocker run -d -p 3456:3456 \
-e TEPLOY_DASH_PASSWORD=$(openssl rand -base64 24) \
-v /deployments:/deployments \
ghcr.io/useteploy/teploy-dash:latestcurl -sL https://raw.githubusercontent.com/useteploy/teploy-dash/main/scripts/install.sh | shOn Linux the script also installs a teploy-dash.service systemd unit and
generates a random admin password into /etc/teploy-dash/teploy-dash.env
(printed on completion). Skip with TEPLOY_DASH_NO_SERVICE=1.
git clone https://github.com/useteploy/teploy-dash.git
cd teploy-dash
go build ./cmd/teploy-dashThe frontend lives at cmd/teploy-dash/frontend/ and is embedded into
the binary at build time via //go:embed. The compiled binary is fully
portable — copy it anywhere and run it.
teploy-dashOpen http://localhost:3456. On first launch you'll be taken to a setup page
to create your username and password. Credentials are stored as a bcrypt hash
in /var/teploy-dash/auth.json.
You can also pre-set a password via environment variable (useful for Docker or automated deploys — the setup page is skipped when this is present):
TEPLOY_DASH_PASSWORD=yourpassword teploy-dashYou can change your password any time from Settings → Account inside the UI.
teploy-dash --port 8080 # custom port
teploy-dash --deployments /opt/deployments # custom CLI state dir
teploy-dash --nucleus-url postgres://localhost:5432/teploy_dash # use Nucleus
teploy-dash --no-auth # local dev only- Live list of apps across every server in
servers.yml(CLI config). - Per-app status, current vs previous image hash, domain, container state.
- Stop / start / restart over SSH; deploy, rollback, lock, maintenance, registry login, env get/set/unset all delegated to the CLI.
- Multi-server fleet view with 60-second cache so the page doesn't SSH on every refresh.
- Persistent groups + projects (organisation overlay stored in
~/.teploy/groups.json, format-compatible with the CLI's embedded UI). - Umbrel-style template catalog: install pre-defined apps with one click.
- WebSocket log tailing per app (with SSE fallback for clients that don't speak WS).
- HTTP, TCP, and ping checks with per-monitor interval and timeout.
HTTP checks honor an optional exact expected status code (when unset,
any 2xx/3xx is healthy). Ping is a TCP-connect probe (the target needs
a
host:port); raw ICMP is not used. - 24-hour stats (uptime %, total / up / down checks, average response time).
- Storage: Nucleus over pgwire (preferred) or rolling JSONL files (fallback). Daily cleanup for the file store keeps 7 days of checks.
- Manual "test now" runs a check immediately without saving.
- Webhook + SMTP alerts on state transitions (up → down, recovered).
- Notification config persisted to
~/.teploy/notifications.jsonand reloadable from the UI without restart.
The CLI is the source of truth. Dash never writes deployment state — it reads the same JSON files the CLI writes, and shells out to the CLI for every action. Whether the deploy came from the terminal, this UI, or a CI webhook, everything reconciles to the same files.
| Flag | Default | Description |
|---|---|---|
--port |
3456 |
HTTP listen port. |
--host |
0.0.0.0 |
HTTP listen host. |
--data |
/var/teploy-dash |
Data dir for monitor history (file-store mode). |
--deployments |
/deployments |
Where the CLI writes per-app state files. |
--nucleus-url |
(empty) | Optional Nucleus / Postgres URL for monitor storage. Falls back to JSONL on connect failure. |
--no-auth |
false |
Disable authentication entirely. Local dev only. |
--public-status |
false |
Serve an unauthenticated public status page at /status. Off by default. |
Dash ships an MCP server at POST /api/mcp,
so Claude Code, Cursor, or any MCP client can inspect your fleet and run
deploy actions. Every action goes through the same teploy CLI delegation the
dashboard buttons use, and every read comes from the server state files the
CLI writes — MCP adds a fourth client to the single source of truth, not a
second source of truth. There is nothing new to drift.
Create a token under Settings → MCP (read-only tokens see only read tools), then:
claude mcp add teploy --transport http \
--header "Authorization: Bearer <token>" \
https://dash.example.com/api/mcpTools: teploy_list_apps, teploy_get_app, teploy_app_logs,
teploy_list_servers, teploy_list_monitors, teploy_list_env_keys
(names only — values never cross the MCP boundary), plus actions
teploy_deploy, teploy_rollback, teploy_restart, teploy_stop,
teploy_start, teploy_lock/unlock, teploy_maintenance_on/off,
teploy_set_env, teploy_unset_env. Tokens are 256-bit secrets stored
hashed in the dash data dir; revocation is immediate.
--public-status (or TEPLOY_DASH_PUBLIC_STATUS=1) serves a customer-facing
status page at /status — no login required. It shows, for each enabled
monitor, only its name, current up/down state, and 24-hour uptime %.
It deliberately never exposes monitor targets/IPs, server names, response
bodies, or any config. Off by default; when off, /status and /api/status
return 404.
| Variable | Default | Description |
|---|---|---|
TEPLOY_DASH_USER |
admin |
Username used when TEPLOY_DASH_PASSWORD is set (env-var bootstrap mode). |
TEPLOY_DASH_PASSWORD |
(optional) | Bootstrap password. If set, credentials are taken from this env var. If absent and no auth.json exists, the first run shows the setup page to create an account. |
TEPLOY_DASH_PUBLIC_STATUS |
(off) | Set to 1/true to enable the public /status page (same as --public-status). |
| Method | Path | Description |
|---|---|---|
| GET | /api/health |
Liveness probe (auth-exempt). |
| GET | /status, /api/status |
Public status page + JSON (auth-exempt; 404 unless --public-status). Exposes only name/up-down/24h-uptime. |
| GET | /api/cli/status |
Whether the teploy CLI is on $PATH and its version. |
| GET | /api/apps |
Fleet app list across all configured servers. |
| GET | /api/apps/{server}/{app}/status |
Single app status. |
| POST | /api/apps/{server}/{app}/{action} |
stop, start, restart, rollback, lock, unlock, maintenance/on, maintenance/off. |
| GET / POST | /api/apps/{server}/{app}/env |
List / set env vars. |
| DELETE | /api/apps/{server}/{app}/env/{key} |
Unset env var. |
| GET | /api/apps/{server}/{app}/log |
Recent CLI deploy log. |
| GET | /api/apps/{server}/{app}/accessories |
List accessories (DBs, queues, etc). |
| GET | /ws/logs/{server}/{app} |
WebSocket log stream (SSE fallback). |
| GET / POST / DELETE | /api/config/servers /api/config/servers/{name} |
Manage servers via CLI. |
| GET / POST | /api/registries |
List / login to image registries. |
| DELETE | /api/registries/{server} |
Logout. |
| GET | /api/templates |
App catalog. |
| POST | /api/templates/install |
Install a template app. |
| GET / POST | /api/groups |
List / create groups. |
| Various | /api/groups/{name}/... |
Assign apps and projects, rename, delete. |
| GET / POST | /api/monitors |
List with 24h stats / create. |
| GET / DELETE | /api/monitors/{id} |
Detail + history / delete. |
| POST | /api/monitors/{id}/test |
Run a check immediately. |
| GET / POST | /api/restore-tests |
List / create scheduled backup verifications. |
| GET / DELETE | /api/restore-tests/{id} |
Detail / delete. |
| POST | /api/restore-tests/{id}/run |
Verify the latest backup now (restores into a scratch container via teploy accessory verify-backup). |
| GET / POST | /api/notifications |
Read / write alert config. |
All non-health routes require a valid session cookie. Sessions are issued by
POST /api/login (24-hour TTL). Failed login attempts are rate-limited
per source IP.
Browser
|
v
teploy-dash (Go, ~17MB) --- session-cookie auth middleware
| embedded SPA (Alpine.js)
| 60s fleet cache
| WebSocket log streamer
|
+--> reads CLI state files at /deployments/{app}/state.json
+--> shells out to `teploy` for actions (deploy, rollback, env, ...)
+--> SSH to fleet servers for stop / start / restart
+--> uptime checks --(pgwire)--> Nucleus
\-(disk)----> JSONL files
AGPL-3.0-or-later. See LICENSE. The embedded frontend/js/alpine.js
is Alpine.js, MIT-licensed and used unmodified.