Guides
Self-hosting `kultd`
Komms 0.1 Alpha publishes a Linux container for kultd, the runnable headless
service built around the kult-node library. It is intended for people who want
their own always-on peer, volunteer mailbox, relay-aware node, or
internet-to-Meshtastic bridge. It is not a central Komms server and other users
do not need it in order to communicate.
The public Alpha package
supports linux/amd64 and linux/arm64. Pull the immutable release tag with:
docker pull ghcr.io/andrigitdev/komms-kultd:0.1.0The 0.1-alpha and alpha tags are moving Alpha aliases; the committed Compose
file tracks 0.1-alpha, while automation should pin 0.1.0 or an image digest.
The image runs the daemon as numeric user/group 10001, stores its sealed
database in /var/lib/komms, and listens on TCP and QUIC/UDP port 4404 by
default. There is intentionally no latest tag during the Alpha series.
Start with Docker Compose
The committed compose.yaml uses named volumes for both the
encrypted node data and an owner-only passphrase file. Initialize the secret
interactively, then start the node:
docker compose run --rm --no-deps --entrypoint kultd-init-passphrase kultd
docker compose up -d
docker compose ps
docker compose logs -f kultdThe first command writes the passphrase directly into the private secrets volume; it does not put the value in the Compose file, shell history, or container environment. Back up the passphrase separately. Losing it makes the encrypted node database unusable. Anyone who obtains both it and the data volume can decrypt the node, so do not back them up together.
The image health check calls the local Unix-socket RPC. Inspect the node or export its pairing bundle with:
docker compose exec kultd kult status
docker compose exec kultd kult bundleStop with docker compose down. The named volumes remain. Adding --volumes
deletes the node database and passphrase and is therefore destructive.
Network and operating modes
Open and forward both 4404/tcp and 4404/udp on the host and firewall. The
TCP listener provides the Noise/Yamux fallback; UDP carries the primary QUIC
transport. AutoNAT, relay reservations, and hole punching still apply, so a
port-forward is helpful but not a claim that every NAT permits inbound traffic.
The default disables mDNS because a bridged container does not represent the
host LAN. Komms operates no mandatory bootstrap service: add trusted bootstrap
or relay addresses, or distribute explicit reachable peer hints, when the node
must discover peers beyond its container network.
To add daemon flags, replace the Compose service's command while retaining both listen addresses. For example, a volunteer mailbox with an explicit bootstrap peer can use:
services:
kultd:
command:
- --listen
- /ip4/0.0.0.0/udp/4404/quic-v1
- --listen
- /ip4/0.0.0.0/tcp/4404
- --bootstrap
- /dns4/example.net/tcp/4404/p2p/PEER_ID
- --serve-mailbox
- --no-mdnsFor a network-attached Meshtastic radio, append --meshtastic-tcp HOST:4403.
For USB serial, pass the device through to the container and append
--meshtastic-serial /dev/ttyACM0; device names and host permissions vary by
OS. Bridging sealed third-party traffic is enabled when a radio is configured
unless --no-bridge is supplied. Review the airtime and bridge limits in the
transport specification before volunteering bandwidth.
Run docker compose run --rm kultd --help for every daemon option. Do not place
the passphrase in KULTD_PASSPHRASE for long-lived deployments: environment
variables can be exposed by process and container inspection tools. A direct
docker run deployment should instead mount an owner-only file at
/run/komms-secrets/passphrase and persistent storage at /var/lib/komms.
Alpha limits and upgrades
The container is built from the same tagged AGPL source as the application artifacts and carries OCI provenance and an SBOM. It is still Alpha software: back up before upgrading, pin a versioned tag for automation, and verify the image digest shown by GHCR. Multi-host orchestration, remote administration, automatic backups, and a stable migration/support promise are not provided. The RPC socket is deliberately local to the container and must not be exposed over TCP.