Deplo

How Deplo works

Two programs, one rule, why that split explains almost everything Deplo does.

Deplo is two programs with one rule between them. Understanding that split explains almost every behavior in the product: why a deploy can fail with "the server is unreachable" instead of quietly building somewhere else, why the dashboard cannot show you a secret, and why the machine Deplo runs on is not special.


The two planes

The browser talks only to the control plane, over gRPC with mutual TLS and a pinned certificate. The control plane never talks to a server directly: every server runs its own agent, and that agent is the only thing that touches Docker.

The control plane decides. It is the dashboard you log into, written in TypeScript. It:

  • holds the data and authenticates people
  • renders the Compose file and the Traefik routing labels
  • picks the ports
  • resolves and decrypts the environment variables
  • hands the finished result over to a server

The server agent executes. One Go binary per server, deplo-agent. It is the only thing anywhere that runs docker, touches the filesystem or opens a shell. Builds, deploys, log streaming, the web console, metrics, the config files a File volume points at, backups, restores, disk cleanup and volume copies all go through it.


The rule

The control plane never touches a Docker socket or a host directly.

Every host-coupled action takes the same path: the UI calls GraphQL, GraphQL calls the data layer, the data layer calls connectAgent(serverId), and that is what reaches the agent. There is exactly one way to dial an agent. It uses mutual TLS, and the agent's certificate fingerprint is pinned when the server is enrolled.

Three consequences worth knowing

  • The machine Deplo runs on is just another server in the fleet. It runs the agent like every other host, and Deplo talks to it over the network like every other host. There is no in-process shortcut for "local", which means the code path you use every day is the one a remote server uses.
  • An unreachable agent is a hard error. Every deploy starts with a live handshake. If it does not answer, the deploy fails and says so. It never silently falls back to building somewhere else.
  • The control plane container must never get the Docker socket. It is the part reachable from the internet, and that mount is root on the box for whoever reaches it. Traefik does not get one either: it reads container events through a read-only socket proxy on an internal network.

What Traefik does

One reverse proxy per server, in a container called deplo-traefik, holding ports 80 and 443. Deplo writes its routing rules and gets out of the way.

Every hostname you add becomes a router. A router carries:

  • the rule (Host(...), optionally with a path)
  • the entry point (web for plain HTTP, websecure for HTTPS)
  • the certificate resolver, when there is one
  • the middleware chain (basic auth, path stripping, redirects)

Certificates are issued by Let's Encrypt over an HTTP challenge and renewed by Traefik itself.

The proxy's stack file on the host is edited in place, comments included, never regenerated from a template. Flags an operator added by hand survive.


Where the data lives

Postgres is the only control-plane store. Apps, teams, domains, deployment history, encrypted secrets, activity: all of it is rows in about 55 tables. There is no document store and no file-based state, and the schema migrates itself at boot.

Your application data lives on the servers, in Docker volumes the agent manages. It is never copied into the control plane, except in transit as ciphertext when a backup goes to a destination on a different host, because agents cannot dial each other.


How secrets move

Secrets are encrypted at rest with AES-256-GCM, using keys derived from DEPLO_SECRET. They are write-only: the ciphertext is never projected into an API response, and no screen has a "show secret" button except two deliberate ones (a basic-auth password and a backup recovery key).

The agent never holds the encryption key. The control plane decrypts at the deploy edge and sends plaintext inside the mutual-TLS call, either as part of the rendered stack or as a 0600 environment file next to it.


What this means day to day

If you seeIt is because
A deploy failed with "agent unreachable"The rule. No fallback build ever happens.
A server card says warningThe agent is up and trusted, but Docker is not answering there, so nothing can deploy.
A secret you set cannot be read backWrite-only by design. Delete and set it again to change one.
The rendered Compose file shows ***Masked on the way out, values and the basic-auth hash alike.
Two apps on one server reach each other by nameThey share the deplo Docker network.

See also

Did this page help you?

On this page