DEV Community

Daniel Ioni
Daniel Ioni

Posted on

Dockerizing MyZubster Onion Infrastructure: From Local Tor HA to Real Multi-Host Decentralization

Dockerizing MyZubster Onion Infrastructure: From Local Tor HA to Real Multi-Host Decentralization

Over the last few development sessions, we pushed the MyZubster Onion infrastructure from a local experimental setup into something much closer to a real decentralized transport layer.

The goal was simple: make MyZubster reachable through Tor, make the Onion layer resilient, and avoid building a fake “decentralized” architecture where every node still depends on the same physical machine.

What we have now is a Dockerized Onion stack with local high availability, an independent remote Onion node, real Tor reachability tests, and a new discovery/failover layer under active validation.

  1. Local Onion high availability with Docker

The first stage was to make the local Tor infrastructure resilient inside Docker.

We built a multi-node Onion lab with multiple Tor-backed nodes running in separate containers. The important part was not just having multiple containers, but verifying that failover actually works.

During testing, we intentionally stopped one active node and confirmed that the health logic moved to another healthy node without collapsing the Onion service.

That gave us:

Local Windows Host

├── Onion node A
├── Onion node B
└── Onion node C

with health monitoring and automatic fallback between local instances.

This is useful for service continuity inside one host, but it is not enough to call the system truly decentralized.

If Docker Desktop, the PC, the local network, or the host itself goes down, all local nodes can still disappear together.

  1. Fixing Gateway health checks

While validating the stack, we also found a practical Docker networking issue in the Gateway healthcheck.

The healthcheck was using:

http://localhost:10000/health

and Docker was reporting the Gateway as unhealthy even though the service itself was working.

We changed the check to explicit IPv4 loopback:

http://127.0.0.1:10000/health

After recreating the container, Docker reported:

Status: healthy
FailingStreak: 0
ExitCode: 0

That fix is now part of the repository rather than existing only as a local runtime patch.

Small infrastructure details like this matter a lot when failover logic depends on health signals.

  1. Moving beyond one physical host

The next step was the important one: deploy another Onion service on a completely separate machine.

We used an independent Ubuntu VPS on Aruba Cloud.

That host already runs its own Docker stack and now exposes an independent MyZubster Onion endpoint with persistent Tor state.

The remote Onion address is:

r34e7wmvekdsonvq2uxi4yhtwbb553lahikajjdiphxpnm24fzfm3syd.onion

Its Tor state is stored in a persistent Docker volume rather than being recreated every time the container restarts.

The container health state was verified as:

healthy
FailingStreak: 0

Tor also showed long-lived circuits, continuous traffic, and several thousand received connections over multiple days.

That gave us a topology closer to this:

Windows / Docker Host

├── local Onion HA nodes

└──────────── Tor Network ─────────────┐

Aruba Ubuntu VPS │
│ │
└── independent Onion service ─────────┘

This is the point where the Onion infrastructure becomes genuinely multi-host.

  1. Real cross-host Tor validation

We did not stop at container health.

From the Windows Tor probe container, we connected through SOCKS directly to the Aruba Onion service:

curl --socks5-hostname 127.0.0.1:9050 \
http://r34e7wmvekdsonvq2uxi4yhtwbb553lahikajjdiphxpnm24fzfm3syd.onion/

The response was:

HTTP/1.1 200 OK

and returned the MyZubster Gateway interface.

That test is important because it proves the complete path:

Windows

local Tor client

Tor network

Aruba hidden service

MyZubster Gateway

HTTP 200

So the remote node is not merely “running”; it is actually reachable through Tor from another independent host.

  1. Why we are not copying the same Onion private key everywhere

One tempting approach to failover would be to copy the same hidden-service private key to every server so all hosts expose the same .onion address.

We are deliberately avoiding that for now.

Duplicating the same Onion identity across multiple independently operated hosts increases the impact of a key compromise and makes key lifecycle management much harder.

Instead, the current architecture keeps separate Onion identities.

For example:

windows-ha
→ wujmjt5u3iiuyutt7hf5rap6ibzxwuo5d45sroqnmbxvlv4bntj75uad.onion

aruba-vps
→ r34e7wmvekdsonvq2uxi4yhtwbb553lahikajjdiphxpnm24fzfm3syd.onion

The better approach is to make the application aware of multiple healthy Onion endpoints.

That is what we are implementing next.

  1. Onion node discovery registry

A new Onion discovery layer is now being added to the Gateway.

The design introduces:

GET /api/onion/nodes

The endpoint is intended to return information such as:

{
"transport": "onion",
"multiHost": true,
"selectedNode": "windows-ha",
"nodes": [
{
"id": "windows-ha",
"publicUrl": "http://wujmjt5u3iiuyutt7hf5rap6ibzxwuo5d45sroqnmbxvlv4bntj75uad.onion",
"priority": 10
},
{
"id": "aruba-vps",
"publicUrl": "http://r34e7wmvekdsonvq2uxi4yhtwbb553lahikajjdiphxpnm24fzfm3syd.onion",
"priority": 20
}
]
}

The selection strategy is based on priority and health status.

If the preferred node is unhealthy, stale, or unreachable, the system can select the next healthy node instead.

  1. A real Tor health probe, not a fake status flag

The most important part of the discovery work is that health is not supposed to come from a static configuration value.

The new implementation includes a dedicated Tor probe process.

Its job is to:

start a Tor SOCKS client;
connect to every configured .onion endpoint;
verify real Tor reachability;
write a health snapshot;
update the result periodically;
mark stale or unreachable nodes as unhealthy.

The Gateway then reads that health file and makes its selection from real network observations.

Conceptually:

       ┌──────────────────────┐
       │ Onion health probe   │
       │                      │
       │ Tor SOCKS client     │
       └──────────┬───────────┘
                  │
      probes real .onion nodes
                  │
    ┌─────────────┴─────────────┐
    │                           │
Enter fullscreen mode Exit fullscreen mode

Windows Onion Aruba Onion
│ │
└─────────────┬─────────────┘

health snapshot


MyZubster Gateway


selectedNode

  1. Docker Compose integration

The discovery probe is being packaged as an optional Compose profile.

The target workflow is:

docker compose --profile onion-discovery up -d \
onion-discovery-probe \
gateway

The probe and Gateway share only the health state required for discovery.

The Gateway reads that health data in read-only mode.

The hidden-service private keys remain isolated inside their own Tor environments.

That separation is intentional.

  1. Fail-closed behavior

The discovery system uses a fail-closed strategy.

That means a node is not considered healthy just because it exists in the configuration.

If health data is:

missing,
malformed,
too old,
or reports a failed probe,

the node is treated as unavailable.

This avoids selecting a node based on stale assumptions.

For infrastructure like Tor routing, fail-closed behavior is generally much safer than pretending everything is healthy until proven otherwise.

  1. Automated tests and CI

The new Onion discovery work includes tests for:

highest-priority healthy node selection;
fallback to a secondary node;
missing health data;
stale health data;
real probe status generation;
SOCKS-based probe construction;
invalid Onion endpoint configuration.

We also fixed an unrelated compatibility issue in the repository's k6 performance script that was blocking one workflow.

The latest validation run completed successfully across the main CI, lint, quality, and performance workflows.

Smoke and load performance tests also passed.

  1. What is already verified

At this stage, we have concrete evidence for the following:

Local Onion HA ✅
Local failover ✅
Gateway Docker health ✅
Persistent remote Onion node ✅
Separate physical host ✅
Separate network environment ✅
Tor bootstrap ✅
Cross-host Onion reachability ✅
HTTP 200 through Tor ✅
Multi-host node registry ✅ implemented in PR
Real health probe ✅ implemented in PR
Automatic node selection ✅ implemented in PR

The discovery feature is still going through final rollout validation before being treated as production-ready.

  1. Where we are going next

The architecture is moving toward a transport model where MyZubster does not depend on one server, one domain, or one Onion endpoint.

Future clients and services such as Zorgax should be able to discover available nodes and choose a working transport automatically.

The target model looks like:

                   MyZubster Client / Zorgax
                              │
                              ▼
                     Discovery / Routing
                              │
            ┌─────────────────┴─────────────────┐
            │                                   │
            ▼                                   ▼
     Windows Onion                        Aruba Onion
            │                                   │
            └────────────── Tor ────────────────┘
Enter fullscreen mode Exit fullscreen mode

Later, additional independent hosts can simply be added to the registry:

node-a → Windows
node-b → Aruba
node-c → another provider
node-d → community-operated host

At that point the question changes from:

“Is the MyZubster server online?”

to:

“Which MyZubster node is currently reachable?”

That is a much more interesting architecture.

Final thought

Dockerization was only the first step.

The real progress came from separating concerns:

Docker handles reproducible runtime environments.
Tor provides network-level anonymity and Onion addressing.
Multiple physical hosts reduce infrastructure dependency.
Health probes provide real availability signals.
Discovery provides automatic routing.
Separate Onion identities avoid unnecessary private-key sharing.

We are not trying to label a cluster of containers as “decentralized.”

The objective is to gradually remove single points of failure from the actual system.

And with the Windows → Tor → Aruba → MyZubster Gateway test returning a real HTTP 200, that transition is now measurable rather than theoretical.

MyZubster Onion infrastructure is moving from local high availability toward real multi-host decentralization.

Docker #Tor #OnionServices #Decentralization #NodeJS #DevOps #SelfHosted #Privacy #OpenSource #DistributedSystems #MyZubster

Top comments (0)