Skip to content

Running Apex in Docker

Alex Kollar edited this page Nov 14, 2026 · 1 revision

Apex ships as a Docker image: a headless node in a container — the same bridge, the same Mesh door, the same DHD door, no desktop window. It is the easiest way to stand up a second (or a first) Mesh node: a homelab box, a NAS, a spare machine, a VPS. The image is free and AGPL, like the rest of Apex; there is no subscription anything.

No identity is ever baked into the image — the node's Mesh keypair, its certificate, and its pairing card are minted on the machine that runs it, at first boot, inside the data volume. A shipped keypair would make every copy the same node, and one leak would be everyone's leak. The model binary is not baked in either: on first run the container downloads navi.llamafile once — the installer's own pattern — and keeps it in the data volume, so the image stays small and the model persists across container replacements. One binary serves both architectures: the image is built for amd64 and arm64.

Up in one breath — DHD first

docker compose up, then read the first-boot output (the container's logs): a boxed banner prints your node's DHD access code, once, next to the node's name and Mesh fingerprint. Then browse to https://<this computer's address> — the DHD door, published on port 443 by the shipped compose file — type the code, name your device, and you are in: the first device at the door trusts itself with the code, and every device after it waits for an approval from one that is already trusted, exactly as on bare metal. Write the code down or take a photo of the banner. It is the only key to the DHD door; it is printed in the logs that one time only. The same banner is kept in a file named FIRST-BOOT.txt in the data volume, for the operator who missed the scroll.

The LAN-trust sentence, plainly: in the container, DHD is on from the first boot — the door answers on your local network from the moment the node starts. That is the container's posture, chosen because a container has no desk to sit at; a bare-metal install keeps DHD off until its operator turns it on. On-by-default never means open: anyone on the network still needs the code to knock, and the trusted-device list in Settings → System → Door is the roster of who got in, each with its own token you can revoke.

If you lose the code, it is a designed path, not a lockout — you need access to the computer the container runs on. Re-print the current code: docker exec <container> python3 /opt/navi-apex/bridge.py --dhd-code. Or mint a new one (the old code stops knocking at once; devices already trusted keep their own tokens): docker exec <container> python3 /opt/navi-apex/bridge.py --dhd-code-reset.

Ports, volumes, and the card warning

The shipped compose file publishes the Mesh door — 8765, 8443, 2475 (one door, one certificate, every number) and the DHD door — 443, plus 80 for its certificate machinery. The bridge itself keeps its standing loopback binding inside the container; the DHD proxy fronts it on the container's network face — the same architecture as bare metal, composed. Publish the Mesh ports onward (router forwards) only if the node should be reachable from outside your network, exactly like a bare-metal node's door.

One named volume — apex-data over the whole app directory — carries everything that makes the node this node: its identity, its config, its chats, its chips, the model. Replacing the container keeps all of it: the installer's preservation law, in container form. Updates ride the product's own in-app updater inside the volume, as on bare metal.

The Docker card warning: inside a container there is no physical LAN interface for the card generator to harvest — the addresses it could find are the container's own plumbing, and a card carrying them would strand anyone you hand it to. The declared public endpoint is the honest address for a container node: set APEX_PUBLIC_ENDPOINT in the compose file (or on the Mesh page) to the host:port peers should dial, before minting cards for others.

What the container does not do

No desktop window and no voice engines in this image — it is a node, run headless and driven through DHD. GitHub sign-in, Quick Sync, chips, Guardian, the Threat Intelligence Center, and the Mesh (pairing, borrowing, lending, the backup route, and relay) work as they do on bare metal, because it is the same bridge throughout.

Clone this wiki locally