Self-hosting · the do-it-yourself Private Line · $0

Run the introduction layer yourself.

AllMyStuff already runs a MyOwnMesh daemon for you — that's the open-source mesh your rooms, screens, files and audio ride on. Point that same daemon at a box you keep online, and the signaling, STUN and TURN that introduce your devices become yours.

It's the Private Line — a venue of your own — built by hand instead of rented: same job, $0, MIT-licensed. Here's the whole thing, step by step.

Start the walkthrough Rather we ran it? The Private Line →
The shape of it · what "server mode" means

One daemon. A handful of roles.

There's no separate "server" download. The MyOwnMesh daemon under AllMyStuff is any combination of a regular member and the infrastructure roles it hosts for everyone else. You turn roles on; it starts the listeners and advertises them so your other machines find them on their own. Roles are device-wide — one box can serve your whole fleet.

nodeon by default

Be a normal member. Turn it off on a dedicated box so it hosts services and joins nothing — pure infrastructure.

signaling:4848

Where two devices find each other — a self-hosted relay your fleet uses instead of the shared one. Safe to run publicly (built-in flood limits).

stun:3478

Tells a device the address the world sees it on, so peers can punch a direct path. No auth, nothing stored.

turn:3478

The fallback relay for devices behind hard NAT (phone hotspots, CGNAT). Answers STUN too, so it covers both on one port.

relayoptional

Application-layer routing between your approved devices. Off by default; needs node on. Most fleets don't need it.

For a typical home fleet, signaling + TURN on one box is the whole job — TURN gives you STUN for free on the same port.

Part 1 of 2 · on a box you keep online

Host the servers.

Pick a machine that's always on — a $5 VPS, a Pi in the closet, a spare mini-PC. It doesn't run AllMyStuff; it just runs the MyOwnMesh daemon. We'll take it to a public, TLS-secured signaling relay with STUN + TURN, running as a service that survives reboot.

1

Put the daemon on the box

The mesh under AllMyStuff is MyOwnMesh — free and MIT. On your server, install just the daemon (no desktop app needed to host):

# daemon only — no desktop GUI $ curl -fsSL https://myownmesh.net/install.sh | sh -s -- --no-gui
# Windows (PowerShell) — daemon only PS> iex "& { $(irm https://myownmesh.net/install.ps1) } -NoGui"
# needs cargo — https://rustup.rs $ git clone https://github.com/mrjeeves/MyOwnMesh $ cd MyOwnMesh $ cargo install --path crates/myownmesh

Run it once in the foreground to confirm it starts (we make it permanent in step 6):

$ myownmesh serve
2

Make it pure infrastructure

A dedicated relay box shouldn't be a member of your fleet — it should just carry introductions. Turn node off so it hosts services and joins nothing:

# host services, join no networks $ myownmesh ctl services disable node

ctl talks to a running daemon over its local socket, so keep myownmesh serve running in another shell while you configure (or jump to step 6 and run it as a service first).

3

Turn on signaling, STUN & TURN

The simple on/off is one command each. STUN comes free with TURN, so you usually only enable the two:

$ myownmesh ctl services enable signaling $ myownmesh ctl services enable turn # answers STUN on the same port $ myownmesh ctl services status

TURN needs a couple of things the on/off toggle can't set — a login and the box's public IP. Open the config (myownmesh config edit) and fill in the turn block:

~/.myownmesh/config.json
// only the bits you must set are shown { "services": { "signaling": { "enabled": true, "bind": "0.0.0.0", "port": 4848 }, "turn": { "enabled": true, "bind": "0.0.0.0", "port": 3478, "public_ip": "203.0.113.7", // this box's routable IP "realm": "myownmesh", "credentials": [ { "username": "alice", "password": "use-a-real-secret" } ] } } }
Heads upnode defaults on and signaling ships safe flood-limit defaults, so neither needs to appear by hand. After editing the file, restart the daemon — or re-apply live from the CLI / the desktop GUI's Settings → Services.
4

Open the UDP ports — the step everyone misses

:3478 is only TURN's control channel. Every relayed connection flows through a separate UDP port, drawn from the OS ephemeral range by default — so you open that whole range too. The enable turn command prints the exact list:

$ myownmesh ctl services enable turn TURN is on. For NAT'd peers to actually relay, these UDP ports must be reachable — at the host firewall AND your cloud security group: • udp 3478 — STUN/TURN control • udp <OS ephemeral range> — relay allocations (one port per peer) find your range: sysctl net.ipv4.ip_local_port_range (e.g. 32768 60999) $ sudo ufw allow 3478/udp $ sudo ufw allow 32768:60999/udp
Why nothing connectsA host firewall reading inactive does not mean your cloud provider lets the packets in — open the ports in the security group too. Forgetting the UDP range is exactly why clients show 0 srflx · 0 relay candidates and fall back to nothing.
  • No credential, no TURN. It won't start without at least one username/password — it'll show enabled, not running.
  • No public IP on a wildcard bind, no TURN. It can't guess its own routable address.
  • Smaller firewall rule? Pin relay_port_min/relay_port_max (e.g. 4915265535) and open only that range.
5

Give signaling a name and a padlock

The relay speaks plain ws://. For your fleet to reach it from anywhere you want TLS on 443 (wss://, which also slips through restrictive networks). One command installs Caddy, writes the proxy, locks the relay to loopback, and starts it:

$ myownmesh install caddy relay.example.com
go public over wss:// — one command
$ myownmesh install caddy relay.example.com Caddy installed. Wrote reverse-proxy block to /etc/caddy/Caddyfile Caddy service is running with the new config. Relay enabled and bound to 127.0.0.1 (reachable only via Caddy). Done. Peers can now point at wss://relay.example.com

Two things still have to be true for the certificate to come up:

  • DNS — an A/AAAA record for the hostname points at this box.
  • Firewall — inbound TCP 80 and 443 open (Caddy needs 80 for the certificate challenge).
Give it its own subdomainPoint your fleet at the relay's hostname — a box that runs the daemon. A static site can't host a WebSocket server, so wss:// a marketing domain hits the website, not a relay. Use a dedicated name like relay.example.com that resolves to your server.
6

Keep it running forever

Register the daemon with the box's init system — systemd on Linux, launchd on macOS — so it comes back after a reboot:

# per-user: no root, starts at login $ myownmesh service install # or system-wide: root, starts at boot — best for a headless server $ sudo myownmesh service --system install

service status shows installed / enabled / running; start, stop, restart, uninstall do the rest. That's the box done — now point your devices at it.

Part 2 of 2 · in the app, on every machine

Point AllMyStuff at your servers.

No config files on this side — it's a settings panel. In the desktop app, open Settings → Networks, pick your network, and open its Servers editor.

SettingsNetworksyour networkServers

Replace the three lists with your own box, then Save & reconnect:

  • Signaling relayswss://relay.example.com (no port — 443). This is the one that matters most: both ends of a network must share a signaling relay to find each other.
  • STUN serversstun:relay.example.com:3478.
  • TURN serversturn:relay.example.com:3478, with the username and credential you set in step 3.
Do it on every machineEach device in your fleet keeps its own server list. Set the same three on every machine that talks to the others — phone, laptop, the lot. The app's Reset to MyOwnMesh defaults button is always there if you want to fall back to the shared servers.

Headless box with no app? Set the same three under each network's signaling.servers / stun_servers / turn_servers in its config.json.

The payoff

A fleet that touches nothing you don't own.

Point signaling, STUN and TURN all at your own box and turn off the public fallback, and your devices introduce themselves entirely on infrastructure you control — no shared servers, no third parties in the path.

your servers only

Set the signaling list to your wss:// host and clear the public fallback (public_fallback: false in config). STUN/TURN as explicit lists pointing only at your box.

trust is unchanged

Who-is-who is proved with signatures over the encrypted channel, no matter who runs signaling. A relay only ever introduces devices — it never sees your screens, files or audio, and can't forge a device.

it degrades gracefully

Your relay is an accelerator, not a single point of failure. If the box goes down and you've left the fallback on, devices drop back to the shared servers and keep working.

Want this without keeping a server alive yourself? The Private Line is exactly this, run for you at 1 Gbps, for $10/mo. Same network model — one is your time, one is your money.

Is it working?

Verify it, and fix the usual snags.

On the server, ctl services status shows what's bound and — for signaling — whether devices are actually reaching you. That separates "nobody's connected yet" from "I'm broken."

myownmesh ctl services status
$ myownmesh ctl services status { "node": { "enabled": false, "joined": 0 }, "signaling": { "enabled": true, "running": true, "listen": "127.0.0.1:4848", "activity": { "connections": 2, "rooms": 1, "events_relayed": 42 } }, "turn": { "enabled": true, "running": true, "listen": "0.0.0.0:3478" } }

Bound, but connections: 0

The relay's fine — traffic isn't arriving. Check, in order: DNS (does the name resolve here?), TLS/proxy (run npx wscat -c wss://relay.example.com — expect a connect), then the firewall.

0 srflx · 0 relay on clients

UDP isn't getting through. You opened 443 for the proxy but not TURN's control port and its relay range — at the host firewall and the cloud security group. Re-run the step-4 checklist.

TURN: enabled, not running

It needs a credential, and a public_ip on a wildcard bind. Set both in config.json, then re-apply or restart the daemon.

Devices still won't meet

The #1 cause is two machines on different signaling relays. Make sure every device lists the same wss:// host under Settings → Networks → Servers.

Reference

The knobs, in one place.

signaling.port
4848, plain ws://. Front it with Caddy for public wss://.
stun.port / turn.port
3478. Don't run standalone STUN and TURN on the same port — TURN covers both.
turn.public_ip
Required on a wildcard bind. The routable address handed out for relayed connections.
turn.credentials
At least one { username, password }. Mirror a pair into each device's TURN entry.
turn.relay_port_min / max
0 = the OS ephemeral range (open it all). Pin a window to shrink the firewall surface.
turn.max_bps_per_connection
Per-connection byte/sec cap, each direction. 0 = unlimited.
signaling.limits
Per-connection flood guards. Defaults: 50 events/s, 20 REQ/s, 64 subs, 16 filters/REQ, 65536 bytes, 64 conns/IP. 0 = no limit.
config & state
~/.myownmesh/config.json. Move the whole tree with MYOWNMESH_HOME.
SERVICES.md

The full operator guide — every field, the discovery model, running a public relay.

MyOwnMesh

The mesh under AllMyStuff — pure-Rust, MIT, the daemon you're hosting.

The Private Line

The same thing run for you — $10/mo, 1 Gbps, cancel anytime.