Skip to content

Publish on your own gateway

A gateway is one of your own hosts, typically a VPS with a public IP, that publishes mesh apps on its own address. It is the way to expose raw TCP or UDP publicly: game servers, databases, DNS, anything. Any port works, including ports below 1024, because the gateway is your machine, spending your ports and bandwidth.

The app itself keeps running wherever it already runs (your homelab, an office machine). The gateway just accepts the traffic and forwards it over the mesh, using a direct hole-punched path to the exporting host when possible and the relay otherwise. Published ports keep working through a Console outage.

A gateway is a regular daemon with the gateway role enabled. In the Console’s Connect a host wizard, switch on “this host is a gateway” and the generated command changes accordingly. Two deployment shapes:

The daemon stays unprivileged; for each published port it starts a tiny forwarder container and lets Docker bind the host port (that is how ports below 1024 work without the daemon ever holding privileges). This needs write access to the Docker socket:

Terminal window
docker run -d --name dedmeshd-vps --restart unless-stopped \
--network host \
--group-add "$(stat -c '%g' /var/run/docker.sock)" \
-v /var/run/docker.sock:/var/run/docker.sock \
-v dedmesh-state-vps:/state \
-e DEDMESH_CONSOLE=console.dednets.com:9443 \
-e DEDMESH_NAME=vps \
-e DEDMESH_JOIN_TOKEN=dn_... \
-e DEDMESH_CA_FINGERPRINT=sha256:... \
-e DEDMESH_DOCKER=inventory \
-e DEDMESH_GATEWAY=on \
registry.thenets.org/thenets/dedmesh-daemon

On a plain Linux server the daemon binds the published ports itself, with no container runtime involved:

Terminal window
curl -fsSL https://console.dednets.com/install.sh | sudo sh -s -- --system \
--console console.dednets.com:9443 \
--name vps \
--token dn_... \
--ca-fingerprint sha256:... \
--gateway

This installs a boot-started systemd service running as a dedicated unprivileged dedmesh user, with exactly one extra permission (CAP_NET_BIND_SERVICE) so it can bind ports below 1024. It opens no Docker socket at all, which makes it the narrower-privilege option for low-port publishing.

From the Console: on the Services page, expose an app (or use an existing one) and pick one of my gateways as the destination, then choose the gateway host and the public port. Publications show a state chip: active, pending (gateway offline or still converging), or error with the daemon’s message (for example, a port already in use).

From the CLI:

Terminal window
dedmeshctl gateway add -daemon alice/vps -port 25565 -target alice/homelab/minecraft
dedmeshctl gateway add -daemon alice/vps -port 53 -proto udp -target alice/homelab/dns
dedmeshctl gateway list
dedmeshctl gateway rm -daemon alice/vps -port 25565

Targets follow the usual username/daemon/app naming, and the usual access rules: you can publish your own apps plus apps shared with you, and revoking a share removes its gateway publications.

  • Publications survive restarts and outages. The gateway persists its assignments and rebuilds them at boot, before (and without) a Console connection.

  • UDP is guarded. Public UDP listeners enforce a per-source anti-amplification budget, so a gateway cannot be used as a reflection amplifier.

  • Switching a Docker-mode gateway to native mode: remove the old forwarder containers first, or they keep holding the published ports:

    Terminal window
    docker rm -f $(docker ps -aq --filter label=dedmesh.gateway=true)
  • Public HTTP/HTTPS does not need a gateway. Every account gets derived public hostnames on the DedNets edge; see Expose a port publicly.