.vscode | ||
bin | ||
screenshots | ||
src/go-proxy | ||
templates | ||
.gitignore | ||
compose.example.yml | ||
config.default.yml | ||
Dockerfile | ||
entrypoint.sh | ||
go.mod | ||
go.sum | ||
Makefile | ||
providers.example.yml | ||
README.md | ||
udp-test-server.Dockerfile |
go-proxy
A simple auto docker reverse proxy for home use. *Written in Go*
In the examples domain x.y.z
is used, replace them with your domain
Table of content
- Key Points
- How to use
- [Binary] (#binary)
- [Docker] (#docker)
- Configuration
- Troubleshooting
- Benchmarks
- Memory usage
- Build it yourself
- Getting SSL certs
Key Points
-
fast, nearly no performance penalty for end users when comparing to direct IP connections (See benchmarks)
-
auto detect reverse proxies from docker
-
additional reverse proxies from provider yaml file
-
allow multiple docker / file providers by custom
config.yml
file -
subdomain matching (domain name doesn't matter)
-
path matching
-
HTTP proxy
-
TCP/UDP Proxy
-
HTTP round robin load balance support (same subdomain and path across different hosts)
-
Auto hot-reload on container start / die / stop or config changes.
-
Simple panel to see all reverse proxies and health (visit port panel port of go-proxy
https://*.y.z:[panel port]
)
How to use (docker)
- Download and extract the latest release (or clone the repository if you want to try out experimental features)
- Copy
config.example.yml
toconfig.yml
and modify the content to fit your needs - Do the same for
providers.example.yml
- See Binary or docker
Binary
- (Optional) Prepare your certificates in
certs/
to enable https. See Getting SSL Certs - cert / chain / fullchain: ./certs/cert.crt - private key: ./certs/priv.key - run the binary
bin/go-proxy
- enjoy
Docker
-
Copy content from compose.example.yml and create your own
compose.yml
-
Add networks to make sure it is in the same network with other containers, or make sure
proxy.<alias>.host
is reachable -
(Optional) Mount your SSL certs to enable https. See Getting SSL Certs - cert / chain / fullchain -> /app/certs/cert.crt - private key -> /app/certs/priv.key
-
Start
go-proxy
withdocker compose up -d
ormake up
. -
(Optional) If you are using ufw with vpn that drop all inbound traffic except vpn, run below to allow docker containers to connect to
go-proxy
In case the network of your container is in subnet `172.16.0.0/16` (bridge),
and vpn network is under `100.64.0.0/10` (i.e. tailscale)
`sudo ufw allow from 172.16.0.0/16 to 100.64.0.0/10`
You can also list CIDRs of all docker bridge networks by:
`docker network inspect $(docker network ls | awk '$3 == "bridge" { print $1}') | jq -r '.[] | .Name + " " + .IPAM.Config[0].Subnet' -`
-
start your docker app, and visit <container_name>.y.z
-
check the logs with
docker compose logs
ormake logs
to see if there is any error, check panel at panel port for active proxies
Known issues
None
Configuration
With container name, no label needs to be added.
However, there are some labels you can manipulate with:
-
proxy.aliases
: comma separated aliases for subdomain matching- defaults to
container_name
- defaults to
-
proxy.<alias>.scheme
: container port protocol (http
orhttps
)- defaults to
http
- defaults to
-
proxy.<alias>.host
: proxy host- defaults to
container_name
- defaults to
-
proxy.<alias>.port
: proxy port- http/https: defaults to first expose port (declared in
Dockerfile
ordocker-compose.yml
) - tcp/udp: is in format of
[<listeningPort>:]<targetPort>
- when
listeningPort
is omitted (not suggested), a free port will be used automatically. targetPort
must be a number, or the predefined names (see stream.go)
- when
- http/https: defaults to first expose port (declared in
-
no_tls_verify
: whether skip tls verify when scheme is https- defaults to false
-
proxy.<alias>.path
: path matching (for http proxy only)- defaults to empty
-
proxy.<alias>.path_mode
: mode for path handling- defaults to empty
- allowed: <empty>, forward, sub
- empty: remove path prefix from URL when proxying
- apps.y.z/webdav -> webdav:80
- apps.y.z./webdav/path/to/file -> webdav:80/path/to/file
- forward: path remain unchanged
- apps.y.z/webdav -> webdav:80/webdav
- apps.y.z./webdav/path/to/file -> webdav:80/webdav/path/to/file
- sub: (experimental) remove path prefix from URL and also append path to HTML link attributes (
src
,href
andaction
) and Javascriptfetch(url)
by response body substitution e.g. apps.y.z/app1 -> webdav:80,href="/path/to/file"
->href="/app1/path/to/file"
- empty: remove path prefix from URL when proxying
-
proxy.<alias>.load_balance
: enable load balance- allowed:
1
,true
- allowed:
Single port configuration example
# (default) https://<container_name>.y.z
whoami:
image: traefik/whoami
container_name: whoami # => whoami.y.z
# enable both subdomain and path matching:
whoami:
image: traefik/whoami
container_name: whoami
labels:
- proxy.aliases=whoami,apps
- proxy.apps.path=/whoami
# 1. visit https://whoami.y.z
# 2. visit https://apps.y.z/whoami
Multiple ports configuration example
minio:
image: quay.io/minio/minio
container_name: minio
...
labels:
- proxy.aliases=minio,minio-console
- proxy.minio.port=9000
- proxy.minio-console.port=9001
# visit https://minio.y.z to access minio
# visit https://minio-console.y.z/whoami to access minio console
TCP/UDP configuration example
# In the app
app-db:
image: postgres:15
container_name: app-db
...
labels:
# Optional (postgres is in the known image map)
- proxy.app-db.scheme=tcp
# Optional (first free port will be used for listening port)
- proxy.app-db.port=20000:postgres
# In go-proxy
go-proxy:
...
ports:
- 80:80
...
- 20000:20000/tcp
# or 20000-20010:20000-20010/tcp to declare large range at once
# access app-db via <*>.y.z:20000
Load balancing Configuration Example
nginx:
...
deploy:
mode: replicated
replicas: 3
labels:
- proxy.nginx.load_balance=1 # allowed: [1, true]
Troubleshooting
Q: How to fix when it shows "no matching route for subdomain <subdomain>"?
A: Make sure the container is running, and <subdomain> matches any container name / alias
Benchmarks
Benchmarked with wrk
connecting traefik/whoami
's /bench
endpoint
Remote benchmark (client running wrk and go-proxy
server are different devices)
-
Direct connection
root@yusing-pc:~# wrk -t 10 -c 200 -d 30s --latency http://10.0.100.1/bench Running 30s test @ http://10.0.100.1/bench 10 threads and 200 connections Thread Stats Avg Stdev Max +/- Stdev Latency 4.34ms 1.16ms 22.76ms 85.77% Req/Sec 4.63k 435.14 5.47k 90.07% Latency Distribution 50% 3.95ms 75% 4.71ms 90% 5.68ms 99% 8.61ms 1383812 requests in 30.02s, 166.28MB read Requests/sec: 46100.87 Transfer/sec: 5.54MB
-
With reverse proxy
root@yusing-pc:~# wrk -t 10 -c 200 -d 30s --latency http://bench.6uo.me/bench Running 30s test @ http://bench.6uo.me/bench 10 threads and 200 connections Thread Stats Avg Stdev Max +/- Stdev Latency 4.50ms 1.44ms 27.53ms 86.48% Req/Sec 4.48k 375.00 5.12k 84.73% Latency Distribution 50% 4.09ms 75% 5.06ms 90% 6.03ms 99% 9.41ms 1338996 requests in 30.01s, 160.90MB read Requests/sec: 44616.36 Transfer/sec: 5.36MB
Local benchmark (client running wrk and go-proxy
server are under same proxmox host but different LXCs)
-
Direct connection
root@http-benchmark-client:~# wrk -t 10 -c 200 -d 10s --latency http://10.0.100.1/bench Running 10s test @ http://10.0.100.1/bench 10 threads and 200 connections Thread Stats Avg Stdev Max +/- Stdev Latency 434.08us 539.35us 8.76ms 85.28% Req/Sec 67.71k 6.31k 87.21k 71.20% Latency Distribution 50% 153.00us 75% 646.00us 90% 1.18ms 99% 2.38ms 6739591 requests in 10.01s, 809.85MB read Requests/sec: 673608.15 Transfer/sec: 80.94MB
-
With reverse proxy
root@http-benchmark-client:~# wrk -t 10 -c 200 -d 10s --latency http://bench.6uo.me/bench Running 10s test @ http://bench.6uo.me/bench 10 threads and 200 connections Thread Stats Avg Stdev Max +/- Stdev Latency 1.78ms 5.49ms 117.53ms 99.00% Req/Sec 16.31k 2.30k 21.01k 86.69% Latency Distribution 50% 1.12ms 75% 1.88ms 90% 2.80ms 99% 7.27ms 1634774 requests in 10.10s, 196.44MB read Requests/sec: 161858.70 Transfer/sec: 19.45MB
Memory usage
It takes ~ 0.1-0.4MB for each HTTP Proxy, and <2MB for each TCP/UDP Proxy
Build it yourself
-
Install go and
make
if not already -
get dependencies with
make get
-
build binary with
make build
-
start your container with
docker compose up -d
Getting SSL certs
I personally use nginx-proxy-manager
to get SSL certs with auto renewal by Cloudflare DNS challenge. You may symlink the certs from nginx-proxy-manager
to somewhere else, and mount them to go-proxy
's /certs