A wireless-first software-defined gateway for ARM single-board computers. Tunneld bridges Wi-Fi and Ethernet to create a private subnet, manages devices via DHCP, resolves DNS securely, reverse-proxies local services with auto-SSL, and optionally exposes them through Zrok overlay tunnels.
Designed to be lightweight and portable — run it at home as a smarter router, or take it anywhere as a self-contained network appliance.
Prerequisites
- Hardware: An ARM64 SBC with both wireless and ethernet interfaces (Raspberry Pi, NanoPi, etc.)
- Operating System: Debian-based OS
- Zrok (optional): A self-hosted Zrok control plane or account for overlay networking
Connects upstream via Wi-Fi and serves a private subnet over Ethernet. Devices plug in and receive IPs, DNS, and internet access — no existing router UI needed. Everything is controlled through the Tunneld dashboard.
Implements the CAKE algorithm to eliminate bufferbloat and reduce latency. Choose between latency-optimized, balanced, or no shaping — applied in real-time via tc.
Generates a Root CA on first run. Every resource gets a TLS certificate signed by your CA, served through nginx. Install the Root CA on your devices for trusted HTTPS across your subnet.
All DNS queries on the subnet are intercepted via iptables and routed through a user-configured DNS server. Point the gateway at any resolver — Cloudflare (1.1.1.1), Google (8.8.8.8), or a local Pi-hole on your network. Same-subnet DNS servers are supported via automatic prerouting rules.
Define resources that point to services running on your subnet. Each resource has a pool of backends that are health-checked via TCP probes. Nginx load-balances across healthy backends with auto-generated configs.
Devices on the subnet can create, list, and remove public Zrok shares with a single curl — no credentials required. The gateway resolves the caller from its DHCP lease and immediately provisions a public URL. Enable per-device from the dashboard.
Expose resources publicly or privately through Zrok tunnels without port forwarding. Share services across Tunneld instances — bind remote shares locally and add them to your nginx pool for distributed load balancing.
Connect multiple Tunneld nodes into a single mesh through a relay coordinator. Each node registers outbound-only over WireGuard, receives a mesh IP, and syncs peers automatically. Tag LAN devices with wg:: prefixes to expose them to the mesh — no port forwarding required.
Combine local and remote backends in a single resource pool. Nginx distributes traffic across all entries — whether they're on your subnet or bound from a peer's Tunneld instance over the overlay.
Guided onboarding flow after initial account creation: connect to Wi-Fi, optionally configure the overlay network control plane and mesh relay.
| Component | Role |
|---|---|
dnsmasq |
DHCP server + DNS resolver forwarding to user-configured upstream |
nginx |
Reverse proxy with per-resource SSL and upstream load balancing |
iptables |
NAT, packet forwarding, and DNS interception |
Zrok v2/OpenZiti |
Overlay tunnel orchestration (namespace names, share, access) |
WireGuard |
Mesh networking interface (wg-mesh) for node-to-node connectivity via relay |
Elixir/Phoenix |
Application server, LiveView dashboard, GenServer process management |
Detailed architecture diagrams with Mermaid (rendered on GitHub):
- Network Topology — How Tunneld bridges Wi-Fi upstream and Ethernet downstream
- Resource Lifecycle — Creating resources, enabling public/private shares, binding remote access
- Distributed Load Balancing — Combining local and remote backends in nginx pools
- Nginx & SSL — Certificate chain, config generation, and hairpin DNS
- Supervision Tree — OTP process map, polling intervals, and PubSub topics
Tunneld is designed for Debian-based SBCs such as Raspberry Pi, NanoPi, or any custom ARM64 setup.
curl -sSf https://raw.githubusercontent.com/toreanjoel/tunneld-installer/main/install.sh | sudo bashThe installer handles all dependencies: dnsmasq, dhcpcd, nginx, iptables, and zrok2.
lib/
tunneld/
application.ex # OTP supervision tree
config.ex # Shared config helpers
persistence.ex # Atomic JSON file persistence with backup recovery
iptables.ex # iptables firewall rule management
cert_manager.ex # SSL certificate lifecycle (root CA + per-resource)
servers/
session.ex # In-memory IP-keyed auth sessions
auth.ex # Login credentials (bcrypt + WebAuthn)
resources.ex # Resource registry (CRUD, Zrok shares, nginx, DNS, health)
devices.ex # DHCP lease monitoring and revocation
services.ex # systemd service monitoring (dnsmasq, dhcpcd, etc.)
wlan.ex # Wi-Fi interface management (wpa_supplicant)
nginx.ex # Nginx reverse proxy config generation
dns_config.ex # DNS server IP configuration
zrok.ex # Zrok v2 CLI orchestration (names, shares, access units)
sqm.ex # Smart Queue Management (tc/CAKE)
wireguard.ex # WireGuard keypair and wg-mesh interface
mesh.ex # Mesh relay coordinator client
updater.ex # OTA update checking
system_resources.ex # CPU, memory, disk monitoring
tunneld_web/
live/
dashboard.ex # Main dashboard LiveView
dashboard/actions.ex # Action dispatcher
setup.ex # First-run setup wizard
login.ex # Login/signup with WebAuthn support
components/ # LiveView components (devices, resources, services, sidebar, etc.)
Run Tunneld locally with mocked hardware interactions:
- Install Elixir 1.18+ and Erlang/OTP 27+
- Install dependencies:
mix deps.get - Install JS/CSS tooling:
mix assets.setup - Start the server:
MOCK_DATA=true mix phx.server
Visit localhost:4000 in your browser.
The MOCK_DATA=true flag stubs all system commands (systemctl, wpa_cli, iw, tc, etc.) with fake data so you can develop on any OS without hardware.
MOCK_DATA=true mix testmix version # show current version
mix version patch # bump patch (0.10.5 -> 0.10.6)
mix version minor # bump minor (0.10.5 -> 0.11.0)
mix version major # bump major (0.10.5 -> 1.0.0)Updates both mix.exs and config/config.exs.
See CONTRIBUTING.md for guidelines on setting up your dev environment, running tests, and submitting changes.