Setup
Network configuration
Every port Tyto uses, opening them, NAT and port forwarding, common failures and the security picture.
The ports
| Port | Protocol | Used for | Needed when |
|---|---|---|---|
| 80 | TCP | HTTP + Let’s Encrypt (ACME) challenge | always |
| 443 | TCP | HTTPS — API, app, realtime, voice signal | always |
| 443 | UDP | HTTP/3 | optional |
| 7881 | TCP | Voice media, TCP fallback | voice |
| 50000–50100 | UDP | Voice media (WebRTC/RTP) | voice |
A separate client stack needs only 80/443 on its host. In
reverse-proxy mode the proxy owns 80/443 and
forwards to HTTP_PORT — but the voice media ports still go directly
to the core host. No voice ports needed with
VOICE_ENABLED=false.
Everything else — database, cache, search, the LiveKit signal port — stays on the internal Docker network and is never published to the host.
Opening the firewall
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 443/udp
sudo ufw allow 7881/tcp
sudo ufw allow 50000:50100/udp
On cloud hosts the same list goes into the provider’s security group / network firewall — both layers must agree.
NAT & port forwarding
Hosting behind a home/office router: forward the same ports to the
server’s LAN address. Voice needs the whole UDP range forwarded — the
server discovers its own public IP (STUN) and advertises it to browsers,
which then send media straight at public-ip:50000–50100.
CGNAT breaks self-hosting. If your ISP puts you behind carrier-grade NAT (public IP starts with 100.64–127.x, or your router’s WAN IP differs from what ifconfig.me shows), inbound connections can’t reach you at all — no forwarding will help. Options: ask the ISP for a real IP, or rent a small VPS and tunnel.
Other NAT pitfalls:
- Double NAT (ISP box + your own router) — forward on both, or bridge the ISP box.
- Hairpin NAT — clients on the same LAN reaching the public IP only works if the router supports NAT loopback; otherwise use split-horizon DNS pointing the domain at the LAN address internally.
When something doesn’t work
| Symptom | Usual cause |
|---|---|
| Certificate issuance fails | 80/tcp unreachable from the internet (ACME needs it) |
| Site loads, voice connects then silence | UDP range not open/forwarded — media can’t flow |
| Voice works on wifi, not on mobile | client network blocks UDP; 7881/tcp fallback also blocked |
| Works remotely, not from home LAN | hairpin NAT (above) |
| Nothing reachable despite forwarding | CGNAT (above) |
Top privacy: VPN-only
For a team or family server, the strongest posture is exposing nothing to the internet: put the server and every client device on a VPN (WireGuard, Tailscale) and skip all the forwarding above — no public ports, no ACME, no scanning traffic, CGNAT irrelevant.
One catch: browsers require a secure context (HTTPS) for microphone access and Web Push — plain HTTP inside the VPN silently breaks voice and push notifications. Ways to keep TLS without going public:
- Tailscale —
tailscale servegives the host a trusted*.ts.netcertificate and proxies to the stack: setBEHIND_PROXY=true+HTTP_PORT, pointserveat it. Easiest path. - Own CA — issue an internal certificate (e.g.
mkcert, step-ca) and distribute trust to member devices; terminate TLS in a local reverse proxy, sameBEHIND_PROXYsetup. - Public name, private address — keep a real domain but resolve it to the VPN address; certificates then need a DNS-01-capable proxy in front (Caddy with a DNS plugin, Traefik), since HTTP-01 can’t reach you.
Voice media flows over the VPN like everything else — the UDP range stays closed to the world.
Security picture
- The exposed surface is deliberately small: one web endpoint (TLS by Caddy) plus the voice media ports. Media is DTLS-SRTP encrypted and useless without a session negotiated over the authenticated signal channel.
- Internal services (MariaDB, Valkey, Meilisearch, LiveKit’s signal port) are not reachable from outside — don’t publish them “for debugging” on a public host.
- Brute-force and flood protection is app-level — rate limits — no fail2ban required.
- Keep the Docker API itself unexposed; it’s root on the host.