Expose your Hydra Node securely with Nginx, Caddy, or Traefik.
Reverse Proxy Setup
Running your Hydra Node behind a reverse proxy is strongly recommended for production deployments. A reverse proxy provides:
- TLS/SSL termination — serve your node over HTTPS with automatic certificates
- DDoS protection — rate limiting and connection throttling at the proxy level
- WebSocket support — proper upgrade header forwarding
- Domain routing — serve your node on a clean domain like
node.example.com - Security — hide internal ports and services from the internet
Architecture
Internet
│
▼
┌──────────────────┐
│ Reverse Proxy │ :443 (HTTPS) / :80 (HTTP → redirect)
│ (Nginx/Caddy) │
└────────┬─────────┘
│ proxy_pass
▼
┌──────────────────┐
│ Hydra Node │ :4001 (localhost only)
│ REST + WS │
└──────────────────┘
When using a reverse proxy, change your node config to bind only to localhost:
server:
port: 4001
host: "127.0.0.1" # Only accept connections from the proxy
domain: "node.example.com"
Your firewall should only allow external access on ports 80 and 443 (the proxy ports). Port 4001 should not be accessible from the internet.
Nginx
Nginx is the most popular reverse proxy. The configuration below handles both regular HTTP requests and WebSocket upgrades.
Install Nginx
# Ubuntu / Debian
sudo apt update && sudo apt install -y nginx
# CentOS / RHEL
sudo yum install -y nginx
# macOS
brew install nginx
Configuration
Create /etc/nginx/sites-available/hydra-node:
# Upgrade map for WebSocket support
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name node.example.com;
# Redirect HTTP → HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name node.example.com;
# ── TLS Certificates ──────────────────────────────────
# Replace with your certificate paths
# (or use Certbot for automatic Let's Encrypt certs)
ssl_certificate /etc/letsencrypt/live/node.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/node.example.com/privkey.pem;
# ── TLS Settings ──────────────────────────────────────
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# ── Proxy to Hydra Node ───────────────────────────────
location / {
proxy_pass http://127.0.0.1:4001;
# Standard proxy 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;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# Timeouts — WebSocket connections can be long-lived
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
# ── Optional: Rate Limiting ──────────────────────────
# Uncomment to add proxy-level rate limiting
# limit_req_zone $binary_remote_addr zone=hydra:10m rate=30r/s;
# limit_req zone=hydra burst=50 nodelay;
}
Enable and Start
# Enable the site
sudo ln -s /etc/nginx/sites-available/hydra-node /etc/nginx/sites-enabled/
# Test the configuration
sudo nginx -t
# Reload Nginx
sudo systemctl reload nginx
Automatic SSL with Certbot
# Install Certbot
sudo apt install -y certbot python3-certbot-nginx
# Obtain and install certificate
sudo certbot --nginx -d node.example.com
# Certbot automatically modifies your Nginx config and sets up auto-renewal
# Verify auto-renewal
sudo certbot renew --dry-run
After Certbot runs, your node is accessible at https://node.example.com with automatic certificate renewal.
Caddy
Caddy is the easiest option — it handles TLS certificates automatically with zero configuration. No Certbot needed.
Install Caddy
# Ubuntu / Debian
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update && sudo apt install -y caddy
# macOS
brew install caddy
# Docker
docker pull caddy:latest
Configuration
Create /etc/caddy/Caddyfile:
node.example.com {
# Automatic HTTPS — Caddy obtains and renews Let's Encrypt certs
# Proxy all traffic to Hydra Node
reverse_proxy 127.0.0.1:4001
# Logging
log {
output file /var/log/caddy/hydra-node.log
format json
}
# Optional: rate limiting (requires caddy-ratelimit plugin)
# rate_limit {
# zone dynamic {
# key {remote_host}
# events 60
# window 1m
# }
# }
}
That's it. Caddy automatically:
- Obtains a Let's Encrypt TLS certificate for
node.example.com - Redirects HTTP to HTTPS
- Handles WebSocket upgrades
- Renews certificates before they expire
Start Caddy
# Reload with new config
sudo systemctl reload caddy
# Or start fresh
sudo systemctl enable caddy
sudo systemctl start caddy
# Check status
sudo systemctl status caddy
Caddy with Docker Compose
version: "3.8"
services:
caddy:
image: caddy:latest
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
restart: unless-stopped
hydra-node:
image: ghcr.io/hydra-network/hydra-node:latest
volumes:
- ./node.yaml:/etc/hydra/node.yaml:ro
- hydra-data:/var/hydra/data
env_file:
- .env
command: start --config /etc/hydra/node.yaml
restart: unless-stopped
volumes:
caddy_data:
caddy_config:
hydra-data:
With the Caddyfile:
node.example.com {
reverse_proxy hydra-node:4001
}
When using Docker Compose, the proxy references the service name
hydra-nodeinstead of127.0.0.1.
Traefik
Traefik is ideal for Docker-native setups and Kubernetes. It auto-discovers services via Docker labels.
Docker Compose with Traefik
version: "3.8"
services:
traefik:
image: traefik:v3.0
command:
- "--api.dashboard=true"
- "--providers.docker=true"
- "--providers.docker.exposedByDefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
- "--certificatesresolvers.letsencrypt.acme.email=admin@example.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
# HTTP → HTTPS redirect
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
- "--entrypoints.web.http.redirections.entryPoint.scheme=https"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- traefik-certs:/letsencrypt
restart: unless-stopped
hydra-node:
image: ghcr.io/hydra-network/hydra-node:latest
labels:
- "traefik.enable=true"
# Router
- "traefik.http.routers.hydra-node.rule=Host(`node.example.com`)"
- "traefik.http.routers.hydra-node.entrypoints=websecure"
- "traefik.http.routers.hydra-node.tls.certresolver=letsencrypt"
# Service
- "traefik.http.services.hydra-node.loadbalancer.server.port=4001"
volumes:
- ./node.yaml:/etc/hydra/node.yaml:ro
- hydra-data:/var/hydra/data
env_file:
- .env
command: start --config /etc/hydra/node.yaml
restart: unless-stopped
volumes:
traefik-certs:
hydra-data:
Traefik automatically:
- Discovers the
hydra-nodeservice via Docker labels - Obtains Let's Encrypt certificates
- Routes
node.example.comto the node on port 4001 - Handles WebSocket upgrades natively
Start
docker compose up -d
# View Traefik dashboard (if enabled)
# http://localhost:8080/dashboard/
Cloudflare Tunnel (No Open Ports)
If you don't want to open any ports on your server, you can use Cloudflare Tunnel to expose your node via Cloudflare's network.
Install cloudflared
# Linux
curl -L -o cloudflared https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64
chmod +x cloudflared
sudo mv cloudflared /usr/local/bin/
# macOS
brew install cloudflare/cloudflare/cloudflared
Create and Configure Tunnel
# Login to Cloudflare
cloudflared tunnel login
# Create a tunnel
cloudflared tunnel create hydra-node
# Configure the tunnel
cat > ~/.cloudflared/config.yml << 'EOF'
tunnel: <TUNNEL_ID>
credentials-file: /home/user/.cloudflared/<TUNNEL_ID>.json
ingress:
- hostname: node.example.com
service: http://127.0.0.1:4001
originRequest:
# Required for WebSocket support
noTLSVerify: false
- service: http_status:404
EOF
# Create a DNS record pointing to the tunnel
cloudflared tunnel route dns hydra-node node.example.com
# Run the tunnel
cloudflared tunnel run hydra-node
Run as a Service
sudo cloudflared service install
sudo systemctl enable cloudflared
sudo systemctl start cloudflared
Note: Cloudflare Tunnel adds latency compared to direct connections. For time-sensitive WebSocket streams, a direct Nginx/Caddy proxy is preferred.
Firewall Configuration
Regardless of which proxy you use, configure your firewall to only expose the necessary ports:
UFW (Ubuntu)
# Allow SSH
sudo ufw allow 22/tcp
# Allow HTTP and HTTPS (for the reverse proxy)
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Do NOT allow 4001 directly — traffic goes through the proxy
# sudo ufw allow 4001/tcp ← DON'T DO THIS
# Enable firewall
sudo ufw enable
sudo ufw status
iptables
# Allow established connections
sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# Allow SSH
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# Allow HTTP/HTTPS
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# Allow localhost (for control plane on 4002)
sudo iptables -A INPUT -i lo -j ACCEPT
# Drop everything else
sudo iptables -A INPUT -j DROP
Verifying Your Setup
After configuring the reverse proxy, verify everything works:
# 1. Health check over HTTPS
curl https://node.example.com/health
# 2. Status endpoint
curl https://node.example.com/status
# 3. Fetch signals
curl https://node.example.com/public/signals
# 4. WebSocket connection
wscat -c wss://node.example.com/stream/live
# 5. Test from the platform dashboard
# Go to Nodes → your node → click "Test Connection"
If the platform shows your node as Online and the test connection succeeds, your reverse proxy is correctly configured.
Common Issues
WebSocket connections drop immediately
Your proxy isn't forwarding the Upgrade and Connection headers. Ensure your Nginx config includes:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
504 Gateway Timeout on WebSocket
Increase the proxy timeout. WebSocket connections are long-lived:
proxy_read_timeout 86400s; # 24 hours
proxy_send_timeout 86400s;
SSL certificate errors
- Ensure your domain's DNS A record points to your server's IP
- Wait a few minutes after DNS changes for propagation
- For Certbot: verify port 80 is open for the HTTP challenge
- For Caddy: it handles everything automatically — just ensure ports 80 and 443 are open
Node shows "Offline" on platform but works locally
The platform tests connectivity to your registered endpoint. Make sure:
- Your endpoint URL on the platform matches your proxy domain (e.g.
https://node.example.com, nothttp://localhost:4001) - The proxy is running and DNS resolves correctly
- No firewall is blocking inbound HTTPS traffic