Running in Docker
Two images — which one to run, what is in each, and why neither contains any model weights.
MeshHold ships two container images. They run the same daemon, from the same binary; what differs is which of other people's programs are installed beside it.
meshhold:<version>-<arch> |
meshhold:<version>-tools-<arch> |
|
|---|---|---|
| Base | distroless static | Debian slim |
| Size | ~14 MB compressed | roughly thirty times that |
| Shell | none | yes |
| ffmpeg, LibreOffice | no | yes |
| llama.cpp, whisper.cpp, sherpa-onnx, stable-diffusion.cpp | no | yes |
| Model weights | no | no |
The last row is the one people are surprised by, so it is worth saying plainly: neither image contains any model weights. They are gigabytes, they are chosen per node, and the node downloads the ones you ask for into a vault — where they replicate to your other trusted machines, so a second node does not fetch the same eight gigabytes again.
Which one
Take the plain image unless you know you want the other. It is a storage, sync, chat and VPN node, which is most of what MeshHold is, and it is a distroless image with no shell in it — a smaller thing to keep patched and a smaller thing to break into.
Take the -tools image when you want that node to do work on the files
it holds: convert documents, transcode video, transcribe recordings, read
them aloud, index them for search, or answer questions about them. Every
program on the external tools page is
already in it, at a version the build pins.
The two are interchangeable at any time. Nothing in a node's data depends on which image wrote it, so swapping the image and restarting is a supported move in both directions — and it is the quickest way to find out whether a job type you are missing is missing for want of a program.
Loading and running
The release publishes each image as a gzipped OCI tarball rather than to a registry:
gunzip -c meshhold-<version>-amd64.tar.gz | docker load
docker run -d --name meshhold \
-p 7777:7777 -p 8080:8080 \
-v meshhold-data:/var/lib/meshhold \
-v /srv/meshhold/config.yaml:/etc/meshhold/config.yaml:ro \
-e MESHHOLD_PASSWORD='choose-something' \
meshhold:<version>-amd64
7777/tcp is libp2p — the port peers reach this node on, and the one worth
forwarding. 8080/tcp is the Web UI and REST API, and is not something to
expose to the internet without thinking about it first.
/var/lib/meshhold is the volume: the metadata store and the encrypted
blocks. The config file is deliberately not in it — it is a bind mount,
or absent entirely, in which case the daemon comes up in limited mode and
the Web UI walks you through setup.
MESHHOLD_PASSWORD is read once. If the store has no password hash yet, the
daemon writes an Argon2id hash of it on first boot and ignores the variable
from then on, so leaving it in a compose file does not keep resetting
anything.
The -tools image runs identically — same ports, same volume, same
entrypoint — under uid 65532, with HOME inside the volume so that anything
a tool downloads for itself survives a restart.
Checking what the node can do
docker exec meshhold meshhold tools --api http://127.0.0.1:8080
On the plain image this lists everything as missing, which is correct and
expected. On the -tools image everything should read yes; anything that
does not is worth reporting, because that image exists precisely so nobody
has to install these by hand.
The same list is in the Web UI under Settings → Tools, with the model store beneath it.
Building the images yourself
build_docker.bat
It expects the Linux binaries to already exist under distr/linux/<arch>/
from build_linux.bat, and deliberately does not build them itself: one Go
binary, several packagers, no chance of the .deb, the .msi and the image
drifting apart in toolchain version or build flags.
The tools image is slow to build — it installs LibreOffice and fetches four model runtimes — so it is off by default and amd64-only when on:
set SKIP_TOOLS=0
set TOOLS_ARCHES=amd64 arm64
The runtime archives are cached across builds by a BuildKit cache mount, so the second build is much faster than the first.