- Per-listener `retries` overrides global default (0 = inherit) - Pool-level `allowed_protos` filters proxies during merge - Connection pooling documented in CHEATSHEET.md - Both features exposed in /config and /status API responses - 12 new tests (config parsing, API exposure, merge filtering) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
9.6 KiB
s5p -- Cheatsheet
CLI
s5p # direct, listen :1080
s5p -C socks5://127.0.0.1:9050 # through Tor
s5p -C socks5://tor:9050,http://px:8080 # Tor + HTTP proxy
s5p -c config/s5p.yaml # from config file
s5p -l 0.0.0.0:9999 # custom listen address
s5p -t 30 # 30s per-hop timeout
s5p -v # debug logging
s5p -q # errors only
s5p -S http://api:8081/proxies # proxy source API
s5p -r 5 # retry up to 5 proxies
s5p -m 512 # max concurrent connections
s5p --api 127.0.0.1:1081 # enable control API
s5p --cprofile # profile to s5p.prof
s5p --cprofile out.prof # profile to custom file
s5p --tracemalloc # memory profile (top 10)
s5p --tracemalloc 20 # memory profile (top 20)
Systemd
make install-service # install unit + reload
sudo systemctl enable --now s5p # enable + start
sudo systemctl status s5p # check status
sudo systemctl restart s5p # restart
sudo systemctl stop s5p # stop
journalctl -u s5p -f # follow logs
journalctl -u s5p --since "5 min ago" # recent logs
Container
make build # podman-compose build
make up # podman-compose up -d
make logs # podman-compose logs -f
make down # podman-compose down
Volumes: ./config/s5p.yaml (ro), ~/.cache/s5p → /data (pool state + profiles)
Dev override: compose.yaml mounts ./src (ro) over the baked-in source.
CI
Gitea Actions runs on push to main:
ruff check+pytest(test)gitleaks detect(secrets scan)- Build + push
harbor.mymx.me/s5p/s5p:latest
Secrets: HARBOR_USER / HARBOR_PASS (configured in Gitea repo settings).
Config
cp config/example.yaml config/s5p.yaml # create live config (gitignored)
Multi-Listener (config)
listeners:
- listen: 0.0.0.0:1080
pool: clean # default for bare "pool"
chain:
- socks5://127.0.0.1:9050
- pool # Tor + 2 clean hops
- pool
- listen: 0.0.0.0:1081
pool: clean
chain:
- socks5://127.0.0.1:9050
- pool:clean # per-hop: explicit clean
- pool:mitm # per-hop: explicit mitm
- listen: 0.0.0.0:1082
chain:
- socks5://127.0.0.1:9050 # Tor only
- listen: 0.0.0.0:1083
chain:
- socks5://127.0.0.1:9050
- [pool:clean, pool:mitm] # random choice per connection
- [pool:clean, pool:mitm] # independent random choice
Per-hop pool: pool = listener default, pool:name = explicit pool,
[pool:a, pool:b] = random choice from candidates.
Bypass Rules (config)
listeners:
- listen: 0.0.0.0:1080
bypass:
- 127.0.0.0/8 # CIDR
- 10.0.0.0/8 # CIDR
- 192.168.0.0/16 # CIDR
- localhost # exact hostname
- .local # domain suffix
chain:
- socks5://127.0.0.1:9050
- pool
| Pattern | Type | Matches |
|---|---|---|
10.0.0.0/8 |
CIDR | IPs in network |
127.0.0.1 |
Exact IP | That IP only |
localhost |
Exact host | String equal |
.local |
Suffix | *.local and local |
Listener Authentication (config)
listeners:
- listen: 0.0.0.0:1080
auth:
alice: s3cret
bob: hunter2
chain:
- socks5://127.0.0.1:9050
- pool
curl -x socks5h://alice:s3cret@127.0.0.1:1080 https://example.com
No auth: key = no authentication required (default).
Listener Retry Override (config)
listeners:
- listen: 0.0.0.0:1080
retries: 5 # override global retries
chain:
- socks5://127.0.0.1:9050
- pool
- listen: 0.0.0.0:1082
chain:
- socks5://127.0.0.1:9050 # 0 = use global default
Per-listener retries overrides the global retries setting. Set to 0 (or
omit) to inherit the global value.
Pool Protocol Filter (config)
proxy_pools:
socks_only:
allowed_protos: [socks5] # reject http proxies
sources:
- url: http://api:8081/proxies/all
When set, proxies not matching allowed_protos are silently dropped during
merge. Useful when a source returns mixed protocols but the pool should
only serve a specific type.
Multi-Tor Round-Robin (config)
tor_nodes: # overrides first hop in all listeners
- socks5://10.200.1.1:9050
- socks5://10.200.1.254:9050
- socks5://10.200.1.250:9050
- socks5://10.200.1.13:9050
Performance Tuning (config)
max_connections: 256 # concurrent connection cap
pool_size: 8 # pre-warmed TCP conns to first hop (0 = off)
pool_max_idle: 30 # evict idle pooled conns (seconds)
Connection Pool (config)
pool_size: 8 # pre-warmed TCP connections per first hop (0 = off)
pool_max_idle: 30 # evict idle connections after N seconds
Pre-warms TCP connections to the first hop in the chain. Only the raw TCP
connection is pooled -- SOCKS/HTTP negotiation consumes it. One pool is
created per unique first hop (shared across listeners). Requires at least
one hop in chain.
| Setting | Default | Notes |
|---|---|---|
pool_size |
0 (off) | Connections per first hop |
pool_max_idle |
30 | Idle eviction in seconds |
Named Proxy Pools (config)
proxy_pools:
clean:
sources:
- url: http://10.200.1.250:8081/proxies/all
mitm: false # adds ?mitm=0
state_file: /data/pool-clean.json
refresh: 300
test_interval: 120
max_fails: 3
mitm:
sources:
- url: http://10.200.1.250:8081/proxies/all
mitm: true # adds ?mitm=1
state_file: /data/pool-mitm.json
refresh: 300
test_interval: 120
max_fails: 3
Singular proxy_pool: still works (becomes pool "default").
Source Filters (proxy_pool sources)
| Filter | Values | Query param |
|---|---|---|
proto |
socks5/socks4/http | ?proto=... |
country |
ISO alpha-2 | ?country=... |
limit |
integer | ?limit=... |
mitm |
true/false | ?mitm=1 / ?mitm=0 |
Tor Control Port (config)
tor:
control_port: 9051
password: "" # or cookie_file: /path/to/cookie
newnym_interval: 60 # auto-rotate every 60s (0 = manual)
curl -s http://127.0.0.1:1081/tor | jq . # status
curl -s -X POST http://127.0.0.1:1081/tor/newnym | jq . # new circuit
Hot Reload
kill -HUP $(pidof s5p) # reload config
podman kill --signal HUP s5p # container reload
Proxy File Format
# one proxy URL per line
socks5://1.2.3.4:1080
socks5://user:pass@5.6.7.8:1080
http://proxy.example.com:8080
Proxy URLs
socks5://host:port
socks5://user:pass@host:port
socks4://host:port
http://host:port
http://user:pass@host:port
Control API
s5p --api 127.0.0.1:1081 -c config/s5p.yaml # enable API
curl -s http://127.0.0.1:1081/status | jq . # runtime status
curl -s http://127.0.0.1:1081/metrics # prometheus metrics
curl -s http://127.0.0.1:1081/pool | jq . # all proxies
curl -s http://127.0.0.1:1081/pool/alive | jq . # alive only
curl -s http://127.0.0.1:1081/config | jq . # current config
curl -s -X POST http://127.0.0.1:1081/reload # reload config
curl -s -X POST http://127.0.0.1:1081/pool/test # health test now
Testing
# Check exit IP
curl -x socks5h://127.0.0.1:1080 https://httpbin.org/ip
# Tor check
curl -x socks5h://127.0.0.1:1080 https://check.torproject.org/api/ip
# Verbose curl
curl -v -x socks5h://127.0.0.1:1080 https://example.com
# With timeout
curl --max-time 30 -x socks5h://127.0.0.1:1080 https://example.com
Profiling
# Enable in compose.yaml: uncomment the command line
python -m pstats s5p.prof # interactive stats viewer
python -m pstats ~/.cache/s5p/s5p.prof # container profile output
Metrics Log
metrics: conn=1842 ok=1790 fail=52 retries=67 active=3 in=50.0M out=1.0G rate=4.72/s p50=198.3ms p95=890.1ms up=1h01m01s pool=42/65
Prometheus Metrics (/metrics)
curl -s http://127.0.0.1:1081/metrics
# TYPE s5p_connections counter
s5p_connections_total 1842
# TYPE s5p_active_connections gauge
s5p_active_connections 3
# TYPE s5p_pool_proxies_alive gauge
s5p_pool_proxies_alive{pool="clean"} 30
# TYPE s5p_chain_latency_seconds summary
s5p_chain_latency_seconds{quantile="0.5"} 0.198300
s5p_chain_latency_seconds{quantile="0.95"} 0.890100
# EOF
OpenMetrics format. Use /status for JSON equivalent.
Troubleshooting
| Symptom | Check |
|---|---|
| Connection refused | Is Tor running? ss -tlnp | grep 9050 |
| Timeout | Increase -t, check proxy reachability |
| DNS leak | Use socks5h:// (not socks5://) in client |
| Auth failed | Verify credentials in proxy URL |
| Port in use | fuser -k 1080/tcp to free the port |
| Proxy keeps failing | Backoff penalizes for 60s; check pool= in metrics |
| "static chain unreachable" | Tor/upstream hop is down; pool tests skipped |
| Slow startup | Normal on cold start; warm restarts serve instantly from state |