Assessment of migrating the 19-server Hydra stack off Hetzner Cloud to self-hosted infrastructure (Pi fleet + Incus workers). Current cost is approximately 85 EUR/month.
| Service | Specs | Monthly Cost | Notes |
|---|---|---|---|
| hydracluster | 2c/4GB (CX23) | ~4.50 EUR | Control plane — STAYS (bootstrapping, see below) |
| hydrarelease | 2c/4GB (CX23) | ~4.50 EUR | Release server (releases.experiencenet.com) |
| hydraguard | 2c/4GB (CX23) | ~4.50 EUR | WireGuard mesh (managed by morpheus) |
| hydraexperiencelibrary | 2c/4GB (CX23) | ~4.50 EUR | |
| hydratransfer | 2c/4GB (CX23) | ~4.50 EUR | |
| hydrapipeline | 2c/4GB (CX23) | ~4.50 EUR | |
| hydraorganization | 2c/4GB (CX23) | ~4.50 EUR | |
| hydravenues | 2c/4GB (CX23) | ~4.50 EUR | |
| hydradistrict | 2c/4GB (CX23) | ~4.50 EUR | |
| hydraneck | 4c/8GB (CX33) | ~8.00 EUR | 8GB is for a co-located TP-Link Omada controller, not hydraneck (28MB) — see #413 |
| hydrahead | 2c/4GB (CX23) | ~4.50 EUR | |
| hydranps | 2c/4GB (CAX11 ARM) | ~3.60 EUR | Already running on ARM64 |
| hydrastreamingmonitor | 2c/4GB (CX23) | ~4.50 EUR | |
| hydraneckwebrtc | 2c/4GB (CX23) | ~4.50 EUR | WebRTC relay |
| hydraheadwebstream | 2c/4GB (CAX11 ARM) | ~3.60 EUR | Already running on ARM64 |
| hydraapplepipeline | 2c/4GB (CX23) | ~4.50 EUR | |
| hydraunrealengine-server | 2c/4GB (CX23) | ~4.50 EUR | |
| mirror-a | 2c/4GB (CX23) | ~4.50 EUR | |
| hydraperforce | 2c/4GB (CX23) | ~4.50 EUR | Perforce server |
These are pure Go binaries with minimal resource needs. No special network or storage requirements.
These have special requirements that make self-hosting harder:
Current Hetzner Hydra stack: ~85 EUR/month (~1020 EUR/year)
After migration: Pi hardware is one-time CAPEX (~400-600 EUR for the fleet), pays for itself in under a year. Remaining Hetzner cost only for services that must stay (WebRTC, Perforce).
hydraneck has been moved from Phase 2 to Phase 1, and the sizing question about its
host split into its own issue: #413.
Measured on the box: hydraneck.service uses 28 MB (peak 31 MB). The cx33's 8 GB is
consumed by an unrelated TP-Link Omada WiFi controller co-tenanted on the same server
(Java -Xmx1024m at 1979 MB, plus MongoDB at 194 MB, in Docker). hydraneck is a
lightweight Go binary like the rest of Phase 1 and fits the hydraskin 512 MiB default
with roughly 18x headroom.
The Omada controller is a separate decision — third-party, stateful, and unrelated to
this migration except by co-location. Tracked in #413.
Measured on the live boxes. Every Hydra service is a single Go binary, so
GOOS=linux GOARCH=arm64 is a Makefile change away — the Pi fleet is genuinely
reachable for this stack.
Incus offers three instance kinds and they differ by ~2 orders of magnitude:
OCI application container (single process, ~1MB real overhead), system
container (boots systemd, ~15 processes, ~20MB), VM (own kernel, ~200MB+).
Measured on pi-node-001: memory.current 7.3MiB for an OCI container vs 142MiB
for an idle Ubuntu system container (anon 1.0MiB vs 11.6MiB).
A Go binary is one static process, so OCI is the correct kind for nearly all of
this stack.
| Service | Measured RSS | Target | Kind | Notes |
|---|---|---|---|---|
| hydracluster | 22MB (cgroup 11MB) | STAY | — | Control plane. Circular dependency — see below. |
| hydrarelease | 20MB (peak 35MB) | Pi/worker | OCI | Also see #407 — its download path is currently broken |
| hydraneck | 28MB | Pi/worker | OCI | See #413 — the cx33 is sized for a co-located Omada controller, not this |
| hydrastreamingmonitor | 19MB (cgroup 9MB) | Pi/worker | OCI | |
| hydravenues | 19MB | Pi/worker | OCI | |
| hydraexperiencelibrary | 18MB | Pi/worker | OCI | |
| hydrapipeline | 17MB | Pi/worker | OCI | |
| hydraheadwebstream | 15MB | Pi/worker | OCI | already arm64 (cax11) — no rebuild needed |
| hydranps | not measured | Pi/worker | OCI | already arm64 (cax11) — no rebuild needed |
| hydraorganization | not measured | Pi/worker | OCI | expected ~20MB, same shape as siblings |
| hydratransfer | not measured | Pi/worker | OCI | file mover — give it disk/cache headroom |
| hydradistrict | not measured | Pi/worker | OCI | |
| hydrahead | not measured | Pi/worker | OCI | |
| hydraapplepipeline | not measured | assess | — | needs Apple/macOS runners, likely stays |
| hydraguard | svc 12MB, turnserver 32MB | STAY | — | Bootstrapping trap: recipes install WireGuard through the exec channel. If management depended on WG you could not repair WG. See #412. |
| hydraneckwebrtc | — | STAY | — | WebRTC needs low latency + bandwidth at the edge |
| hydraperforce | — | STAY | — | storage-heavy, needs reliable disk I/O |
| hydraunrealengine-server | — | STAY | — | resource requirements |
| mirror-a | hydramirror 40MB | STAY (for now) | — | needs bandwidth; also the redirect target that #407 is about |
GOARCH=arm64 targets to the Makefiles that lack them.pi-node-001 has 7.7GB; pi-node-003/004 have 3.9GB. At ~45MB real per OCI scale
(≈20MB container + ≈25MB service) the whole Phase 1 set fits one Pi several times
over — memory is not the constraint. Caps are not reservations, so the 512MiB
default can be overcommitted freely.
Previously listed as "move last". Reclassified to STAY — the dependency is
circular, not merely awkward about ordering.
hydracluster is what provisions and manages hydraskin nodes. If it ran as a scale
on one of them:
POST /api/v1/nodes/{id}/exec is how every node innodes.yaml is the source of truth for enrollment,This is the same trap already flagged for hydraguard, whose WireGuard config is
installed through the exec channel — you would need the tunnel up to repair the
tunnel. hydracluster is the more severe case because it is the layer hydraguard's
repair path itself depends on.
Keeping the control plane on infrastructure it does not manage is the point: it is
the fixed reference the self-hosted fleet is managed from. The ~4.50 EUR/month is
cheap for a recovery path that does not require driving somewhere.
Revised savings: hydracluster joins hydraguard, hydraneckwebrtc, hydraperforce,
hydraunrealengine-server and mirror-a as the Hetzner-resident set.