Expose a port publicly
Any HTTP service running on one of your hosts can get a public HTTPS address
served by the DedNets edge. The hostname is derived automatically as
{app}-{username}.{domain}; TLS certificates are issued on demand at the
first request. You never open a port on your own network: traffic enters at
the edge and rides the daemon’s existing outbound connection.
There are three ways to mark a service public. All of them end up in the same place; pick whichever fits how the service runs. Prefer your own domain? See Use your own domain.
From the Console (no config at all)
Section titled “From the Console (no config at all)”Open Services. The left rail lists your hosts and everything each one publishes; click a host to open it. (The page became this explorer in v0.10.1 — it used to be one collapsible card per host.)
A host’s pane shows its containers with one chip per port. A chip you can click is a port nothing publishes yet; a green one names the service already using it. Click a free chip and the publish dialog opens with the container and port filled in — give the app a name, pick a protocol, and choose whether it gets a public DedNets hostname.
A container only advertises the ports its image EXPOSEs and the ones it
published to the host, so a service listening on anything else has no chip. The
dashed port… chip at the end of the row is for exactly that (since
v0.11.2): type the port and its transport, and publish it like any other. The
daemon dials the container directly, so any port it really listens on works —
the chips are a convenience, not the limit.
The protocol choices are evidence-driven: a UDP port offers only UDP, and a port
the daemon probed and found answering something other than HTTP will not offer
http. A port you typed was probed by nobody, so the dialog takes your word for
it — though the protocol still follows the number until you pick one (5432
suggests tcp, 8080 http). Below the containers, Host ports lists what is
listening on the machine outside Docker, with the same Expose action — and the
pane’s Expose a port button takes a port number directly, for anything
discovery has not found.
That page also has a Use with AI button (since v0.4.3): if you would rather have an agent do it, it hands your assistant the exact steps.
Once published, the service appears in the rail with a health dot and its 24-hour uptime, and clicking it opens everything about it: every hostname that reaches it, the request path a request takes to get there, its traffic and latency, and Remove when you are done with it. (That button was called Unpublish before v0.11.2. It takes the whole service away; dropping only its DedNets hostname is the × on that hostname’s row, below.)
Taking the DedNets hostname away again
Section titled “Taking the DedNets hostname away again”The DedNets hostname is not permanent, and dropping it is not the same as
unpublishing the service. Open the service, find the row for
{app}-{username}.{domain} under Hostnames, and click the × on it
(since v0.11.2). The edge stops serving that name, and everything else stays: any
domain of your own bound to the app keeps serving it, and your other hosts keep
reaching it over the mesh at {username}/{daemon}/{app}. Click Add
hostname → The DedNets name to put it back.
That is the usual last step of moving an app onto your own domain: bind your name, check it serves, then retire the DedNets one so the app answers on a single address.
The × is there only for a service the Console publishes (its badge reads
console). For one published by a Docker label or the daemon’s config file,
the switch lives where the app is declared — set dedmesh.public: "false" or
public = false and the hostname goes away on the daemon’s next snapshot. If
the host is offline when you turn the name off, it keeps answering until the
host reconnects and applies the change; the Console says so when that happens.
Keeping it private
Section titled “Keeping it private”A public HTTP app can be put behind a DedNets login instead of being open to
anyone with the URL: tick Require a DedNets sign-in in the publish dialog, or
set dedmesh.auth / auth / -auth dednets below. Only you and the accounts
you allow get in, and everyone else is stopped at the edge. See Put an app
behind a DedNets login (since v0.10.0).
With Docker labels
Section titled “With Docker labels”If you prefer the service to declare itself, label its container:
# docker compose exampleservices: blog: image: ghost labels: dedmesh.enable: "true" dedmesh.port: "2368" dedmesh.name: "blog" # default: the container name dedmesh.protocol: "http" # http (default) | tcp | udp dedmesh.public: "true" # request the public hostname dedmesh.auth: "dednets" # optional, since v0.10.0: require a DedNets # sign-in - none (default) | dednetsThe daemon watches Docker events, so starting the container publishes the app and stopping it unpublishes it. No Console clicks needed.
In the daemon’s config file
Section titled “In the daemon’s config file”For a daemon run from a config file, add the app there:
[[apps]]name = "blog"protocol = "http"local_host = "127.0.0.1"local_port = 2368public = trueauth = "dednets" # optional, since v0.10.0: none (default) | dednetsWhat about raw TCP and UDP?
Section titled “What about raw TCP and UDP?”Raw TCP/UDP is published by a gateway: one of your own hosts (say, a VPS) publishes the port on its own address, any port you like, including 80, 443, or 53. See Publish on your own gateway.
DedNets never binds a raw port on its own infrastructure for you - the shared edge serves HTTP/HTTPS only (the admin-only raw edge ports were removed in v0.6.2).
Services that should not be public at all can stay mesh-only: other daemons of yours (or users you share the app with) reach them through local proxy ports, and nothing is exposed to the internet.