Run your own Autonomous System (AS) with BGP peering and a ZeroTier mesh network.
Create an independent AS that:
- Announces your IP block to the Internet via BGP
- Provides connectivity to distributed nodes through a ZeroTier mesh
- Enables you to host services accessible from the public Internet
INTERNET
β
β BGP (your AS announced globally)
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ISP / IXP (datacenter) β
β (not under your control) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
β BGP peering (eBGP)
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β BORDER ROUTER (your AS) β
β β
β ββββββββββββββββββββββββ ββββββββββββββββββββββββββββ β
β β BIRD β β ZeroTier β β
β β BGP AS ${BORDER_ β β controller + client β β
β β ROUTER_AS} β β β β
β β β β assigns IPs from range β β
β β announces β β source-routes return β β
β β ${MESH_ADDRESS_ β β traffic to ISP β β
β β RANGE} β β (policy table 123) β β
β ββββββββββββββββββββββββ ββββββββββββββββββββββββββββ β
β β
β Connects your AS to both: Internet (BGP) and your mesh (ZeroTier) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
β ZeroTier mesh
βΌ
ββββββββββββββββββββ ββββββββββββββββββββ ββββββββββββββββββββ
β Mesh Node 1 β β Mesh Node 2 β β Mesh Node N β
β IP from β β IP from β β IP from β
β ${MESH_ β β ${MESH_ β β ${MESH_ β
β ADDRESS_ β β ADDRESS_ β β ADDRESS_ β
β RANGE} β β RANGE} β β RANGE} β
β zerotier β β zerotier β β zerotier β
β (anywhere) β β (anywhere) β β (anywhere) β
ββββββββββββββββββββ ββββββββββββββββββββ ββββββββββββββββββββ
- A mesh node (IP from
${MESH_ADDRESS_RANGE}) wants to reach the Internet - The AlterMundi ZeroTier fork installs source-based policy routing on the node: traffic sourced from the node's mesh IP is routed via the ZeroTier interface to the border router
- Border router forwards to the ISP via BGP peering (policy table 123)
- Response arrives at the border router and is forwarded back through ZeroTier to the node
- Someone on the Internet wants to reach a mesh node IP
- BGP routing directs traffic to your ISP (your AS is announced)
- ISP sends to your border router
- Border router forwards via ZeroTier mesh to the node
The border router acts as the ingress node for the mesh. It runs the ZeroTier controller and sets ingressNodeV4 to its own mesh IP. Every node that joins receives this value and the AlterMundi fork automatically installs per-node source-based policy routing, directing return traffic through the border router. The border router then forwards that traffic to the ISP using a dedicated routing table.
See docs/ZEROTIER.md for a full explanation of the ingress routing architecture.
| Component | Location | Purpose |
|---|---|---|
deploy/bird-border/ |
Public server | ZeroTier controller + BGP (BIRD) β the border router |
deploy/zerotier-ui/ |
Same server | ZeroTier web UI for member management |
deploy/zerotier/ |
Any location | Mesh node (client) |
The central component. Runs the ZeroTier controller and BGP daemon on the same host:
- ZeroTier controller: built from AlterMundi/ZeroTierOne (
feature/ingress-node), manages network membership and distributesingressNodeV4config to all nodes - BIRD: BGP daemon, announces your IP block to the ISP
- Ingress node: forwards public IP traffic from the internet to mesh nodes via source-based policy routing
Web interface for authorizing members and inspecting network state. Built from
the altermundi/zerotier-ui fork, which
adds ingressNodeV4 configuration support. Reads the controller auth token from
the shared zerotier_data volume.
Note: The ZeroTier controller itself runs inside
deploy/bird-border/, not here. This stack only provides the web UI.
Simple nodes that join the mesh:
- Run zerotier (AlterMundi fork) to establish overlay
- Receive
ingressNodeV4config from the controller and install source routing automatically - Can host services accessible from the Internet
| Network | CIDR | Purpose |
|---|---|---|
| Your AS block | ${MESH_ADDRESS_RANGE} | Public IPs announced via BGP |
| Mesh overlay | (same as above) | ZeroTier mesh uses your public block |
| BGP peering | ${BGP_NETWORK_RANGE} | Link between you and ISP |
Note: In this design, mesh IPs are your public IPs. This means services on mesh nodes are directly reachable from the Internet once BGP is established.
- IP allocation: Obtain IP block from RIR (LACNIC, ARIN, etc.) or lease from provider
- AS number: Obtain from RIR or use private AS (64512-65534) for testing
- BGP peering: Agreement with ISP or IXP for BGP session
- Public server: For ZeroTier control plane
- Datacenter presence: For border router (colocation or VPS with BGP support)
Before deploying, copy and customize environment files:
# Root configuration (optional - for shared values)
cp .env.example .env
# Component-specific configuration
cp deploy/zerotier-ui/.env.example deploy/zerotier-ui/.env
cp deploy/bird-border/.env.example deploy/bird-border/.env
cp deploy/zerotier/.env.example deploy/zerotier/.env
cp deploy/rpi-isp/.env.example deploy/rpi-isp/.env # if using mock ISPEdit each .env file with your specific values. See .env.example files for documentation.
cd deploy/bird-border
cp .env.example .env
# Edit .env: set BGP vars; ZT_NETWORK_ID can be added after creating the network
docker compose up -dSee deploy/bird-border/README.md for BGP configuration.
Use the ZeroTier controller API to create the network and configure ingressNodeV4.
See docs/ZEROTIER.md for the full step-by-step procedure.
cd deploy/zerotier-ui
cp .env.example .env
# Edit .env with your UI password and ports
docker compose up -d --buildSee deploy/zerotier-ui/README.md for details.
cd deploy/zerotier
cp .env.example .env
# Set ZT_NETWORK_ID
docker compose up -dFor development without real BGP peering, use a Raspberry Pi as mock ISP.
See deploy/rpi-isp/README.md for full mock ISP setup instructions.
βββββββββββββββββββ βββββββββββββββββββ
β RPi (mock) βββββββ BGP βββββββββΊβ Border Router β
β AS ${ISP_AS} β ${BGP_ β AS ${BORDER_ β
β ${ISP_IP} β NETWORK_ β ROUTER_AS} β
β β RANGE} β ${BORDER_ β
β announces test β β ROUTER_IP} β
β prefixes β β β
β β ingress node β β
βββββββββββββββββββ βββββββββββββββββββ
β
β mesh
βΌ
βββββββββββββββββββ
β Mesh Nodes β
β ${MESH_ β
β ADDRESS_ β
β RANGE} β
β zerotier β
β β
βββββββββββββββββββ
docker exec bird-border birdc show protocols
docker exec bird-border birdc show routedocker exec zerotier zerotier-cli info
docker exec zerotier zerotier-cli listnetworks
ping <mesh-node-ip> # other mesh nodes in ${MESH_ADDRESS_RANGE}# From RPi, should reach any mesh node
ping <mesh-node-ip> # any IP in ${MESH_ADDRESS_RANGE}- ZeroTier containers run with
NET_ADMIN+SYS_ADMINcapabilities.SYS_ADMINis required for ZeroTier's network namespace and tun/tap device management inside Docker. Both capabilities are scoped to the container and do not grant host-level root access. - Use proper TLS termination (reverse proxy) in front of the web UI for production.
ZTNCUI_PASSWDis stored in plaintext in.envfiles β protect file permissions.- Configure host firewall to restrict access to zerotier-ui ports.
MIT