| docker | ||
| routlin | ||
| debug_leases.py | ||
| FUNDRAISING.md | ||
| GUIDE.md | ||
| PRO_FEATURES.md | ||
| README.md | ||
Routlin
A collection of Python scripts that transform an existing Linux server (with at least two Ethernet NICs) into a fully-featured enterprise-grade router, eliminating the need for a separate router appliance.
Why Replace Your Router?
Consumer and prosumer router appliances are constrained by OEM firmware. Security patches depend entirely on the vendor's release schedule, features and functionality are often limited, and devices that reach end of life receive no vendor support at all, leaving gaping security vulnerabilities exposed on your network indefinitely. Running your router on a general-purpose Linux machine gives you:
- Faster speeds - Utilize full fledged computer hardware (typically exceeds that of consumer appliances)
- Full flexibility - Any configuration that Linux and its tooling support
- Better security - Patch your own kernel and packages on your own schedule, with no dependency on a vendor who may have abandoned your hardware
Summary
These scripts do not run continuously in the background. They install and facilitate the configuration of battle-hardened software (dnsmasq for DHCP and DNS, nftables for firewall and NAT, chrony for NTP, freeradius for RADIUS, avahi for mDNS discovery, and wireguard for VPN) using JSON files that you edit. A fully-featured, easy-to-use web management dashboard is included for users who prefer not to edit JSON directly.
Capabilities
The suite is organized into independent but complementary scripts, each managing one layer of the stack:
Core: DHCP, DNS, Firewall, RADIUS, mDNS, and WireGuard VPN (core.py)
- Configures VLAN sub-interfaces via
systemd-networkd - Assigns static or dynamic DHCP reservations by MAC address and hostname
- Defines dynamic IP pools per VLAN
- Manages per-VLAN gateway, DNS, and NTP settings derived from
server_identities - Runs one
dnsmasqinstance per VLAN, each bound exclusively to its gateway IP, giving true per-VLAN DNS filtering - Applies per-VLAN content filtering - VLANs with different blocklist sets each get their own merged blocklist (blocklists are downloaded and merged by
dns-blocklists.py) - Supports local hostname overrides (split DNS for DDNS hostnames)
- Installs a daily
systemdtimer that runsdns-blocklists.pyto refresh blocklists - Tracks lifetime DNS metrics (queries forwarded, cache hits, authoritative, TCP peaks, pool usage)
- Builds
nftablestables atomically - safe to re-apply without service disruption - Handles port forwarding (DNAT/SNAT) for externally accessible services
- Handles port wrangling - redirects DNS and NTP requests to the local resolver regardless of what the client device may have hardcoded
- Blocks traffic from specific IPs or subnets via
banned_ips- supports single IPs, CIDR notation, wildcards, and ranges for both IPv4 and IPv6 - Enforces inter-VLAN isolation by default (forward chain policy drop); specific cross-VLAN traffic is permitted via
inter_vlan_exceptions - Masquerades outbound traffic for all non-WireGuard VLANs automatically
- Auto-detects active container bridge interfaces (Docker, Podman, libvirt, etc.) and adds forward rules so VLAN clients can reach containerized services
- Auto-detects active container bridge interfaces and adds DNS listening on each bridge IP, so containers can reach the local DNS resolver during builds and at runtime
- Installs a
systemdboot service (routlin-nat.service) to re-apply firewall rules on every boot - Co-exists with Docker (does not touch Docker-managed
nat/filtertables) - Generates FreeRADIUS
clients.confandusersfiles fromcore.jsonreservations, enabling dynamic VLAN assignment via MAC Authentication Bypass (MAB) for both wired (802.1X) and wireless clients - Manages a
.radius-secretshared secret file (generated automatically on first--applyif RADIUS is enabled) - Configures
avahi-daemonas an mDNS reflector to forward service discovery announcements (AirPrint, AirPlay, Chromecast, etc.) across VLANs - Supports any number of WireGuard VPN interfaces (
is_vpn: trueVLANs); generates the server keypair on first apply, writes the server conf to/etc/wireguard/, and brings the interface up withwg-quick; subsequent applies sync peer changes live without restarting the interface - Supports per-peer split-tunnel (VPN subnet only) or full-tunnel (all traffic) routing; peer data is stored directly in
core.json
Optional: DNS Blocklists (dns-blocklists.py)
- Downloads blocklists from upstream providers you choose (e.g. OISD, Hagezi)
- Merges them per unique VLAN combination into conf files loaded by
dnsmasq - Runs
core.py --applyafter a successful download to reload all instances - Invoked by the daily
systemdtimer installed bycore.py --apply
Optional: DDNS (ddns.py)
- Detects the current public IP by rotating through multiple IP-check services
- Updates the specified DNS providers (currently supporting Cloudflare, No-IP and DuckDNS), supporting multiple hostnames and subdomains per provider
- Caches the last known IP per provider to avoid unnecessary API calls
- Installs a
systemdtimer that runs every 5 minutes by default - Logs all updates and errors to
ddns.log
Optional: Routlin Dashboard
- Web UI for managing all aspects of the router (VLANs, reservations, blocklists, VPN, DDNS, firewall, and more) without editing JSON by hand
- Runs as a Docker container alongside the existing scripts
- Changes made in the dashboard are queued and applied to the live system automatically via a 1-minute systemd timer
Software Dependencies
These packages are required. install.py checks that they are installed and will prompt to install any that are missing.
| Dependency | Purpose | Required By |
|---|---|---|
python3 |
Runs all scripts | All |
systemd |
Service, timer, networkd, and timesyncd management | All |
dnsmasq |
DHCP server and DNS resolver/forwarder | core.py |
nftables |
Firewall, NAT, port forwarding, and port wrangling | core.py |
chrony |
NTP server - synchronizes system clock and serves time to VLAN clients | core.py |
freeradius |
RADIUS server for dynamic VLAN assignment via MAC auth | core.py |
avahi-daemon |
mDNS reflector for cross-VLAN service discovery | core.py |
wireguard-tools |
WireGuard VPN (wg, wg-quick) |
core.py (when WireGuard VLANs are configured) |
docker |
Runs the Routlin Dashboard container | install.py (dashboard only) |
caddy |
Reverse proxy for external HTTPS access to the dashboard | install.py (external access only) |
Conflicting Software
The following services conflict with this suite. No manual action is required: core.py disables them automatically on --apply. core.py re-enables them on --disable.
- systemd-resolved - DNS stub resolver that conflicts with
dnsmasqon port 53. Disabled on--apply; re-enabled on--disable. - systemd-timesyncd - Basic SNTP client that cannot serve time to LAN clients; replaced by
chrony. Disabled on--apply; re-enabled on--disable. - ufw - Firewall manager that conflicts with the
nftablesruleset. Disabled on--applywithout removal.
Hardware Requirements
- A Linux server with at least two Ethernet NICs:
- One NIC facing your ISP modem/ONT (WAN)
- One NIC facing your internal switch (LAN)
For manual configuration and command-line usage without the dashboard, see USAGE.md.