HexGate is a lightweight, high-performance API Gateway built from scratch in Go.
This project is a high-availability, stateless API gateway that runs in a complete Docker-based
environment. It features an Nginx load balancer (Tier 1) routing traffic to a scalable pool of stateless HexGate instances (Tier 2)
, which in turn use Consul for service discovery and Redis for distributed quotas.
- High Availability (HA): Deployed with an Nginx load balancer in front of multiple, scalable hexgate instances
- Dynamic Service Discovery: Integrates directly with HashiCorp Consul.
Backends are no longer static;
hexgateautomatically discovers, adds, and removes them in real-time as they register or fail health checks. - Path-Based Routing: Intelligently routes requests to different backend service pools based on the URL path (e.g., /users/* -> user-service, /products/* -> product-service).
- JWT Authentication (RS256): Secures routes with a secure, asymmetric (RS256) JWT validation middleware.
- Distributed Quotas: Uses Redis with a
Sliding Windowalgorithm to enforce shared quotas (e.g., 1000 requests/day) across all gateway instances. - Built-in Observability: Exposes a
/metricsendpoint for Prometheus, tracking request rates, latencies, and response codes - TLS/SSL Termination: Centralized SSL termination at the Nginx load balancer.
- Dynamic Configuration (Hot Reload): Uses Consul KV as a centralized, dynamic source of truth for all configuration.
- Prerequisites
- Go 1.19 or later
- Docker & Docker Compose (for monitoring stack)
- Generate SSL Certificates (for TLS) The gateway is configured to run in HTTPS mode by default. You must generate a self-signed certificate and private key for it to find.
cd config
# Run the openssl command
# You can press Enter to accept the defaults for all questions.
openssl req -x509 -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem -days 365
# Navigate back to the root directory
cd ..This will create key.pem and cert.pem inside the config folder.
- cert.pem (Public Certificate): This file is sent to every client (like a browser) to prove the server's identity and allow the client to encrypt data. It's safe to share.
- key.pem (Private Key): This is your server's secret. It must never be shared. It's the only thing that can decrypt data sent by clients.
- Load Configuration into Consul The gateway no longer reads from config.yaml. You must load this configuration into the Consul Key/Value store before starting the stack.
- Start Consul
docker-compose up -d consul- Add the configuration
- Open the Consul UI: http://localhost:8500
- Click on Key/Value in the top menu.
- Click the Create button.
- For the Key, enter: hexgate/config
- For the Value, copy the entire contents of your local config/config.yaml file and paste it into the text box.
- Save
- Run the full stack
Start all services in High-Availability mode (with 2 hexgate instances).
docker-compose up -d --build --scale hexgate=2The service is now running:
- Nginx Load Balancer (HTTPS): https://localhost:8443
- Consul UI: http://localhost:8500
- Prometheus: http://localhost:9090
- Grafana: http://localhost:3000 (login: admin / admin)
- Redis (internal): redis:6379
- Run the Test Backends
Open 3 new terminals and run the following commands, one in each:
# Terminal 1: A user service
go run test/backend.go -port 8081 -service "user-service" -advertise-addr "host.docker.internal"
# Terminal 2: Another user service
go run test/backend.go -port 8082 -service "user-service" -advertise-addr "host.docker.internal"
# Terminal 3: A product service
go run test/backend.go -port 8083 -service "product-service" -advertise-addr "host.docker.internal"Go to your Consul UI (http://localhost:8500) and you will see these services marked as "healthy." Your hexgate logs (via docker-compose logs -f hexgate) will show them being discovered.
HexGate is pre-configured to work with the included Prometheus and Grafana stack.
- Navigate to http://localhost:3000 (login: admin / admin).
- On the left menu, go to Connections (plug icon) -> Add new connection.
- Select Prometheus.
- For "Prometheus server URL", enter: http://prometheus:9090
- Click "Save & Test". You should see a green success message.
- On the left menu, click the Dashboard icon (four squares) -> New -> New Dashboard to start building.
Now that your backends are running, send requests to the gateway:
# This will be routed to the "product-service" pool
curl -k https://localhost:8443/products/abc
# Hello from Backend (Port 8083)
# This will be routed to the "user-service" pool
curl -k https://localhost:8443/users/1
# Hello from Backend (Port 8081)
# Send again to see the load balancing
curl -k https://localhost:8443/users/2
# Hello from Backend (Port 8082)Now, go to Terminal 3 (port 8081) and stop the server (Ctrl+C). Wait a few seconds for Consul to detect the failure.
Now, all traffic for /users/ will go only to the remaining healthy backend:
curl -k https://localhost:8443/users/3
# Hello from Backend (Port 8082)- Authentication is enabled by default in config.yaml to support quotas.
curl -k https://localhost:8443/users/1
# 401 Unauthorized: Missing Authorization headerGenerate a token (by running go run test/gentoken.go) and attach it:
TOKEN=...[token]...
curl -H "Authorization: Bearer $TOKEN" https://localhost:8443/users/1
Our config is set to 1 request/sec with a burst of 3. Send 4 rapid requests:
# (Assumes you are sending the valid JWT token)
curl -H "Authorization: Bearer $TOKEN" https://localhost:8443/users/1 # OK
curl -H "Authorization: Bearer $TOKEN" https://localhost:8443/users/2 # OK
curl -H "Authorization: Bearer $TOKEN" https://localhost:8443/users/3 # OK
curl -H "Authorization: Bearer $TOKEN" https://localhost:8443/users/4
# 429 Too Many Requests

