12 KiB
v9.1 packaging fix
v9.1 fixes the v9 source archive packaging. The v9 ZIP accidentally omitted internal/stacks/, which could leave an older local copy of that package in place when extracting over an existing checkout and cause method-signature build errors. v9.1 is a complete source archive and includes internal/stacks/stacks.go and its tests. Always extract it into a fresh directory.
Dockwatch v9.1
Go module:
git.send.nrw/sendnrw/dockwatch
Dockwatch is a single-binary Go control plane for Docker Compose, Docker resources and uptime monitoring. It combines Dockge/Dockhand-style operational workflows with Uptime-Kuma-style monitoring while keeping its own implementation and UI.
The same binary runs as standalone, master or agent. SQLite uses modernc.org/sqlite, so the application itself builds with CGO_ENABLED=0.
What is included
Compose / stack management
- multiple Compose stacks per environment
- local Docker plus remote agents
- staged
docker compose config --quietvalidation before a save - atomic managed-file writes and rollback on save failure
- full bidirectional Compose designer backed by a YAML AST
- recursive editing of all YAML maps/arrays/scalars, including unknown/future Compose fields
compose.yaml, root.env,envs/,secrets/andconfigs/- Up / Start / Stop / Restart / Down / Pull / Update / Force recreate
- service/container status
- live SSE logs with pause, follow, filter and download
- interactive PTY/WebSocket
docker compose execterminal - normalized service/dependency/network/volume graph from
docker compose config --format json - registry digest based image-update checks
- safe stack deletion: only Dockwatch-managed definition files are removed by default
- explicit, typed-name confirmation for full stack-folder purge
The editor keeps unsaved drafts in browser storage. Background refresh never overwrites a dirty editor.
Git-backed stacks
Git sources support:
- local or remote-agent environment
- repository, branch, workdir and Compose filename
- manual sync
- optional deploy after sync
- signed webhooks (
X-Hub-Signature-256,X-Gitlab-TokenorX-Webhook-Token) - encrypted webhook secret at rest
- last commit / last sync / last error
A Git sync is now managed-file based. Dockwatch stores a small .dockwatch/git-manifest.json in the stack directory and only replaces/removes files that belong to the Git checkout. Unrelated runtime or bind-mount data beside the Compose project is preserved. Syncs of the same source are serialized and a failed apply rolls back the touched file set.
Symlink stack destinations and symlink paths inside Git-managed writes are rejected.
Docker resources
Containers
- list/filter
- start / stop / restart / force-remove
- operator-only inspect
- one-shot CPU/memory/network/block stats
Images
- list/filter
- pull / remove / prune
- registry login/logout
- persistent Docker auth via
DOCKER_CONFIG=/data/docker-config
Volumes
- list/filter
- create / remove / prune
- driver and labels
Networks
- list/filter
- create / remove / prune
- driver, labels,
internal,attachable
Docker positional arguments and Compose service names are validated before invoking the CLI so option-like values cannot be interpreted as Docker CLI flags.
Monitoring
Probe types:
- HTTP(S)
- TCP
- DNS
- Docker container running state
- Docker container running + optional
healthyrequirement
Monitor lifecycle:
- create / edit / delete
- pause / resume
- manual or timed maintenance
- maintenance notes
- manual Check now
- interval and timeout
- HTTP method and accepted status range
- request headers/body
- keyword / inverted keyword assertion
- optional HTTP TLS verification bypass
- local or remote-agent execution
- heartbeat history
- last latency/status message
- 24-hour uptime
- retention cleanup
The scheduler uses a lightweight monitor query and does not recompute 24-hour uptime statistics every two seconds. Manual checks are serialized with scheduled checks and do not accidentally resume paused/maintenance monitors. Shutdown-cancelled checks are not persisted as false outages.
Services and public status pages
Probes can be grouped into user-facing Services. Aggregation is strict:
- any probe
down-> Servicedown - otherwise maintenance -> Service
maintenance - pending/paused states are retained
- otherwise Service
up
Service dialogs can assign/unassign probes directly.
Admins can publish selected Services on public pages:
/status/<slug>
/public/api/status/<slug>
The public JSON API uses a dedicated DTO and exposes only public status information (name, status, uptime, latency and last-check timestamp), not targets, node IDs, request headers, bodies or credentials. HTML/JSON status responses are not cached.
Notifications
Providers:
- generic JSON webhook
- ntfy
- Gotify
- SMTP
SMTP supports:
STARTTLS- implicit
SSL/TLS None- authentication on/off independently of TLS
- optional certificate-verification bypass for private/self-signed infrastructure
SMTP connections inherit the notification deadline after connect as well, so a stalled server cannot leave a delivery goroutine hanging indefinitely. SMTP/password/token/secret values are AES-GCM encrypted at rest. Notification configuration is admin-only in both API and UI.
PocketID / OIDC and roles
Roles are mapped from OIDC/PocketID groups:
viewer– dashboards/status/inventory, sanitized monitor configurationoperator– stack configuration/logs/terminal, Docker actions, monitor/service operations, Git stacksadmin– environments, notifications, status-page administration and audit log
Viewer API responses do not expose monitor request headers/body/keywords. Stack definition detail and live logs require Operator access because .env, secrets and runtime logs may contain sensitive data.
Required OIDC setup:
AUTH_DISABLED=false
BASE_URL=https://dockwatch.example.com
OIDC_ISSUER=https://id.example.com
OIDC_CLIENT_ID=...
OIDC_CLIENT_SECRET=...
APP_SECRET=replace-with-at-least-32-random-characters
OIDC_ADMIN_GROUP=dockwatch-admins
OIDC_OPERATOR_GROUP=dockwatch-operators
Redirect URI:
https://dockwatch.example.com/auth/callback
OIDC discovery, token exchange and verification are bounded by HTTP_TIMEOUT_SECONDS. Session/state cookies are HttpOnly, SameSite=Lax and become Secure when BASE_URL uses HTTPS.
Master / agent
Modes:
APP_MODE=standalone
APP_MODE=master
APP_MODE=agent
An agent exposes only /agent/v1/* and requires an AGENT_TOKEN of at least 24 characters. Agent tokens are AES-GCM encrypted in the master's SQLite database.
Remote-capable features include:
- stacks and actions
- logs and PTY terminal
- Compose graph
- image update checks
- containers/images/networks/volumes
- monitoring probes
- Git clone/sync/deploy
See examples/compose-master.yml and examples/compose-agent.yml.
Full Compose designer
The Compose tab uses two synchronized representations:
- the raw YAML source of truth
- a recursive visual designer
YAML changes are parsed after a short debounce. Visual changes patch the selected YAML AST path instead of reserializing a simplified Compose model. Unknown keys, x-* extensions and comments are therefore preserved where possible. Invalid/incomplete YAML pauses visual synchronization and never overwrites the source editor.
The designer is not limited to a hard-coded subset: arbitrary maps, arrays and scalar types remain editable, including current/future Compose fields and long syntax objects.
Server-side save validation still uses Docker Compose itself after all related .env, secret, env-file and config files have been staged.
Reliability and security work in v9
The v9 review includes, among other changes:
- fixed fresh-database migration ordering before monitor indexes are created
- SQLite WAL/busy-timeout/foreign-key configuration with a small connection pool
- strict environment parsing instead of silently accepting invalid numeric/boolean values
- graceful HTTP shutdown and sensible server timeouts
- security headers and same-origin checks for browser mutations
- request JSON size limit and single-value decoding
- admin-only audit activity and notification configuration
- audit retention cleanup
- public-status DTO that does not leak internal monitor configuration
- safe IP parsing for audit entries
- sensitive viewer responses reduced
- Compose save snapshot/rollback
- safe stack delete vs. explicit purge
- symlink protection on stack/Git file paths
- atomic managed-file writes
- Git sync serialization and managed-file manifest
- Git copy file-descriptor leak fixed
- Node URL validation rejects credentials/query/fragment
- remote terminal WebSocket URL/query handling fixed
- registry/Docker CLI positional-argument validation
- scheduler query and shutdown behaviour improved
- defensive frontend collection handling (
nullcannot break list pages) - admin-only navigation entries hidden for non-admin users
- persisted dark/light theme and collapsible/mobile sidebar
- API connectivity indicator and manual refresh
- denser Docker/monitor/service tables and better empty/error states
Configuration
.env.example contains the defaults. Important values:
APP_MODE=standalone
LISTEN_ADDR=:8080
BASE_URL=http://localhost:8080
DATA_DIR=/data
STACKS_DIR=/stacks
AUTH_DISABLED=true
APP_SECRET=change-me-change-me-change-me-32bytes
CHECK_CONCURRENCY=8
CHECK_RETENTION_DAYS=30
HTTP_TIMEOUT_SECONDS=10
AUDIT_RETENTION_DAYS=180
AUTH_DISABLED=true is for local development only. Do not expose that configuration publicly.
Run with Docker Compose
cp .env.example .env
docker compose up -d --build
Runtime mounts normally include:
/data SQLite, Docker registry config
/stacks Compose projects
/var/run/docker.sock Docker Engine access
Giving Dockwatch access to the Docker socket grants highly privileged control of that Docker host. Protect the UI and agent endpoint accordingly.
Build from source
The pinned OIDC/OAuth2 releases require Go 1.25. The Docker build uses golang:1.25-alpine.
go mod tidy
go test ./...
CGO_ENABLED=0 go build ./cmd/dockwatch
Build metadata can be injected through the supplied Makefile/Dockerfile (VERSION, COMMIT, BUILD_DATE) and is visible in /healthz and the UI.
Database
SQLite database:
/data/dockwatch.db
modernc.org/sqlite is used, so no SQLite CGO binding is required. Migrations are additive and include users/sessions, nodes, monitors/checks/services, public status pages, audit events, notification channels and Git sources.
Verification for this archive
The artifact-building environment cannot reach proxy.golang.org, so it cannot download the real external modules or generate a trustworthy go.sum here. The repository intentionally does not ship fake checksums or test stubs.
For quality control, the project is copied into a temporary test workspace where API-compatible local stubs replace only the external dependencies. Those stubs are not included in the ZIP. The checks used for v9 include:
# all packages type/compile checked with temporary external-module stubs
go test -run='^$' ./...
go test ./internal/config ./internal/gitops ./internal/httpapi \
./internal/monitor ./internal/nodes ./internal/notify ./internal/stacks
go vet ./...
go test -race ./internal/config ./internal/gitops ./internal/httpapi ./internal/monitor ./internal/nodes ./internal/notify ./internal/stacks
node --check web/app.js
The SQLite migration SQL is additionally smoke-tested against Python's SQLite engine because the temporary modernc.org/sqlite stub does not implement a real SQL driver.
On a normal networked development machine or during docker build, run go mod tidy && go test ./... once against the real pinned dependencies.