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

Traefik
TLS Termination / Router

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.

GOST Tunnel
WebSocket Tunnel Handler

Core tunneling engine. Listens on :8080 for WebSocket tunnel connections and uses a gRPC ingress plugin to resolve which tunnel owns each subdomain.

GOST Plugins
Ingress Management

Companion container running the ingress subcommand. Connects to Redis to store and retrieve tunnel-to-subdomain bindings with a 24-hour expiration.

Redis
State / Persistence

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)

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

docker-compose.yml
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
For production deployments, configure Traefik with a valid TLS certificate via Let's Encrypt (using the acme provider) and replace gost.local with your actual domain.

End-to-End Flow

  1. A tunnel client connects via WebSocket at wss://wisper.gost.local:443 (handled by Traefik then forwarded to GOST tunnel on :8080).
  2. GOST registers the tunnel and obtains a public subdomain via the gRPC ingress plugin, persisted in Redis.
  3. External visitors reach https://{subdomain}.gost.local, which Traefik routes to the ingress entrypoint port :8000.
  4. The tunnel handler, consulting the ingress plugin, forwards traffic through the WebSocket tunnel back to the client's local service.