Proxy mesh in Kubernetes: Tor exit nodes and geo-routing with hola-proxy
Published: 2026-04-19
The infra cluster runs a proxy namespace with two kinds of outbound proxies: a Tor rotating exit node cluster and hola-proxy instances that tunnel through residential IPs in specific countries.
The proxy namespace
namespace: proxy
├── tor-proxy ← rotating-tor-http-proxy (HAProxy + N Tor circuits)
├── hola-proxy-jp ← Japan exit node via Hola peer network
├── hola-proxy-sg ← Singapore exit node
└── hola-proxy-au ← Australia exit node
All proxies listen on port 8080 inside the cluster. External access goes through APISIX routes or direct VPN access.
Tor rotating proxy
Uses zhaow-de/rotating-tor-http-proxy — a container that runs N Privoxy+Tor pairs behind HAProxy. Each circuit is a separate Tor identity, round-robin selected by HAProxy.
yamlapiVersion: apps/v1
kind: Deployment
metadata:
name: tor-proxy
namespace: proxy
spec:
replicas: 1
template:
spec:
containers:
- name: tor-proxy
image: registry.example.com/docker/proxy/rotating-tor-http-proxy:latest
env:
- name: TOR_INSTANCES
value: "10"
ports:
- containerPort: 30118 # HAProxy frontend port
resources:
requests:
memory: "512Mi"
cpu: "200m"
limits:
memory: "1Gi"
cpu: "500m"
10 Tor instances = 10 concurrent circuits, round-robin. Each request gets a different exit IP. The container exposes HAProxy on port 30118.
hola-proxy instances (routing through Tor)
Each country is a separate Deployment. hola-proxy routes through the Tor proxy for anonymization:
yamlapiVersion: apps/v1
kind: Deployment
metadata:
name: hola-proxy-jp
namespace: proxy
spec:
replicas: 1
template:
spec:
containers:
- name: hola-proxy-jp
image: registry.example.com/docker/proxy/hola-proxy:latest
args:
- -bind-address
- 0.0.0.0:8080
- -country
- jp
- -proxy
- http://tor-proxy.proxy.svc.cluster.local:30118
- -timeout
- 35s
- -verbosity
- "20"
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "300m"
startupProbe:
tcpSocket:
port: 8080
failureThreshold: 30
periodSeconds: 5
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 20
periodSeconds: 20
failureThreshold: 3
Hola uses residential P2P exit nodes. Tor anonymizes the connection from the cluster to the Hola peer network.
The startupProbe with 30 retries (150s total) gives hola-proxy time to establish its first peer connection before being considered failed.
Services
yamlapiVersion: v1
kind: Service
metadata:
name: hola-proxy-jp
namespace: proxy
spec:
type: ClusterIP
selector:
app: hola-proxy
country: jp
ports:
- name: http
port: 8080
targetPort: 8080
Repeat for sg, au. Internal consumers use hola-proxy-jp.proxy.svc.cluster.local:8080.
APISIX route for external access
Expose one proxy via APISIX for external (VPN) access:
yamlapiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: proxy-jp
namespace: proxy
spec:
http:
- name: proxy-jp
match:
hosts:
- proxy-jp.infra.example.com
paths:
- "/*"
backends:
- serviceName: hola-proxy-jp
servicePort: 8080
Why raw manifests instead of HelmRelease
These Deployments live in spoke/proxy/ as plain YAML, not HelmReleases:
- No templating needed across environments — these only exist on
infra - Country-specific proxies don't make sense on
dev/test - The pod spec is simple — raw YAML is cleaner than a chart with one template
Image source
The hola-proxy image is built from an internal GitLab project and pushed to registry.example.com. Pull credentials come from gitlab-registry-docker imagePullSecret (a SealedSecret in spoke/secrets/).
Monitoring proxy health
Check all proxy pods:
bashkubectl get pods -n proxy -o wide
Test a specific exit node:
bash# Test Japan exit IP
curl -x http://hola-proxy-jp.proxy.svc.cluster.local:8080 https://api.myip.com
# Test Tor rotating proxy
curl -x http://tor-proxy.proxy.svc.cluster.local:30118 https://check.torproject.org/api/ip
Watch logs for connection errors:
bashkubectl logs -n proxy deployment/hola-proxy-jp -f
What can go wrong
hola-proxy returns "no available upstreams".
Hola's peer network occasionally rotates available peers. Restart the pod: kubectl rollout restart deployment/hola-proxy-<country> -n proxy. If it persists, the country code may no longer be supported — check the hola-proxy GitHub for the current list.
Tor circuits take too long to bootstrap. The Tor proxy pod logs show "Bootstrapped 0%" for minutes after startup. This usually means the VPS IP is listed in a Tor blocklist, or the network blocks the Tor port. Try a different Tor bridge configuration.
APISIX route returns 502 for the proxy endpoints.
The backend service is probably reachable but the upstream health check is failing. Check APISIX admin API: curl http://apisix-admin:9180/apisix/admin/upstreams.
Summary
- Two proxy types in one namespace: Tor rotating exit nodes (anonymity) and hola-proxy (geo-specific residential IPs)
- Each hola-proxy instance targets a single country (
country: JP,country: US, etc.) and runs as a separate Deployment - Services use
ClusterIP— external access goes through APISIX routes with optional auth - Raw Kubernetes manifests instead of HelmRelease because the setup changes rarely and doesn't benefit from Helm's values templating
- Test geo-routing:
curl -x http://hola-proxy-jp.proxy.svc.cluster.local:8080 https://api.myip.com