Easy Proxies is a sing-box based proxy pool manager.
It focuses on turning many upstream nodes into one stable local HTTP proxy entry, while still supporting per-node ports when needed.
- Supports
pool,multi-port, andhybridruntime modes. - Supports pool scheduling strategies:
sequential,random,balance, andadaptive. - Builds upstream outbounds for:
vmess,vless,trojan,ss/shadowsocks,hysteria2/hy2,socks5/socks,http/https. - Supports node sources:
- inline
nodes:inconfig.yaml nodes_file(one URI per line)- structured
subscription_sources(auto/clash/v2ray) - legacy
subscriptionscompatibility
- inline
- Provides automatic health checks, node blacklist recovery, and adaptive selection based on latency, active connections, and recent failures.
- Provides Web dashboard + API for:
- runtime node status, search/filter/sort, probe, and export
- settings update (
external_ip,probe_target,skip_cert_verify) - config node CRUD + reload
- subscription source CRUD, preview, status, and manual refresh
- Adds configurable DNS resolver for outbound domain resolution (important for VMess nodes with domain hosts).
- Optional GeoIP labeling with auto-update and hot-reload (region/country metadata in dashboard).
| Area | Original version | This version |
|---|---|---|
| Subscription import | Mainly relies on basic node sources and compatibility-style subscription loading | Adds structured subscription_sources, supports auto / clash / v2ray, and allows create/edit/enable/disable/delete directly in Web UI |
| Subscription workflow | Refresh is available, but source visibility and pre-check are limited | Adds subscription preview, source status summary, clearer empty-source feedback, and a faster manual refresh flow |
| Node management | Focuses on runtime proxy pool behavior | Adds config node CRUD, batch enable/disable, one-click disable for abnormal nodes, and tighter runtime/config linkage |
| Web UI | Covers basic monitoring and management | Improves subscription editing layout, filtering experience, region-based views, and reduces UI lag with debounced search and tab short-cache |
| Reload and recovery | Basic reload and port recovery | Optimizes subscription-triggered reload path, preserves port mapping during refresh, and avoids unnecessary health-gate blocking in refresh scenarios |
| Docker / deployment | Can run in container with manual setup | Improves start.sh, writable mounted data files, persistent ./data workflow, healthcheck readiness, and clearer deployment guidance |
In short: the original version already provided a usable sing-box proxy pool, while this version focuses more on subscription operations, Web management ergonomics, runtime recovery, and Docker deployment experience.
- Thanks to jasonwong1991/easy_proxies for the original Easy Proxies project. This version builds on that foundation and extends it with enhanced subscription management, UI experience, and deployment workflow improvements.
cp config.example.yaml config.yaml
cp nodes.example nodes.txtEdit config.yaml and configure your node source (nodes.txt, subscription_sources, or inline nodes).
Start locally:
go run ./cmd/easy_proxies -config config.yamlRecommended:
./start.shIt prepares:
./data/config.yaml./data/nodes.txt./data/geoip/
Then it runs docker compose up -d --build. Compose mounts ./data:/data, so Web UI changes persist directly to the host.
If you prefer to prepare the data directory manually:
mkdir -p data
cp config.example.yaml data/config.yaml
cp nodes.example data/nodes.txt
docker compose up -dIf you want to access the management panel via host port 9090, change management.listen in ./data/config.yaml to 0.0.0.0:9091 and set a strong password.
mode: pool
listener:
address: 0.0.0.0
port: 2323
username: user
password: pass
pool:
mode: adaptive # recommended; sequential / random / balance / adaptive
failure_threshold: 3
blacklist_duration: 24h
management:
enabled: true
listen: 127.0.0.1:9091 # change to 0.0.0.0:9091 when publishing Docker ports
probe_target: http://cp.cloudflare.com/generate_204
password: "" # set a strong password before exposing the panel
dns:
server: 223.5.5.5
port: 53
strategy: prefer_ipv4
nodes_file: nodes.txt
# Optional: structured subscription sources
# subscription_sources:
# - name: Main Clash subscription
# url: https://example.com/sub?token=xxx
# type: clash # auto / clash / v2ray
# enabled: truedns controls domain resolution used by sing-box DNS client and VMess domain dialing:
dns:
server: 223.5.5.5
fallback_servers: # Fallback DNS servers (used when primary fails)
- 8.8.8.8
- 1.1.1.1
port: 53
strategy: prefer_ipv4Allowed strategy values:
as_isprefer_ipv4prefer_ipv6ipv4_onlyipv6_only
If you see logs like lookup <domain>: empty result, set a reachable resolver and an explicit strategy.
pool: one HTTP entry for all nodes.multi-port: one local HTTP port per node.hybrid: pool + multi-port together.
- If enabled
subscription_sourcesexist:- subscription nodes are fetched and appended to runtime nodes
nodes_fileis used as the output path for fetched nodes- startup skips reading
nodes_file
- Legacy
subscriptionsis still accepted and normalized into the structured source model. - Inline
nodesalways participate when present.
Runtime builder supports:
vmessvlesstrojanss/shadowsockshysteria2/hy2socks5/sockshttp/https
Parser may recognize additional URI prefixes in subscription text for compatibility, but unsupported schemes are skipped during build.
Main endpoints:
POST /api/authGET|PUT /api/settingsGET /api/nodesPOST /api/nodes/{tag}/probePOST /api/nodes/{tag}/releasePOST /api/nodes/probe-all(SSE)GET /api/exportGET /api/subscription/statusPOST /api/subscription/refreshGET|POST /api/subscriptionsPOST /api/subscriptions/previewPUT|DELETE /api/subscriptions/{id}GET|POST|PUT|DELETE /api/nodes/config[...]POST /api/reloadGET /api/healthz
When management.password is empty, API/UI auth is bypassed.
- The Docker image defaults to
/data/config.yamland/data/nodes.txt. - Compose mounts
./data:/dataso Web UI changes can persist back to the host together with GeoIP data. - The bundled healthcheck probes
127.0.0.1:9091/api/healthzinside the container. - Before publishing the panel, change
management.listenin./data/config.yamlto0.0.0.0:9091and setmanagement.password. - If GeoIP is enabled, prefer
database_path: ./geoip/GeoLite2-Country.mmdbso the path resolves correctly relative toconfig.yamlin both local and Docker workflows.
- If
./data/config.yamlor./data/nodes.txtwas accidentally created as a directory,./start.shexits early with a clear error. - If the container starts but the Web UI is unreachable through published ports, verify that
management.listenin./data/config.yamlis not still127.0.0.1:9091. - If GeoIP labeling does not appear, confirm that the database path exists and resolves correctly relative to the active
config.yaml.
- Reload (
/api/reloador subscription refresh) interrupts active connections. - Settings API persists values to
config.yaml; some changes require reload to fully take effect. - Default normalization values (when omitted) are in
internal/config/config.go.
go test ./...