Lightweight, secure Docker HTTP/SOCKS5 Tor proxy with local REST control API
10K+
Production-grade, leak-proof Tor HTTP and SOCKS5 proxy packaged for Docker. Route entire container networks with zero application changes, connect AI coding assistants (Claude Desktop, Cursor) via built-in MCP, and rotate circuits dynamically via REST.
Source repository: https://github.com/hapheus/torproxyโ
network_mode: "service:torproxy" to tunnel all outbound TCP & DNS through Tor automatically โ zero app proxy flags or code changes.2501 (SSE) and via Stdio (torproxy mcp) for seamless Claude Desktop, Cursor, and Antigravity integration.SIGNAL NEWNYM) or switch exit countries on the fly without container restarts via the REST API (:8080).torproxy), with authenticated loopback Tor ControlPort.TORPROXY_COUNTRY=DE or DE,AT) backed by built-in tor-geoipdb.linux/amd64, linux/arm64, and linux/arm/v7.Create a docker-compose.yml:
services:
torproxy:
image: hapheus/torproxy:latest
container_name: torproxy
restart: unless-stopped
ports:
- "127.0.0.1:8118:8118" # HTTP/HTTPS proxy (Privoxy)
- "127.0.0.1:9050:9050" # SOCKS5 proxy (Tor)
- "127.0.0.1:8080:8080" # REST API & Health status
Launch the proxy:
docker compose up -d
docker run -d \
--name torproxy \
--restart unless-stopped \
-p 127.0.0.1:8118:8118 \
-p 127.0.0.1:9050:9050 \
-p 127.0.0.1:8080:8080 \
hapheus/torproxy:latest
Wait a few seconds for Tor to finish bootstrapping, then test:
# 1. Test HTTP proxy via Privoxy (port 8118) โ returns {"IsTor": true, "IP": "..."}
curl --proxy http://127.0.0.1:8118 https://check.torproject.org/api/ip
# 2. Test SOCKS5 proxy with remote DNS resolution (port 9050)
curl --socks5-hostname 127.0.0.1:9050 https://check.torproject.org/api/ip
# 3. Check live connection status via REST API (port 8080)
curl -s http://127.0.0.1:8080/status | jq .
# 4. Request a new Tor identity / circuit on demand
curl -X POST http://127.0.0.1:8080/reconnect
# 5. Dynamically switch exit country without restarting
curl -X POST "http://127.0.0.1:8080/reconnect?country=DE"
Important
**Leak-Proof Guarantee (Fail-Closed):** If Tor fails or disconnects, `torproxy` fails closed immediately. It will **never** fall back to direct clearnet routing.
.onion and web fetch tools via MCP.network_mode: "service:torproxy".TORPROXY_COUNTRY=DE,AT,US).Route another container's entire network traffic (TCP + DNS) through Tor without setting proxy flags:
services:
torproxy:
image: hapheus/torproxy:latest
pull_policy: always
cap_add:
- NET_ADMIN
environment:
TORPROXY_GATEWAY_MODE: "true"
restart: unless-stopped
# Any container attached to torproxy's network stack is automatically anonymized:
scraper:
image: curlimages/curl:latest
network_mode: "service:torproxy"
depends_on:
torproxy:
condition: service_healthy
command: ["-s", "https://check.torproject.org/api/ip"]
Connect Claude Desktop directly via Docker Stdio without exposing host ports:
{
"mcpServers": {
"torproxy": {
"command": "docker",
"args": ["exec", "-i", "torproxy", "torproxy", "mcp"]
}
}
}
Or connect Cursor / Antigravity via HTTP/SSE (http://127.0.0.1:2501/sse).
Route traffic exclusively through specific countries:
docker run -d \
--name torproxy \
--restart unless-stopped \
-e TORPROXY_COUNTRY=DE,AT \
-p 127.0.0.1:8118:8118 \
-p 127.0.0.1:9050:9050 \
-p 127.0.0.1:8080:8080 \
hapheus/torproxy:latest
The management API listens on port 8080 (HTTP):
| Endpoint | Methods | Description |
|---|---|---|
/status | GET | Tor bootstrap and current verified egress IP address. |
/ip | GET | Current verified Tor egress IP address. |
/health | GET | Multi-subsystem health check (tor, proxies, egress). Returns HTTP 200 when healthy, 503 during bootstrap/errors. |
/diagnostics | GET | Uptime, reconnect counters, bootstrap details, and cache status. |
/metrics | GET | Prometheus exposition format metrics. |
/reconnect | POST (or GET) | Triggers SIGNAL NEWNYM to establish a new circuit. Supports ?country=DE query or JSON body. |
/disconnect | POST (or GET) | Disables Tor network traffic (SETCONF DisableNetwork=1). |
/connect | POST (or GET) | Re-enables Tor network traffic (SETCONF DisableNetwork=0). Supports ?country=DE query or JSON body. |
Warning
The management API is unauthenticated by default. Bind it strictly to `127.0.0.1` or place it behind an authenticating reverse proxy if exposed across networks.
| Variable | Default | Description |
|---|---|---|
TORPROXY_COUNTRY | (unset) | Comma-separated ISO 3166-1 alpha-2 codes (e.g. DE, AT, US, DE,AT) to restrict exit nodes. |
TORPROXY_STRICT_NODES | 0 | If 1/true, refuse circuits if nodes in the requested country/countries are unavailable. |
TORPROXY_GATEWAY_MODE | 0 | Enable Transparent Tor Gateway mode (cap_add: [NET_ADMIN] required). |
TORPROXY_API_TOKEN | (unset) | Optional Bearer token / API key securing REST API port 8080. |
TORPROXY_GEOIP_AUTO_UPDATE | 0 | Opt-in: periodically download fresh GeoIP database from Tor Project GitLab. |
TORPROXY_LISTEN_PORT | 8118 | Privoxy HTTP proxy port inside container. |
TORPROXY_SOCKS_PORT | 9050 | Tor SOCKS5 proxy port inside container. |
TORPROXY_API_PORT | 8080 | REST API port inside container. |
For maximum security in production, run with a read-only root filesystem and drop all Linux capabilities:
services:
torproxy:
image: hapheus/torproxy:latest
restart: unless-stopped
read_only: true
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
tmpfs:
- /tmp:rw,noexec,nosuid,size=32M
- /run:rw,noexec,nosuid,size=16M
- /var/lib/tor:rw,noexec,nosuid,size=64M
- /var/log/privoxy:rw,noexec,nosuid,size=16M
ports:
- "127.0.0.1:8118:8118"
- "127.0.0.1:9050:9050"
- "127.0.0.1:8080:8080"
This project is licensed under the MIT Licenseโ .
Content type
Image
Digest
sha256:990c05c94โฆ
Size
29.5 MB
Last updated
about 13 hours ago
docker pull hapheus/torproxy