N independent Cloudflare WARP exits as local SOCKS5 proxies — one command.
Runs MicroWARP containers on podman: each exit is one tiny container with its own WARP identity, its own SOCKS5 port, and its own podman volume.
Useful for browser farms, multi-account tooling, and anything that needs several clean egress IPs without buying residential proxies.
Your app / browser / farm
│ round-robin
▼
┌────────────────────────────────┐
│ warp-n1 → socks5h://…:40001 │──► WARP exit IP A
│ warp-n2 → socks5h://…:40002 │──► WARP exit IP B
│ ... │
│ warp-n10 → socks5h://…:40010 │──► WARP exit IP J
└────────────────────────────────┘
podman + MicroWARP: 1 container + 1 volume = 1 WARP identity
Single WARP (or single VPS IP) gets rate-limited fast when you open many
sessions, and heavyweight warp-cli daemon images (~201 MB image, ~150 MB RAM
per node) don't scale to a farm. MicroWARP's engine is kernel WireGuard + C
microsocks: ~9–15 MB image and under 1 MB of RAM per node, so a 1 GB host
runs dozens of exits and the whole stack starts in seconds. The real limit is
Cloudflare's IP diversity, not memory.
How it works:
| Property | Behavior |
|---|---|
| Orchestration | one podman run per node |
| Fleet discovery | mmw.* labels (podman ps --filter label=mmw.managed=true) |
| Identity | 1 named volume per node (mmw-data-n<N>) → separate WARP registration, atomic wipe |
| Isolation | Default bind 127.0.0.1 only (not public) |
| Persistence | optional quadlet systemd user units — survives reboots |
| Ops | ./mmw run → parallel probe → recreate-until-unique → warp-pool.txt |
- Linux, x86_64 / arm64
- Podman ≥ 4.4 (rootless works and is the primary target; rootful works too)
curl, bash ≥ 4.3; nothing else- Containers allowed
NET_ADMIN+SYS_MODULE; outbound UDP/TCP for WireGuard
git clone https://github.com/elliottophellia/MultiMicroWARP.git
cd MultiMicroWarp
./mmw run -n 10 # start 10 exits, recreate bad ones until unique
./mmw status # probe the fleet (no -n needed; labels find it)
./mmw stop # remove the fleet (identities are kept)run starts N nodes, probes every port through its SOCKS5 exit, and
recreates bad nodes (FAIL, warp≠on, or duplicate exit IP) until every
exit is unique, MAX_ROUNDS is hit, or the distinct-IP count stalls for two
healthy rounds. It then reports your host's ceiling instead of grinding.
Success writes warp-pool.txt and prints the port → IP map.
| Command | Meaning |
|---|---|
./mmw run [-n N] |
Start stack → probe → recreate until unique + warp=on → write warp-pool.txt |
./mmw probe [-n N] |
Wipe identities, start N fresh nodes, one probe pass → this host's distinct-exit ceiling |
./mmw status |
List fleet (label-discovered) + probe all ports |
./mmw pool [-o FILE] [-H HOST] [--scheme socks5h|socks5] [--stdout] |
Export the SOCKS pool |
./mmw logs <n|warp-nN> [lines] |
Node logs |
./mmw wipe <n…|all> |
Drop node container + identity volume → fresh registration on next run |
./mmw stop |
Remove all managed containers; identity volumes are kept |
./mmw restart |
stop, then run |
./mmw install [-n N] |
Install the fleet as quadlet systemd user units |
./mmw uninstall [--purge] |
Remove the units; --purge also drops identities |
Options: -n/--count (1–64), -p/--base-port (default 40001), --bind
(default 127.0.0.1, use 0.0.0.0 only behind a firewall/Tailscale),
--image. Tuning: --max-rounds, --recreate-batch, --recreate-wait,
--initial-wait. Everything is also settable in mmw.env; copy mmw.env.example for a
documented starting point.
Rootless podman restart policies don't survive a host reboot; systemd does. The intended flow is to converge first, then persist (existing identities and exit IPs are reused):
./mmw run -n 10 # converge to a healthy unique pool
./mmw stop # remove containers; volumes (identities) kept
./mmw install -n 10 # write quadlet units + start via systemctl --user
./mmw status # fleet is still discovered by labelsThis writes ~/.config/containers/systemd/mmw-warp-n*.container units
(perms 600; they may contain your SOCKS password) and starts them. Per node:
systemctl --user status mmw-warp-n3. Remove everything with
./mmw uninstall (keep identities) or ./mmw uninstall --purge (fresh
registrations next time).
If install warns about linger, run once:
sudo loginctl enable-linger $USER; otherwise the units stop when you log out.
Each node runs with caps NET_ADMIN+SYS_MODULE, four sysctls
(src_valid_mark, IPv6 enable ×2, IPv6 forwarding), log rotation
(max-size=3m), and BIND_ADDR=0.0.0.0 inside the container; the published
host port scopes access. Every MicroWARP setting is reachable:
mmw flag |
MicroWARP env |
|---|---|
--protocol wireguard|masque |
TUNNEL_PROTOCOL |
--endpoint "162.159.192.1 188.114.96.3:2408 …" |
ENDPOINT_IP — rotated across nodes, re-rotated each recreate round (bare IPs get port 2408) |
--socks-user U --socks-pass P |
SOCKS_USER / SOCKS_PASS (must come as a pair) |
--mtu, --keepalive |
MTU, KEEPALIVE |
--ipv6 / --no-ipv6 |
ENABLE_IPV6=1 / 0 |
--gh-proxy URL |
GH_PROXY (when github.com is blocked where nodes boot) |
--set KEY=VAL (repeatable) |
anything else: MASQUE_PROXY_MODE, MASQUE_HTTP2, MASQUE_SNI, MASQUE_MTU, WARP_JWT, WARP_LICENSE, TAILSCALE_CIDR, GOMEMLIMIT, … |
The same variables work as plain env vars or in mmw.env
(EXTRA_ENV=( "MASQUE_PROXY_MODE=socks" )). MicroWARP 0.3.0 registers
dual-stack (IPv4 + IPv6) by default; force IPv4-only with --no-ipv6.
Handy for CI / dry runs: upstream's --set MICROWARP_TEST_MODE=1 starts
containers that skip all initialization.
warp-pool.txt (from run, or ./mmw pool):
socks5h://127.0.0.1:40001
socks5h://127.0.0.1:40002
...
curl --proxy socks5h://127.0.0.1:40001 https://api.ipify.orgfrom itertools import cycle
POOL = [f"socks5h://127.0.0.1:{40001+i}" for i in range(10)]
proxy_cycle = cycle(POOL)
def next_proxy():
u = next(proxy_cycle)
return {"http": u, "https": u}socks5h resolves DNS through the proxy, which keeps clients off your
local resolver. socks5 resolves locally; since the WARP tunnel is dual-stack
by default both schemes work, but if you forced --no-ipv6, local resolution
can hand the proxy an unroutable IPv6 target (curl: (97)). Switch with
./mmw pool --scheme socks5 only if a client demands it. (Upstream
MicroWARP's README notes socks5h can feel slower on resolves; if you see
resolve timeouts, try --scheme socks5.)
More examples in examples/.
| Target size | Realistic outcome |
|---|---|
| 10–15 exits | Normal to attempt. On a network that hands out diverse egress you may land close to N unique — but on one host behind a single NAT/VPN path, Cloudflare often reuses a few exits and unique can cap well below N. |
| ~20 exits | Hard to get 20 unique. Anycast overlaps; expect duplicate-IP nodes the recreate loop must churn through. |
| 30+ | Not practical for all-unique on one host. Use multiple hosts (each contributes its own egress) or accept heavy IP reuse. |
A host behind a single NAT/VPN frequently routes every account to the same
egress (unique=1). That is Cloudflare's assignment, not a bug here.
Measure your host in about a minute with fresh accounts:
./mmw probe -n 10Levers when you want more diversity: --endpoint rotation, patience
(MAX_ROUNDS=12 RECREATE_WAIT=90 ./mmw run …), smaller recreate batches
(--recreate-batch 1), or more hosts.
./mmw status # fleet + per-port probe
./mmw logs 1 # node logs (or: ./mmw logs warp-n1)
podman inspect warp-n1 | less # full container state- Containers start then exit / never go healthy — check
./mmw logs n. Usual causes: missingNET_ADMIN/SYS_MODULEin your podman setup, a locked-down kernel refusingwg0creation, or first boot needing outbound HTTPS to github.com (wgcf download) and Cloudflare. If GitHub is blocked, set--gh-proxy. - All healthy but same public IP — mild overlap is normal (see above).
./mmw runalready recreates duplicates; a manual single-node reset is./mmw wipe 3 && ./mmw run -n 10. - First boot all FAIL — registration takes a few seconds per node, more
under load. Wait a minute and re-
./mmw status. - Port already allocated —
ss -lptn | grep 4000, then./mmw run -n 10 -p 41001. - Remote host needs the pool — bind to a private interface
(
--bind 0.0.0.0behind a firewall, or a Tailscale IP if your podman supports binding to it), then./mmw pool -H <tailscale-ip>. - Quadlet units didn't survive reboot — enable linger:
sudo loginctl enable-linger $USER. - Start over clean —
./mmw stop && ./mmw wipe all(or./mmw uninstall --purgefor a quadlet fleet).
You are responsible for Cloudflare WARP ToS and local law. This repo only orchestrates containers.
- ccbkkb/MicroWARP — the minimal
kernel-WireGuard WARP engine +
microsocksSOCKS5 server inside every node - Micolaabdi/multi-warp — reference for the orchestration pattern (N independent identities, recreate-until-unique loop)
MIT