Service Registry + Redirect / Reverse Proxy Load Balancer.
flux stores service instances in Redis, receives heartbeat load from each instance, and routes traffic to the least-loaded healthy target. It supports two modes:
| Mode | Behaviour | Use case |
|---|---|---|
| Redirect (default) | Returns HTTP 302 to the best instance. Payload goes client → backend directly. |
Internal networks, same-VPC services |
Reverse Proxy (PROXY_MODE=true) |
Proxies the request to the best instance and returns the response. Payload goes client → flux → backend → flux → client. | Public-facing APIs behind Nginx/Traefik, HTTPS termination |
docker run -d --name redis redis:7-alpine
docker run -d --name flux --link redis -e REDIS_ADDR=redis:6379 -p 8080:8080 ghcr.io/xinnaider/flux
curl http://localhost:8080/healthgit clone https://github.com/xinnaider/flux
cd flux
docker compose up -d
# Check it's alive
curl http://localhost:8080/health
# {"status":"ok"}Register a service instance:
curl -X POST http://localhost:8080/register \
-H "Content-Type: application/json" \
-d '{"name":"ms.auth","host":"10.0.0.5","port":3001,"health_url":"/health"}'
# {"instance_id":"10.0.0.5:3001","ttl_seconds":15}Send heartbeats (every ~10s) with real load:
curl -X POST http://localhost:8080/heartbeat \
-H "Content-Type: application/json" \
-d '{"name":"ms.auth","instance_id":"10.0.0.5:3001","active_connections":3}'
# {"ok":true,"ttl_seconds":15}Redirect a client to the least-loaded instance:
curl -v http://localhost:8080/ms.auth/login
# HTTP/1.1 302 Found
# Location: http://10.0.0.5:3001/login| Field | Type | Required |
|---|---|---|
name |
string | yes |
host |
string | yes |
port |
int | yes |
health_url |
string | no |
Returns {"instance_id":"<host>:<port>","ttl_seconds":15}.
| Field | Type | Required |
|---|---|---|
name |
string | yes |
instance_id |
string | yes |
active_connections |
int | yes |
The registry resets the 15s TTL. Missing 2-3 heartbeats = instance expires out.
Redirect mode (default): returns 302 Found with Location: http://<host>:<port>/<path>.
Proxy mode (PROXY_MODE=true): proxies the request to the best instance and returns its response directly. No redirect. Supports X-Forwarded-For, X-Forwarded-Host, X-Forwarded-Proto, and X-Real-IP headers.
# Redirect mode
curl -v http://localhost:8080/ms.auth/login
# HTTP/1.1 302 Found
# Location: http://10.0.0.5:3001/login
# Proxy mode
curl -v http://localhost:8080/ms.auth/login
# HTTP/1.1 200 OK
# {"hello":"world"} ← response from backendcurl -X POST http://localhost:8080/release \
-H "Content-Type: application/json" \
-d '{"name":"ms.auth","instance_id":"10.0.0.5:3001"}'curl -X POST http://localhost:8080/unregister \
-H "Content-Type: application/json" \
-d '{"name":"ms.auth","instance_id":"10.0.0.5:3001"}'curl http://localhost:8080/health
# {"status":"ok"}| Env | Default | Description |
|---|---|---|
PORT |
8080 |
HTTP listen port |
REDIS_ADDR |
localhost:6379 |
Redis address |
REDIS_PASSWORD |
— | Redis password |
REDIS_DB |
0 |
Redis DB number |
HEARTBEAT_TTL |
15s |
Instance TTL (Go duration) |
CLEANUP_INTERVAL |
5s |
Stale instance cleanup interval |
REQUEST_TIMEOUT |
30s |
HTTP read/write timeout |
PROXY_MODE |
false |
Enable reverse proxy mode (instead of 302 redirect) |
PROXY_TIMEOUT |
30s |
Backend connection timeout in proxy mode |
PROXY_IDLE_CONNS |
100 |
Max idle connections in the transport pool |
PROXY_IDLE_PER_HOST |
10 |
Max idle connections per backend host |
For public-facing APIs with HTTPS, place Nginx in front of flux with proxy mode enabled.
[Client] --HTTPS--> [Nginx :443] --HTTP--> [flux :8080] --HTTP--> [Backend]
upstream flux_upstream {
server flux:8080;
keepalive 64;
}
server {
listen 443 ssl http2;
server_name api.exemplo.com;
ssl_certificate /etc/ssl/certs/cert.pem;
ssl_certificate_key /etc/ssl/certs/key.pem;
location / {
proxy_pass http://flux_upstream;
proxy_http_version 1.1;
# Headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header Connection "";
# Timeouts
proxy_connect_timeout 10s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
# Buffering
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
}
}services:
nginx:
image: nginx:alpine
ports: ["443:443", "80:80"]
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./certs:/etc/ssl/certs:ro
depends_on: [flux]
flux:
image: ghcr.io/xinnaider/flux
environment:
PORT: "8080"
REDIS_ADDR: "redis:6379"
PROXY_MODE: "true"
PROXY_IDLE_CONNS: "200"
PROXY_IDLE_PER_HOST: "50"
depends_on: [redis]
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5In proxy mode, flux proxies ~7.5k req/s per instance (reduced from ~10k in redirect mode due to byte copying). Scale horizontally behind Nginx for more throughput:
flux-1: # ... same config as above
flux-2: # ... same config
flux-3: # ... same configNginx's upstream will round-robin between them.
flux uses a small Redis key surface:
service:ms.auth:instances SET — instance IDs for the service
instance:ms.auth:10.0.0.5:3001 HASH — host, port, health_url, connections
Instances are hash entries with a TTL. If the heartbeat stops, the hash expires and Cleanup removes the stale reference from the service set.
go run ./cmd/server
go build -o bin/flux ./cmd/server
./bin/flux
docker build -t flux .
docker run -p 8080:8080 --network host fluxPull the official image without any authentication:
docker pull ghcr.io/xinnaider/flux
docker run -e REDIS_ADDR=host.docker.internal:6379 -p 8080:8080 ghcr.io/xinnaider/fluxRequires Redis on localhost:6379 (or REDIS_TEST_ADDR):
go test -v -race ./...- Service instances register with host/port.
- Every ~10s they send a heartbeat with current
active_connections. - When a client hits
GET /{service}/*, flux queries Redis for all live instances, picks the one with the least connections. - flux also increments the chosen instance's counter as a fallback between heartbeats.
- Instances that stop heartbeating are dropped after TTL + cleanup.
The routing strategy depends on the mode:
- Redirect mode (
PROXY_MODE=false, default): flux returns302 Location: http://<host>:<port>/<path>. Traffic flows client → instance directly. flux never touches the payload. - Proxy mode (
PROXY_MODE=true): flux proxies the entire request to the backend, reads the response, and returns it to the client. Payload goes through flux, but the client never sees internal addresses. Use behind Nginx for HTTPS.
| Layer | Technology |
|---|---|
| Runtime | Go |
| State | Redis 7 |
| HTTP | net/http |
| Infra | Docker Compose |
MIT