Edge reverse proxy. Listens on ports 80 and 443, redirects HTTP to HTTPS, and routes traffic by host rule — wisper.gost.local to the tunnel port, *.gost.local to the ingress entrypoint.
Tutorial
Self-Hosted Public Reverse Proxy
Deploy your own GOST-powered public reverse proxy with Docker Compose — Traefik, GOST tunnel, ingress plugins, and Redis, all on your infrastructure.
Architecture Overview
The stack consists of four Docker services working in concert. Traefik terminates TLS and routes incoming requests. The GOST tunnel service handles WebSocket tunnel connections from clients. GOST Plugins manages dynamic subdomain-to-tunnel bindings via gRPC with Redis persistence. Together they provide a full self-hosted equivalent of the relay service described in the GOST+ blog.
Services
Core tunneling engine. Listens on :8080 for WebSocket tunnel connections and uses a gRPC ingress plugin to resolve which tunnel owns each subdomain.
Companion container running the ingress subcommand. Connects to Redis to store and retrieve tunnel-to-subdomain bindings with a 24-hour expiration.
Key-value store for ingress mappings. Uses an Alpine image with persistence enabled (save every 60 seconds if at least 1 key changed).
Configuration
GOST Configuration (gost.yml)
services:
- name: service-0
addr: :8080
handler:
type: tunnel
metadata:
entrypoint: :8000
ingress: ingress-0
listener:
type: ws
ingresses:
- name: ingress-0
plugin:
type: grpc
addr: gost-plugins:8000
log:
level: info
Docker Compose
version: '3'
services:
traefik:
image: traefik:v2.9.6
restart: always
command:
- "--providers.docker"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
- "--entrypoints.web.http.redirections.entryPoint.scheme=https"
- "--log.level=INFO"
ports:
- "80:80"
- "443:443"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
gost-tunnel:
image: gogost/gost
restart: always
labels:
- "traefik.http.routers.gost-tunnel.tls=true"
- "traefik.http.routers.gost-tunnel.rule=Host(`wisper.gost.local`)"
- "traefik.http.routers.gost-tunnel.service=gost-tunnel"
- "traefik.http.services.gost-tunnel.loadbalancer.server.port=8080"
- "traefik.http.routers.gost-ingress.tls=true"
- "traefik.http.routers.gost-ingress.service=gost-ingress"
- "traefik.http.routers.gost-ingress.rule=HostRegexp(`{subdomain:[a-z0-9]+}.gost.local`)"
- "traefik.http.routers.gost-ingress.priority=10"
- "traefik.http.services.gost-ingress.loadbalancer.server.port=8000"
volumes:
- ./gost.yaml:/etc/gost/gost.yaml
gost-plugins:
image: ginuerzh/gost-plugins
restart: always
command: "ingress --addr=:8000 --redis.addr=redis:6379 --redis.db=2 --redis.expiration=24h --domain=gost.local --log.level=debug"
redis:
image: redis:7.2.1-alpine
restart: always
command: "redis-server --save 60 1 --loglevel warning"
volumes:
- redis:/data
volumes:
redis:
driver: local
End-to-End Flow
- A tunnel client connects via WebSocket at wss://wisper.gost.local:443 (handled by Traefik then forwarded to GOST tunnel on :8080).
- GOST registers the tunnel and obtains a public subdomain via the gRPC ingress plugin, persisted in Redis.
- External visitors reach https://{subdomain}.gost.local, which Traefik routes to the ingress entrypoint port :8000.
- The tunnel handler, consulting the ingress plugin, forwards traffic through the WebSocket tunnel back to the client's local service.