PearDock is decentralized Docker management on the Holepunch stack. You never need to open ports, expose Docker, or worry about remote access again. You only need keys, peers, and the Engine you already run.
dockerd. No always-on VPN.
Discovery and NAT traversal use HyperDHT. Docker commands ride protomux-rpc on that stream.
Docker Engine is powerful locally. Remotely, people usually open a TLS API port, maintain a VPN, or enroll hosts in a hosted panel. PearDock takes another path: treat the host as a peer you reach with a public key. No open ports, no exposed Engine, no remote-access checklist that never ends.
You can manage a home lab behind CGNAT, a VPS without punching firewall holes, or a fleet of edge machines. Peers need to complete HyperDHT hole punching (or a relay fallback), and you need the right key material.
Most “remote Docker” setups trade convenience for a large attack surface or a permanent middleman.
dockerd TCP + TLS to the internetdocker ps
flowchart LR
subgraph Traditional["Traditional"]
A1[Laptop] --> VPN[VPN / open :2376 / SaaS]
VPN --> D1[dockerd]
end
subgraph PearDock["PearDock"]
A2[Desktop client] -- "HyperDHT + Noise + protomux-rpc" --> S2[peardock-server]
S2 --> D2[dockerd]
end
PearDock v2 identity is a HyperDHT keypair derived from a secret seed. It is not a shared “topic string” like some older swarm designs.
| Material | Who has it | Purpose |
|---|---|---|
SERVER_SEED |
Server only (secret) | 32-byte seed; generates the listening keypair. Never share. |
SERVER_PUBLIC_KEY |
Operators / clients | 64 hex chars. Paste into the client to dial this host. |
| Client keys | Each desktop install | Peer identity for ACL, invites, and reconnect cache. |
On first start the server writes SERVER_SEED and SERVER_PUBLIC_KEY to
/opt/peardock/.env (mode 600) and prints the public key in the journal.
Check the .env file for both values before connecting.
Rotating the seed changes the public key. All clients must re-add the peer.
Back up that file like any other root secret.
hs:// URL.
The server public key is for PearDock control RPC.
Tunnel capabilities are separate secrets created per published port.
Under the hood this is classic Holepunch connectivity: announce, look up, hole-punch (or relay), then encrypt. PearDock’s application layer starts only after that secure stream exists.
Desktop app holds public key + optional invite
Distributed peer discovery & signaling
Listens on keypair next to Docker
The server registers its presence on HyperDHT under its cryptographic identity so dialers can find a path.
The client queries the DHT, then both sides attempt coordinated UDP probes so NAT mappings open (“hole punching”).
A Noise-encrypted stream carries protomux-rpc methods and server→client pushes (stats, logs, events).
sequenceDiagram
participant C as Client
participant DHT as HyperDHT
participant S as peardock-server
participant D as dockerd
S->>DHT: announce identity
C->>DHT: lookup public key
DHT-->>C: candidate endpoints
C->>S: hole punch / connect
C->>S: Noise handshake
C->>S: protomux-rpc handshake
S-->>C: role · protocol version
C->>S: listContainers / deploy / …
S->>D: dockerode
D-->>S: result
S-->>C: response + optional pushes
Most devices sit behind Network Address Translation. Routers map private IPs to public ports and block unsolicited inbound traffic. That is why “just open Docker on the internet” is a bad default, and why P2P needs help to dial home.
Open / consistent mappings → hole punching usually succeeds quickly. Typical home routers often work well.
Symmetric NAT or carrier-grade NAT can block direct paths. The stack may need more probes or a relay. Still no need to expose dockerd publicly.
PearDock benefits from the same Holepunch/libudx generation of tools that power Hyperswarm and Holesail. You don’t configure port forwards for the control channel. You share a key. That is the whole remote-access story: no open ports, no exposed Engine, no perpetual VPN.
Once the encrypted stream is up, PearDock speaks a structured RPC protocol, not ad-hoc JSON lines. That is how the UI stays rich: typed methods, roles, and live pushes.
Desktop client (Electron / Pear) for containers, registry, stacks, templates, Swarm, tunnels, terminals, and the job tray.
Request/response methods + push channels. PROTOCOL_VERSION negotiated on connect (currently 3).
Peer discovery, hole punch coordination, Noise transport under the app protocol.
Server modules for containers, images, volumes, networks, stacks, Swarm, vault, registry browser, image updates, tunnels…
Talks to local dockerd (usually the unix socket; server user in the docker group).
handshake, ping: session setup and livenesslistContainers, checkImageUpdates, start/stop, recreateContainerdeployContainer, deployStack, syncStackFromGitgetContainerLogs, startTerminal, containerStatspush:containers, push:allStats: live fleet view without spam pollingpush:logs, push:terminalOutput: streaming operator surfacespush:pullProgress / push:pushProgress / build progress: hybrid job-tray layer progress when the Engine streams eventsMethod ↔ role maps live in the shared protocol. Viewers can observe; operators mutate day-to-day; admins get destructive and policy-level actions. See RPC & API and Security.
Managing Docker is not the same as reaching a published web app or TCP service.
PearDock keeps a second plane for that: Holesail L4 tunnels.
Still no open host ports. Share an hs:// key instead of a firewall rule.
flowchart LR
subgraph Control["Control plane"]
UI[Client RPC]
SRV[Server handlers]
UI --> SRV
SRV --> DK[Docker Engine]
end
subgraph Data["Data plane"]
HC[Holesail client]
HS[HolesailServer]
HC -- "hs:// capability" --> HS
HS --> Port["host:published-port"]
end
Control -.->|"orthogonal"| Data
| Plane | Technology | Carries |
|---|---|---|
| Control | HyperDHT + protomux-rpc | Docker ops, ACL, logs stream control, deploy, stats |
| Data / tunnels | Holesail | Raw TCP/UDP bytes to a published host port via hs:// |
Why not use only Holesail? Holesail moves bytes between sockets. It is not an RPC framework, role system, or audit log. PearDock needs structured methods and live UI channels, so control stays on protomux-rpc.
Tunnels are on by default (ENABLE_HOLESAIL=0 to disable).
Treat every hs:// URL as a capability secret. Details:
Holesail guide.
P2P does not mean “open to the world.” It means “reachable by those who hold the right cryptographic handles.”
.env: after install, check /opt/peardock/.env for SERVER_PUBLIC_KEY and SERVER_SEED.pd1. invites (no seed sharing), and revoke for multi-operator fleets.
You still must secure Docker itself: who is in the docker group, what images you run, and how secrets enter containers.
PearDock is a control surface. It inherits the power of the Engine socket it uses.
Deep dive: Threat model & hardening · Vulnerability reporting
One desktop client can keep many servers. Each peer is a separate HyperDHT identity with its own role and health.
There is no “log into PearDock cloud.” There is only: which public keys did you add, and which of those peers are online.
From zero to a live containers list. This is the path most operators take.
flowchart TB
A[Install peardock-server on Docker host] --> B["Read keys from /opt/peardock/.env"]
B --> C[Install desktop client]
C --> D[Add peer · key / seed / pd1 invite]
D --> E[Handshake · role · protocol version]
E --> F[listContainers · stats pushes · manage]
F --> G{Need app traffic?}
G -->|Yes| H[Create Holesail tunnel · share hs://]
G -->|No| I[Stay on control plane only]
SERVER_PUBLIC_KEY and (for admin) SERVER_SEED from /opt/peardock/.env.pd1. invite; wait for health.Install: Download & install · Guide: Quick start · Binaries: Releases
Distributed hash table used for peer announcements, lookups, and hole-punch coordination. Peers find each other by cryptographic identity without PearDock hosting a central directory of your fleet.
Encrypted, mutually authenticated transport under HyperDHT connections. Application data (RPC) is not sent in the clear across the peer path.
RPC framework PearDock uses for named methods and server-initiated push channels over a multiplexed stream.
Technique where two NATed peers send coordinated outbound packets so temporary firewall mappings allow a direct path. If both sides are hostile NAT, encrypted relays may assist.
Network Address Translation (and carrier-grade NAT) share public IPs and block unsolicited inbound connections. That is why “open port 2376” is painful and unsafe as a default.
Both are written to /opt/peardock/.env on first start. The public key is what clients dial (viewer if used alone). The seed is secret server identity. Paste it with the public key for admin only. Sharing the seed is equivalent to handing over admin.
Self-contained peardock share string from Access → Create invite. Embeds public key + HMAC capability so operators connect without receiving SERVER_SEED. Paste the full string in Add peer (never truncate).
Peer-to-peer L4 reverse proxy. An hs:// URL is a capability to reach a specific tunnel endpoint. It is separate from PearDock’s Docker control key.
Node.js Docker client used by the PearDock server to talk to the local Docker Engine API (typically via the unix socket).
Server-enforced permission tier for RPC methods. UI may hide actions, but the server is the authority.
Continuously updated binary channel used by the installer and releases page. Recommended for current peardock-server and peardock-client builds.
Install a server next to Docker, paste the public key into the client, and operate without a central control plane.