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 only 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, pick the Gateway host type and the generated command changes accordingly:

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 itself. This is native mode: no container runtime is involved in publishing, and it is the narrowest-privilege way to publish low ports.

A gateway can instead let Docker do the port binds: for each published port the daemon starts a tiny forwarder container (from the registry.thenets.org/thenets/dedmesh-daemon image) and dockerd binds the host port. Switch an installed gateway to it in its config file:

[gateway]
enabled = true
mode = "docker"

The daemon then needs write access to the Docker socket, so add its group to the unit (SupplementaryGroups=docker, which the installer already grants when the machine has Docker) and restart the service.

From the Console: on the Services page, open the host and 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
dednetsctl gateway add -daemon alice/vps -port 25565 -target alice/homelab/minecraft
dednetsctl gateway add -daemon alice/vps -port 53 -proto udp -target alice/homelab/dns
dednetsctl gateway list
dednetsctl 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.

Serve your domains over HTTPS from your gateways

Section titled “Serve your domains over HTTPS from your gateways”

(Since v0.12.0.) A gateway can also be a web server for your own hostnames. Turn the web option on and the host terminates HTTPS itself, obtains a Let’s Encrypt certificate automatically, and proxies each request over the mesh to the app, wherever it runs - since v0.13.5 over a direct hole-punched path to the app’s host whenever one can be established (two hosts on the same LAN talk to each other directly and their traffic never leaves it), and over the relay otherwise. Every gateway of yours with the option on serves the same hostnames with the same certificates: one of them obtains each certificate and shares it with the others through DedNets end-to-end, so your certificate keys never pass through our servers in the clear.

Enable it at install time by adding --web to the command above:

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 --web

or on an existing gateway host, in dedmeshd.toml:

[gateway]
enabled = true
web = true # binds :443 and :80 on this host
acme = "letsencrypt" # default; "letsencrypt-staging" while you experiment

The same system service that lets a gateway bind low ports covers 443 and 80, so nothing else changes. If the bind is refused the host page says so and names the fix; the option is never worked around with extra privileges.

What a web-enabled gateway serves:

  • Your custom domains on the “My Global Web Servers” door. In the Console’s Domains page pick that door (or dednetsctl domain add example.com -front global), publish one A record per web-enabled host, and every one of them serves the domain - or, since v0.12.1, only the hosts you tick for that domain (-hosts user/a,user/b); a host left out never receives that domain’s certificate key. See Use your own domain.
  • Your public app hostnames ({app}-{username}.dednets.com). Public DNS keeps sending those to the DedNets edge, but a gateway holds a valid certificate for them and serves them whenever a client reaches it with that name - handy for a split-horizon DNS or a hosts-file entry inside your own network.
  • (Since v0.13.4.) Your custom domains on the DedNets edge door, the same way: every hostname you bind under such a domain (a single name, or each label under a wildcard) is also served by your web-enabled gateways, with a certificate of their own, whenever a client reaches a gateway with that name. Nothing changes on the public internet; the point is a LAN resolver that answers *.lab.example.com with a gateway’s private address, so local clients stay local instead of going out to the edge and back. A label you bind later is picked up on its own. Domains on My own node are not served this way (that node answers for them alone), and neither are names under a wildcard that you have not bound. (Since v0.13.6) you may also point such a domain’s public DNS at a gateway instead of the edge: the Console accepts either target, the gateway obtains its own certificates, and the domain stays on the edge door. See Use your own domain.
  • Only your own apps, never one shared with you.

If a gateway refuses a name (the browser reports a certificate or connection error before any page), its log says which name and why: not assigned to it by the Console, or assigned but without a certificate yet, with the site’s state.

An app that requires a DedNets sign-in is gated on the gateway exactly as on the edge: the visitor signs in at the Console and comes back with the same X-DedNets-User headers. The bot check and request logs stay edge-only.

The host page lists each served hostname with its certificate state and, for each, the hosts the certificate key was shared with; those should always be your own web-enabled gateways. dednetsctl gateway web prints the same table.

(Since v0.13.3.) You can switch a host’s web server off and on from the Console without touching the machine: the host page has a Turn web server off button on every gateway that has the option configured, and dednetsctl gateway web off alice/vps / ... on alice/vps do the same. Off closes that host’s 443 and 80, takes it out of the set that shares your certificate keys, and leaves web = true in its config so on is one click. A host that is off shows a muted “Web off” badge instead of its site list.

Certificates survive a restart of the app’s host: a container that goes down and comes back keeps the certificate your gateways already hold, so a compose down && up never spends a Let’s Encrypt order.

A certificate order that fails shows the name as error with Let’s Encrypt’s own explanation, and the gateway retries on its own: five minutes after the failure, then doubling up to once a day (since v0.14.2; earlier releases waited an hour before the first retry). Most first failures are transient on Let’s Encrypt’s side - a DNS lookup timing out at one of its validation vantage points - and clear on the next attempt with nothing for you to do. A rate-limit error waits at least the hour Let’s Encrypt asks for.

  • 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.

Since v0.14.0 a gateway with the web server option can also publish any http(s) address on its own network under a hostname of yours, with HTTPS and an optional sign-in: see Reverse-proxy anything on your network.