Skip to content

Repository files navigation

Docktornet

Docktornet is a declarative infrastructure reconciler for self-hosted services. Containers describe reverse-proxy, DNS, and monitoring intent through docktornet.* labels; Docktornet discovers that intent and reconciles Nginx Proxy Manager, Cloudflare DNS, and Gatus from one source of truth.

Status

v1.1.0 is the current stable release. The agent/server architecture, SQLite-backed ownership, Nginx Proxy Manager, Cloudflare DNS, Gatus, full resync, health endpoint, deployment manifests, and cutover runbook are implemented. See the v1.1.0 release notes for the multi-domain NPM workflow and post-v1 reliability and security changes.

Why Docktornet Is Technically Interesting

  • Declarative control plane: infrastructure follows container labels instead of separately maintained proxy, DNS, and monitoring configuration.
  • Distributed discovery: lightweight agents enumerate Docker or Podman sockets on each host and send authenticated snapshots to a central server.
  • Stateful reconciliation: SQLite tracks snapshots and resource ownership so the server can converge external systems and remove resources it owns after their labels disappear and the restart grace period expires.
  • Failure-aware integrations: reconciliation includes request timeouts, no-op detection, Cloudflare rate-limit handling, an NPM circuit breaker, and atomic Gatus configuration writes.
  • Guarded adoption: dry-run is the default and existing resources are only adopted when explicitly enabled.
  • Small deployment shape: three Rust crates produce an agent and a central server, with Compose manifests for each role.
flowchart LR
engines[Docker / Podman] --> agents[Docktornet agents]
agents -->|Authenticated snapshots| server[Docktornet server]
server <--> state[(SQLite ownership state)]
server --> npm[Nginx Proxy Manager]
server --> cf[Cloudflare DNS]
server --> gatus[Gatus]
Loading

Architecture And Operations

The public GET /healthz response contains liveness status and server time. Send the configured bearer token to include agent inventory; unauthenticated callers do not receive machine IDs, forwarding addresses, or last-seen times.

Resource removal lifecycle

When a service disappears from an agent snapshot, Docktornet preserves its managed NPM proxy hosts, Cloudflare DNS record, and Gatus configuration for a five-minute restart grace period. If the service returns, its stored external resource mappings are restored. If it remains absent, Docktornet removes the owned resources; failed external deletions are persisted and retried during full resync.

Quick start (local dev)

just run-server # in one terminal, with server.env loaded
just run-agent # in another, with agent.env loaded

DOCKTORNET_SECRET must be non-empty on the server. Agent DOCKTORNET_POLL_INTERVAL values must be at least one second. See the deployment guide for the complete environment-variable reference.

Container labels

Docktornet reconciles state from Docker/Podman container labels. All keys are prefixed with docktornet.; any other labels are ignored. Containers with no docktornet.* labels are skipped entirely.

Boolean fields accept true/1/yes or false/0/no.

Nginx Proxy Manager (docktornet.npm.<index>.*)

<index> is an integer (0, 1, …) so a single container can publish multiple proxy hosts.

To route several public domains to the same upstream as one NPM entry, give host a comma-separated list (e.g. app.example.com,app.mydomain.org). All domains share that entry's port, scheme, SSL and certificate. Use a separate <index> only when you need a different upstream or settings.

Certificates and adoption: NPM stores one certificate per proxy host. When cert_id is omitted, automatic resolution selects a certificate only when it covers every listed domain (exactly or through its wildcard/SAN names). If no certificate covers every domain and ssl=true, Docktornet refuses to publish the host instead of silently downgrading it to HTTP. An explicit cert_id is used as configured, so ensure it covers every domain. Adoption can find an existing host through any listed domain, but is refused if the update would drop domains that are not present in the Docktornet labels. Use a separate <index> when domains require different certificates or settings.

LabelRequiredTypeDefaultExampleNotes
docktornet.npm.<index>.hostyesstring (comma-separated)flowsync.example.comOne or more public hostnames (FQDN) on a single proxy host. Comma-separated for multiple domains.
docktornet.npm.<index>.portyesu168080Upstream port.
docktornet.npm.<index>.schemeyeshttp | httpshttpUpstream scheme.
docktornet.npm.<index>.sslyesbooltrueEnable SSL on the proxy host.
docktornet.npm.<index>.hstsnobooltrue when ssl is onfalseEnable HSTS. Forced off when ssl is off.
docktornet.npm.<index>.hsts_subdomainsnobooltrue when HSTS is onfalseApply HSTS to subdomains. Forced off when HSTS is off.
docktornet.npm.<index>.http2nobooltrue when ssl is ontrueEnable HTTP/2 support. Forced off when ssl is off.
docktornet.npm.<index>.block_exploitsnobooltruefalseBlock common exploits.
docktornet.npm.<index>.websocketsnobooltruefalseEnable Websocket support.
docktornet.npm.<index>.cert_idnou32unset7Existing NPM certificate ID.

Cloudflare DNS (docktornet.cf.dns.*)

A single DNS record per container (not indexed).

LabelRequiredTypeDefaultExampleNotes
docktornet.cf.dns.nameyesstringflowsyncRecord name (zone is appended automatically).
docktornet.cf.dns.targetyesstringhome.example.comCNAME target (must be a full domain).
docktornet.cf.dns.proxiedyesbooltrueWhether the record is proxied through Cloudflare.

Gatus monitors (docktornet.gatus.<index>.*)

<index> is an integer so a single container can define multiple monitors.

LabelRequiredTypeDefaultExampleNotes
docktornet.gatus.<index>.urlyesstringhttps://flowsync.example.comEndpoint to monitor.
docktornet.gatus.<index>.intervalyesstring30sCheck interval.
docktornet.gatus.<index>.conditionsnostring[STATUS] == 200[STATUS] == 200, [RESPONSE_TIME] < 500Comma-separated condition list.
docktornet.gatus.<index>.groupnostringhost/machine idmediaGatus dashboard group. Defaults to the machine this container runs on.
docktornet.gatus.<index>.namenostringcontainer nameHomepageDisplay name shown in alerts/dashboard. Defaults to the container name; colliding defaults get a <index> suffix.
docktornet.gatus.<index>.telegramnobooltruefalseEmit a Gatus custom alert (used for Telegram).

Example

A minimal compose.yml fragment for a container that publishes one proxy host, one DNS record, and three monitors (public, internal, and direct IP):

services:
flowsync:
image: example/flowsync:latestlabels:
# Nginx Proxy Managerdocktornet.npm.0.host: flowsync.example.comdocktornet.npm.0.port: "8080"docktornet.npm.0.scheme: httpdocktornet.npm.0.ssl: "true"# Cloudflare DNSdocktornet.cf.dns.name: flowsyncdocktornet.cf.dns.target: home.example.comdocktornet.cf.dns.proxied: "true"# Gatus monitorsdocktornet.gatus.0.url: https://flowsync.example.comdocktornet.gatus.0.interval: 30sdocktornet.gatus.1.url: https://flowsync.home.localdocktornet.gatus.1.interval: 30sdocktornet.gatus.2.url: http://192.168.1.42:8080docktornet.gatus.2.interval: 60sdocktornet.gatus.2.telegram: "false"

Development

The Justfile exposes the standard formatting, linting, checking, testing, and local run commands. Run just to list them.

Licence

Docktornet is licensed under the GNU Affero General Public License v3.0 or later. See LICENSE.

About

Declarative infrastructure reconciler for self-hosted services. Docker and Podman container labels drive Nginx Proxy Manager, Cloudflare DNS, and Gatus.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages