22
33** 🌐 Languages:** ** English** · [ Русский] ( README.ru.md )
44
5- > Self-hosted transparent proxy manager for Raspberry Pi 4/5 (and any
6- > other Linux box). Drops in next to your router, intercepts LAN
7- > traffic via nftables TPROXY, and routes it through xray-core based
8- > on your rules — domain, GeoIP, GeoSite, MAC, port, protocol — with a
9- > web UI.
5+ > Self-hosted router and transparent proxy manager for Raspberry Pi 4/5
6+ > (and any other Linux box). Sit it ** beside** your router, or let it
7+ > ** be** the router — taking the ISP uplink, handing out addresses,
8+ > doing NAT and serving the WiFi. Either way it intercepts LAN traffic
9+ > via nftables TPROXY and routes it through xray-core by your rules —
10+ > domain, GeoIP, GeoSite, MAC, port, protocol — from a web UI.
1011
1112[ ![ CI] ( https://img.shields.io/github/actions/workflow/status/DaveBugg/PiTun/ci.yml?branch=master&label=CI )] ( # )
1213[ ![ License] ( https://img.shields.io/badge/license-BSD--3--Clause-blue )] ( LICENSE )
1920## Table of contents
2021
2122- [ What it is] ( #what-it-is )
23+ - [ Router mode] ( #router-mode )
2224- [ Why PiTun?] ( #why-pitun )
2325- [ Screenshots] ( #screenshots )
2426- [ Architecture] ( #architecture )
2527- [ Features] ( #features )
2628- [ Supported protocols] ( #supported-protocols )
2729- [ Quick start] ( #quick-start )
2830- [ Server-side proxy install] ( #server-side-proxy-install )
31+ - [ Updating] ( #updating )
2932- [ Configuration] ( #configuration )
3033- [ Troubleshooting] ( #troubleshooting )
3134- [ Uninstall] ( #uninstall )
4043## What it is
4144
4245PiTun turns a small Linux box into a ** transparent proxy gateway** for
43- your home network. Devices that use the box as their default gateway
44- have their outbound traffic intercepted at the kernel level, routed
46+ your home network — and, since v1.6.0, into ** the router itself ** if you
47+ want it to be. Traffic is intercepted at the kernel level, routed
4548through one of several supported VPN protocols, and either tunnelled,
4649sent direct, or dropped — all according to rules you set up in the
4750web UI.
4851
52+ ** Two shapes, one box.** Which one you run is a single choice in the
53+ panel, and gateway is the default:
54+
55+ | | ** Gateway** — beside your router | ** Router** — instead of it |
56+ | ---| ---| ---|
57+ | Who hands out addresses | your existing router | PiTun |
58+ | Who does NAT | your existing router | PiTun |
59+ | How devices are covered | point their gateway at PiTun | just by being on the network |
60+ | Hardware needed | any box, one port is enough | two or more ports |
61+ | WiFi | your router's | PiTun's, if the adapter can serve it |
62+
63+ The gateway shape is the gentle one: nothing about your network
64+ changes, and only the devices you point at PiTun go through it. The
65+ router shape covers everything on the LAN by construction — including
66+ the devices that have no proxy settings to speak of.
67+
4968It was built for and primarily tested on the ** Raspberry Pi 4 / 5**
5069(64-bit Raspberry Pi OS), but the project ships ** linux/amd64** images
5170too, so any Intel/AMD mini-PC, NUC, old laptop or x86_64 server that
@@ -66,6 +85,55 @@ routing rule set:**
6685| SOCKS5 | ` 1080 ` | Explicit proxy for browsers and apps |
6786| HTTP | ` 8080 ` | For apps without SOCKS5 support |
6887
88+ ## Router mode
89+
90+ ** Since v1.6.0 PiTun is a complete router.** Not a proxy that happens to
91+ forward — it takes the ISP line, runs the DHCP server, does the NAT, is
92+ the firewall, and puts the WiFi on the air. Everything the proxy already
93+ did keeps working on top: rules, node rotation, chains, the DNS log.
94+
95+ Turn it on in ** Router → Operating mode** . It is offered only on hardware
96+ with two or more physical ports, and never switched on by itself.
97+
98+ ** The uplink** , in the shapes an ISP line actually comes in:
99+
100+ | Mode | What it means |
101+ | ---| ---|
102+ | DHCP | Address from the provider. What most providers call ** IPoE** |
103+ | Static | Address, gateway and DNS you were given |
104+ | PPPoE | Login and password, the session dialled by PiTun |
105+ | VLAN tag | 802.1Q id on the uplink port, on its own or with any of the above |
106+ | MAC clone | Present the provider a specific MAC — for lines bound to one |
107+
108+ ** The network below.** DHCP with your own pool and lease time, and
109+ per-device reserved addresses assigned from the Devices page. PiTun
110+ announces itself as the resolver, so routing rules and the DNS query log
111+ cover devices that never opted in to anything. A LAN can be several
112+ ports — sockets and the radio bridged into one segment, so a laptop on
113+ the cable and a phone on the WiFi share a subnet and see each other.
114+
115+ ** The WiFi.** WPA2 or WPA2/WPA3-transitional, band and channel, hidden if
116+ you like, country code obeyed. PiTun checks the adapter can serve an
117+ access point before it tries: plenty of cards can only join networks, and
118+ finding that out when hostapd refuses to start means the working setup is
119+ already dismantled.
120+
121+ ** The firewall.** The uplink accepts nothing new from the internet — one
122+ blanket rule rather than a list of ports to remember. The panel, SSH and
123+ xray's own listeners all bind ` 0.0.0.0 ` : they stay reachable from the LAN
124+ and invisible from outside. Two exceptions keep the line working — DHCP
125+ replies, which arrive as NEW rather than RELATED, and the ICMP that path-
126+ MTU discovery needs. If PiTun sits behind another router, its "WAN" is
127+ your own network, and you can publish the panel and SSH there on purpose;
128+ that switch refuses outright if the uplink address turns out to be public.
129+
130+ > ** Switching modes changes the network under you.** Router mode has no
131+ > fallback — PiTun * is* the router — so a bad apply would leave nobody
132+ > able to undo it. PiTun therefore reverts to gateway unless you confirm
133+ > the network still works, and an unconfirmed change never survives a
134+ > reboot. Do the first switch where you can reach the box, not over SSH
135+ > from the network it is about to rebuild.
136+
69137## Why PiTun?
70138
71139A bunch of self-hosted proxy managers already exist:
@@ -115,22 +183,37 @@ router-bound options:
115183 the whole LAN (TPROXY), SOCKS5 for apps that want an explicit proxy,
116184 HTTP for legacy clients. Most tools force you to pick one.
117185
186+ - ** It can replace the router entirely.** Those packages run * on* a
187+ router because they need one. PiTun can be it: ISP uplink (DHCP,
188+ static, PPPoE, VLAN, MAC clone), DHCP server, NAT, firewall and WiFi
189+ — see [ Router mode] ( #router-mode ) . One box instead of two, and the
190+ proxy policy applies to everything on the network by construction,
191+ not only to the devices you remembered to point at it.
192+
118193vs. ** router-based packages** (podkop, passwall, passwall2,
119194homeproxy, xKeen): they're brilliant at "all my devices, minimal
120- setup" on a $30 router with 64 MB RAM. PiTun is for the case where
195+ setup" on a $30 router with 64 MB RAM, and if that is your whole
196+ requirement they are the lighter answer. PiTun is for the case where
121197you want ** server-side orchestration** (deploy & manage your own
122198VPS), ** multi-tier traffic policy** (different rules per device
123- group), and ** a modern web UI with a mobile-friendly layout — tweak
124- a rule from your phone, even from the bathroom if you must ** — at
125- the cost of needing an RPi 4/5 (or any small Linux box) with 64 GB+
126- disk.
199+ group), ** a box that can be the router rather than ride on one ** , and
200+ ** a modern web UI with a mobile-friendly layout — tweak a rule from
201+ your phone, even from the bathroom if you must ** — at the cost of
202+ needing an RPi 4/5 (or any small Linux box) with 64 GB+ disk.
127203
128204## Screenshots
129205
206+ <details >
207+ <summary ><strong >Dashboard</strong > — click to expand · 1 screenshot</summary >
208+
209+ <br >
210+
130211<a href =" docs/screenshots/dashboard.jpg " >
131212 <img src =" docs/screenshots/dashboard.jpg " alt =" Dashboard " width =" 800 " >
132213</a >
133214
215+ </details >
216+
134217<details >
135218<summary ><strong >VPS provisioning & x-ui orchestration</strong > (since v1.3.0) — click to expand · 6 screenshots</summary >
136219
@@ -245,6 +328,9 @@ disk.
245328
246329## Architecture
247330
331+ ** Gateway mode** — PiTun sits beside the router and proxies the devices
332+ pointed at it:
333+
248334```
249335 ┌──────────────────────────────────────────────┐
250336 Devices ────► │ PiTun host (RPi / mini-PC) │
@@ -265,12 +351,58 @@ disk.
265351 └──────────────────────────────────────────────┘
266352```
267353
354+ ** Router mode** — the same engine, with PiTun holding both ends of the
355+ network. The proxy layer above is unchanged; what is added is everything
356+ a router does:
357+
358+ ```
359+ ┌──────────────────────────────────────────────┐
360+ ISP ────────►│ WAN dhcp / static / pppoe / vlan / mac-clone│
361+ │ │ │
362+ │ ├─ nftables: NAT (masquerade) + firewall │
363+ │ │ uplink accepts nothing new inbound │
364+ │ │ TCP MSS clamped to the path MTU │
365+ │ │ │
366+ │ ▼ │
367+ │ ┌────────── the proxy engine above ────────┐ │
368+ │ └──────────────────────────────────────────┘ │
369+ │ │ │
370+ │ ├─ dnsmasq: DHCP + reserved addresses │
371+ │ ├─ hostapd: WiFi access point │
372+ │ │ │
373+ Devices ◄─────┤ LAN one or more ports, bridged: br-lan │
374+ (wired+WiFi) │ cable and radio in one subnet │
375+ └──────────────────────────────────────────────┘
376+ commit-confirm watchdog guards every change
377+ ```
378+
268379Web UI talks to a FastAPI backend that owns the xray-core process,
269380the nftables ruleset, and a SQLite database with all configuration.
270381Frontend is a single-page React app served by nginx.
271382
272383## Features
273384
385+ ** Router** (v1.6.0 — see [ Router mode] ( #router-mode ) )
386+ - Two operating modes: ** gateway** beside your router, or ** router**
387+ instead of it. Explicit choice, gateway by default
388+ - Uplink: DHCP (IPoE), static, ** PPPoE** , 802.1Q VLAN tag, MAC clone —
389+ with NAT, the firewall and the counters following the interface the
390+ traffic actually leaves by, ppp link or tagged sub-interface included
391+ - DHCP server with pool, lease time and per-device reserved addresses
392+ - ** A LAN of several ports** — sockets and the radio bridged into one
393+ segment, one subnet, one DHCP scope
394+ - ** WiFi access point** — WPA2 or WPA2/WPA3-transitional, band, channel,
395+ hidden SSID, regulatory domain; gated on an AP-capability probe of the
396+ adapter
397+ - A WAN that accepts nothing new from the internet, with the panel and
398+ SSH publishable on the uplink on purpose when PiTun sits behind another
399+ router — refused if that address turns out to be public
400+ - ** Commit-confirm watchdog** — a change that breaks the network reverts
401+ itself unless a human confirms, and never survives a reboot unconfirmed
402+ - Uplink diagnosis on nftables counters, for the failures that are
403+ otherwise silent: no DHCP replies, no ICMP coming back, traffic leaving
404+ by a port that isn't the uplink
405+
274406** Core**
275407- Transparent proxy via TPROXY + nftables, no per-device client
276408- SOCKS5 / HTTP proxies on the LAN
@@ -359,7 +491,9 @@ Frontend is a single-page React app served by nginx.
359491- ** Host network controls** — change the box's own gateway / DNS from
360492 Settings → Network with an auto-backup + one-click rollback; warns on
361493 a routing self-loop (gateway pointing at the box itself) or a
362- double-hop through another PiTun
494+ double-hop through another PiTun. (In router mode PiTun owns its own
495+ uplink, so these are configured on the ** Router** page instead and
496+ this section refuses to fight it)
363497
364498** Servers & deployments**
365499- Inventory of remote VPS hosts (host, SSH credentials, tags) separate
@@ -378,8 +512,24 @@ Frontend is a single-page React app served by nginx.
378512 healthcheck (panel API, xray, nginx, UFW, TLS cert, disk, mem),
379513 cache↔panel sync for hand-added clients, random / custom
380514 fakesite rotation
381- - ** SNI / REALITY-dest scanner** in the node form — probe a candidate
382- host for TLS 1.3 + HTTP/2 before saving it as the masquerade target
515+
516+ - ** Connect a panel PiTun didn't install.** Import a server that already
517+ runs x-ui and the X-ui page used to stay empty — a panel became one only
518+ by being deployed through PiTun. Paste the ` xui:// ` line from the
519+ install, or just your panel login: the API token is fetched for you,
520+ reusing an existing one rather than minting a new one per attempt
521+ - ** The panel travels with its server** — a server exported with secrets
522+ carries its panel registration (bundle envelope v3), so restoring it
523+ elsewhere doesn't leave x-ui installed and unlisted
524+ - ** REALITY dest / SNI scanner** where the inbound is created — probe a
525+ candidate IP or domain * through the active node* for TLS 1.3 + HTTP/2 and
526+ see what actually answers, instead of guessing a masquerade target from a
527+ hardcoded list
528+ - ** Connection-lifetime policy for Xray** — one set of timeouts for the
529+ box's own instance and every registered panel. Xray's default kills an
530+ idle * pooled* connection after five minutes, which is the "works, then
531+ it doesn't" that SDK and agent clients hit; panels are patched rather
532+ than overwritten, and one button pushes a change to the whole fleet
383533
384534** Operations**
385535- One-click GeoIP / GeoSite refresh — three switchable upstream
@@ -395,6 +545,12 @@ Frontend is a single-page React app served by nginx.
395545 your node list with any v2rayN-compatible client; symmetric
396546 ` Import ` button auto-detects URI list vs JSON bundle. Single-node URI
397547 export straight from a node card too
548+ - ** Whole-box configuration backup** — Settings → Backup & Restore
549+ exports settings, subscriptions, nodes, routing sets and rules, DNS
550+ rules, balancers, circles, devices and UA templates as one file, and
551+ restores it onto a fresh box. Secrets are opt-in, so the file you share
552+ for debugging carries no credentials; restore previews per-section
553+ add/update/delete counts before it writes anything
398554- ** Login lockout** — five consecutive failed logins lock an account
399555 for 15 minutes (HTTP 429 + ` Retry-After ` ); the primary LAN-only
400556 brute-force guard
@@ -426,6 +582,7 @@ Frontend is a single-page React app served by nginx.
426582| ** RAM** | 1 GB | 2 GB+ (helps with naive sidecars and large geo data refresh) |
427583| ** Disk** | 4 GB free | 8 GB+ (docker images + DB growth + DNS query log) |
428584| ** Network** | 1 LAN interface, static IP, wired preferred | 1× wired GbE for LAN |
585+ | ** Network — [ router mode] ( #router-mode ) ** | 2 physical ports (one for the ISP, one for the LAN) | 2× wired GbE, plus an AP-capable WiFi adapter to serve wireless |
429586| ** OS** | Any modern 64-bit Linux with kernel ≥ 5.4 (TPROXY support) | Raspberry Pi OS 64-bit, Debian 12+, Ubuntu 22.04+ |
430587| ** Architectures** | ` linux/arm64 ` * (RPi 4/5)* · ` linux/amd64 ` * (Intel/AMD mini-PC, NUC, x86_64 server)* | — |
431588
@@ -435,6 +592,10 @@ Frontend is a single-page React app served by nginx.
435592- Docker + Docker Compose v2
436593- Root access on the host (nftables + raw socket binding)
437594- A static LAN IP for the host
595+ - For [ router mode] ( #router-mode ) : two or more physical ports, and — if
596+ PiTun is to serve the WiFi — an adapter that can run an access point.
597+ PiTun checks the adapter for you and says so before anything is torn
598+ down
438599
439600### Install — one-liner
440601
@@ -738,6 +899,46 @@ rotation — random pick from the bundled archive or upload a custom
738899` .zip` . Chain orchestration (see ** Proxy Chains** below) glues two
739900registered panels into a two-hop VLESS+Reality tunnel.
740901
902+ # # Updating
903+
904+ ** From the panel.** Settings → Updates checks GitHub — through the active
905+ node, so a throttled direct route isn' t a blocker — shows what changed, and
906+ applies it with live progress.
907+
908+ The button does not update the box itself: it writes a request that a
909+ host-side agent carries out, because the update replaces the very container
910+ the panel is served from. The installer sets that agent up. On a box
911+ installed before v1.6.2 it isn' t there, and the button will sit at 0%
912+ forever — install it once:
913+
914+ ` ` ` bash
915+ bash /opt/pitun/scripts/pitun-update.sh --install-agent
916+ ` ` `
917+
918+ This does ** not** enable unattended updates. The agent only acts on a
919+ request you made.
920+
921+ ** From the command line.** Re-run the installer with a version; it detects
922+ an existing install, snapshots the database first, and refuses to go
923+ backwards:
924+
925+ ` ` ` bash
926+ curl -fsSL https://raw.githubusercontent.com/DaveBugg/PiTun/master/install.sh -o /tmp/pitun-install.sh
927+ sudo bash /tmp/pitun-install.sh --version v1.6.2
928+ ` ` `
929+
930+ ** On a schedule** , opt-in and separate from the button — a daily systemd
931+ timer that only reports unless you ask it to act:
932+
933+ ` ` ` bash
934+ bash /opt/pitun/scripts/pitun-update.sh --install-timer # check and report
935+ bash /opt/pitun/scripts/pitun-update.sh --install-timer --apply # check and update
936+ ` ` `
937+
938+ Every path takes a SQLite snapshot to
939+ ` /opt/pitun/data-backup-pre-vX.Y.Z-* .db` before touching anything, and
940+ router mode comes back on its own after the containers are recreated.
941+
741942# # Configuration
742943
743944All runtime config goes through the web UI. The only settings that
0 commit comments