Deploy a daemon
Every machine you connect runs one daemon, dedmeshd. It makes a single
outbound connection to the Console and needs no inbound firewall rules and no
special privileges: root is used once, to install it as a service that runs as
an unprivileged user.
The easiest path is the Console’s Connect a host wizard (on the Hosts page). It walks three steps:
- Name the host. The Console mints a single-use join token for it.
- Run the command it generates on the machine. The command embeds the join token and the Console’s CA fingerprint, so there is nothing to edit.
- Verify: the wizard watches until the host enrolls and shows its connection transport and discovered containers.
The generated command is shown below with placeholders so you know what you are running. Prefer copying the exact command from the wizard.
Linux server (systemd)
Section titled “Linux server (systemd)”The wizard installs a boot-started systemd service:
curl -fsSL https://console.dednets.com/install.sh | sudo sh -s -- --system \ --console console.dednets.com:9443 \ --name myhost \ --token dn_... \ --ca-fingerprint sha256:...This one must run as root. It writes a unit under /etc/systemd/system,
creates the dedmesh service account, and (see below) may hand that unit the
Docker socket’s group. The service itself still runs as the dedicated
unprivileged dedmesh user, starts at boot, and keeps everything (config,
credentials, and a self-updatable copy of the binary) under
/var/lib/dedmesh-myhost. Root is for the install, not for the daemon.
Run it without root and nothing happens: the installer refuses before it
downloads anything, so the machine is left untouched and the single-use join
token is not spent. It prints the exact line to re-run, and if a terminal is
attached it offers to re-run under sudo for you.
Three variants, depending on the access you have on that machine:
| your session there | what to run |
|---|---|
a normal account with sudo |
the command above, as written |
already root (many minimal cloud and container images have no sudo at all) |
the same line with | sudo sh changed to | sh |
ssh myhost '<command>', non-interactive |
sudo cannot prompt for a password without a terminal and fails with “sudo: no tty present”. Use ssh -t, a NOPASSWD sudoers rule, or log in as root. For unattended runs prefer NOPASSWD or root over ssh -t: with a TTY attached, the installer’s “existing installation found” prompt also becomes interactive |
| no root at all | hand the line to whoever has it; there is no workaround, and the installer will not half-install |
If this server also runs Docker (since v0.6.1): the daemon looks for a local engine on every install, so your containers show up in the Console too. Reading the socket takes membership in the group that owns it, and setup wires that into the unit for you:
# systemctl cat dedmeshd-myhostSupplementaryGroups=dockerIt follows whatever group actually owns the socket on this host (docker,
dockerroot, or a site-specific name), and if Docker is installed but not
started yet it uses the docker group. Setup prints what it granted.
Worth knowing before you accept it: that group is effectively root on the
machine. Anything that can talk to the Docker socket can start a privileged
container. It is granted automatically because this command already required
root to run, because it is scoped to this one unit rather than added to the
dedmesh account for everything, and because it is easy to take back:
# delete the SupplementaryGroups line, then:sudo systemctl daemon-reload && sudo systemctl restart dedmeshd-myhostThe restart is not optional. daemon-reload rewrites the unit, but the process
that is already running keeps the group it started with until it is replaced.
Simpler still: add --no-docker to the install command to decline it up front,
or re-run the installer with --no-docker to drop it. A host with no Docker
gets no line at all.
If you install Docker after enrolling, re-run the install command (or
sudo dedmeshd setup --system with --token omitted) so the unit is rewritten;
until then the daemon logs a warning that the socket is present but unreadable.
Config file instead
Section titled “Config file instead”If you build or download the binary yourself, a minimal TOML config replaces the environment variables:
console = "console.dednets.com:9443"name = "myhost"join_token = "dn_..." # first run onlystate_dir = "dedmeshd-state"ca_file = "ca.pem" # the Console CA, pinnedgeo = "auto" # auto (default) | off - see "Where your hosts # appear on the map" below. Since v0.11.2
[[apps]] # optionally export a service right awayname = "nginx"protocol = "http" # http | tcp | udplocal_host = "127.0.0.1"local_port = 8080public = true # gets a public hostname, see the expose guideRun it with dedmeshd -config myhost.toml, or let dedmeshd setup install it
under systemd (Linux) or launchd (macOS) as your own user.
Exporting a unix socket instead of a port
Section titled “Exporting a unix socket instead of a port”Since v0.10.1 an app may front a local unix-domain socket with local_path
instead of local_host / local_port:
[[apps]]name = "haunted"protocol = "tcp"local_path = "/run/user/1000/haunted/daemon.sock"local_path must be an absolute path, requires protocol = "tcp", and cannot
be combined with local_port or local_host. These apps are not health-probed
(there is no port to dial), so they report health unknown. The socket’s own
file permissions are what protect it on the host; reach it from another machine
with a proxy port rather than publishing it.
A config with no [docker] section discovers containers by default (since
v0.6.1), tolerating a machine with no engine at all. Add the section to change
that:
[docker]enabled = false # no discovery at all# enabled = true# inventory = false # discover, but do not list containers to the ConsoleWhere your hosts appear on the map (since v0.11.2)
Section titled “Where your hosts appear on the map (since v0.11.2)”The Console’s overview opens on a map of your fleet. To put a host on it, the
daemon asks https://ip.thenets.org/ where it is and reports the answer home:
the public IP that endpoint saw, the country, region and city it resolves to,
and a latitude/longitude. That is the whole list. Nothing about what runs on the
machine, what is on its network, or who uses it ever goes to that endpoint - the
request carries no information about your host beyond the address it is coming
from, which is the address any site you visit already sees.
It is on by default, and there are two independent ways to turn it off:
- Per host, in the config above:
geo = "off"- orDEDMESH_GEO=offin the environment of a host that runs with no config file. - Per account, in the Console: Settings → Data collection. Turning it off there covers every host you own at once, including ones you cannot easily edit.
Either switch stops the lookup and deletes what the daemon cached
(<state_dir>/geo.json), so nothing is left behind. Hosts with no location are
not dropped from the overview - they are listed beside the map as “location
unknown”, because a host you cannot see is worse than a host you cannot place.
The lookup is not a poll. The daemon calls that endpoint on its first run, when the Console notices its public address has changed, and at most once every 12 hours otherwise. A restart re-queries nothing.
Verify
Section titled “Verify”The wizard’s third step confirms enrollment automatically. Outside the wizard, the Hosts page shows the machine online with its connection transport, and the CLI agrees:
dednetsctl daemonsGood to know
Section titled “Good to know”- Location is on, and opts out two ways. Hosts report their public location
so the Console can map them;
geo = "off"(orDEDMESH_GEO=off) turns it off for one host, and Settings → Data collection for the whole account. (Since v0.11.2.) - Tokens are single-use. If a command fails before enrolling, generate a fresh command from the wizard.
- Updates are opt-in. Daemons do not update themselves unless you turn the rollout on; the Hosts page shows each host’s version and update state. A host whose install directory is read-only cannot update itself; re-run the install command on it to move it.
- Revoking a host (trash icon on its row) deletes it and everything it published; its certificate stops working on the next connection.
- An agent can do this for you. With the MCP server connected, an AI agent enrolls the host and hands you the exact install command: Let an AI agent run DedNets. (Since v0.4.1.)