Skip to content

Deployment Topologies

Kheopsian edited this page Jul 30, 2026 · 3 revisions

🇬🇧 English · 🇫🇷 Français

Deployment Topologies

Hydra runs in three shapes. All three speak the same UI/API; the difference is where the engines live.

Monolith (default)

One process, race + hoard local. Simplest, one container. Use it whenever you control real inbound ports (home router forwarding, or a VPS): race and hoard each bind and forward their own port.

Agent (--agent-only)

A headless node hosting one or more engines, exposed over gRPC (token + TLS) for a remote front to drive. No local UI. Used to place engines on other machines.

Front-only (--front-only)

A UI/controller with no local engine. It dials remote agents and aggregates them into one dashboard, one torrent list, one add/route surface.

Sharding a monolith

A monolith can also register remote agents and add extra local engines to spread load — via the UI (Agents → Local engines) or [[engine]] blocks in the config. Each engine has an id and a role (race/hoard); you can run, say, one race + several hoards on one box.

Choosing a deployment

  • You control real inbound ports (home router, or a VPS) → monolith. race and hoard each forward their own port. One container, done.

  • You're behind a VPN that forwards a single port (gluetun + Proton / PIA / …) → do not cram both engines behind one tunnel (one port can't serve two engines). Instead:

    • a front-only controller, plus
    • one --agent-only per engine, each in its own gluetun, so each engine gets its own forwarded port.

    Each agent's gluetun UP_COMMAND pushes its rotated port to its local engine (hydra set-listen-port …), and the front-only node aggregates and drives them from one UI. This is the recommended way to seed over single-port VPNs without losing inbound on either engine.

See Categories and Routing for how new torrents get placed across engines/agents.

Running it (Docker Compose)

The flags that select a shape live on the hydra binary:

Flag Where Meaning
(none) monolith race + hoard local, full UI + API
--front-only controller UI/API, no engine; dials [[agent]]s from config
--agent-only agent engines + gRPC data-plane, no HTTP API
--agent-addr :9090 agent required with --agent-only — gRPC listen addr
--agent-token <tok> agent/front shared bearer token (front sends it, agent checks it)
--agent-tls-cert / --agent-tls-key agent TLS for the gRPC data-plane
--listen-port-hook <port> agent opt-in loopback-only POST /listen-port so a co-netns gluetun can push the forwarded port (see below)

Monolith behind a single VPN

Simplest when you only need inbound on one engine. Hydra runs in gluetun's network namespace; gluetun pushes the rotated forwarded port to Hydra's native endpoint (/api/hoard/listen-port, X-API-Key auth) — not the qBit shim.

services:
  gluetun:
    image: qmcgaw/gluetun
    cap_add: [NET_ADMIN]
    devices: [/dev/net/tun]
    environment:
      VPN_SERVICE_PROVIDER: protonvpn
      VPN_TYPE: wireguard
      # ... your WIREGUARD_* credentials ...
      VPN_PORT_FORWARDING: "on"
      FIREWALL_INPUT_PORTS: "8199"          # let the LAN reach the UI
      VPN_PORT_FORWARDING_UP_COMMAND: >
        /bin/sh -c 'wget -qO- --header="X-API-Key: ${HYDRA_API_KEY}"
        --post-data "{\"port\":{{PORTS}}}"
        http://127.0.0.1:8199/api/hoard/listen-port'
    ports:
      - "8199:8199"                          # UI, published via gluetun
    restart: unless-stopped

  hydra:
    image: ghcr.io/kheopsian/hydra:latest
    network_mode: "service:gluetun"
    depends_on: [gluetun]
    volumes:
      - ./config:/config
      - /path/to/data:/data
    restart: unless-stopped

HYDRA_API_KEY is the key from ./config/default.toml (printed once on first boot — see Installation and First Run).

Front-only + one agent per engine (single-port VPNs, recommended)

A single VPN tunnel forwards one port, which can't serve two engines. Give each engine its own gluetun, run it as an --agent-only node, and aggregate them from a --front-only controller. Each agent enables the --listen-port-hook so its own gluetun can push its forwarded port — the agent has no HTTP API otherwise.

services:
  # ---- HOARD agent, behind its own VPN ----
  gluetun-hoard:
    image: qmcgaw/gluetun
    cap_add: [NET_ADMIN]
    devices: [/dev/net/tun]
    environment:
      VPN_SERVICE_PROVIDER: protonvpn
      VPN_TYPE: wireguard
      # ... your WIREGUARD_* credentials ...
      VPN_PORT_FORWARDING: "on"
      FIREWALL_INPUT_PORTS: "9090"          # let the front reach the gRPC data-plane
      VPN_PORT_FORWARDING_UP_COMMAND: >
        /bin/sh -c 'wget -qO- --header="X-API-Key: ${AGENT_TOKEN}"
        --post-data "{\"port\":{{PORTS}}}"
        http://127.0.0.1:9091/listen-port'
    ports:
      - "9090:9090"                          # HydraAgent gRPC, reached by the front
    restart: unless-stopped

  hydra-agent-hoard:
    image: ghcr.io/kheopsian/hydra:latest
    network_mode: "service:gluetun-hoard"
    depends_on: [gluetun-hoard]
    command: >
      hydra --config /config/default.toml
            --agent-only
            --agent-addr :9090
            --agent-token ${AGENT_TOKEN}
            --listen-port-hook 9091
    volumes:
      - ./agent-hoard:/config
      - /path/to/data:/data
    restart: unless-stopped

  # ---- Front-only controller (no engine) ----
  hydra-front:
    image: ghcr.io/kheopsian/hydra:latest
    command: hydra --config /config/default.toml --front-only
    ports:
      - "8199:8199"                          # the one UI for everything
    volumes:
      - ./front:/config
    restart: unless-stopped

./agent-hoard/default.toml — one engine so the tunnel's single port serves it:

[[engine]]
id   = "hoard"
role = "hoard"

./front/default.toml — the front dials the agent through gluetun's published port:

[[agent]]
addr   = "gluetun-hoard:9090"
token  = "same value as AGENT_TOKEN"
tls_ca = ""            # path to the CA if you set --agent-tls-cert/-key on the agent

Notes:

  • Two different ports on the agent: 9090 is the gRPC data-plane, published so the front can reach it; 9091 is the loopback hook, never in ports: — it is bound to 127.0.0.1 in hard code and reachable only from inside the shared netns (gluetun + the agent), never over the VPN or the LAN.
  • Add a race engine by duplicating the pair (gluetun-race + hydra-agent-race) with its own gRPC port (e.g. 9092), its own hook (9093), a [[engine]] id="race" role="race" config, and a second [[agent]] block on the front.
  • See Gluetun (VPN) for provider-specific port-forwarding details.

Clone this wiki locally