Deployment
Deploy Tubo with Docker Compose for local testing or run it directly on VMs with systemd for production. The relay only needs one machine with a public IP and an open TCP port.
Docker Compose (local testing)
The repository ships with a docker-compose.yml that starts a relay, a gateway, and a demo service:
$ git clone https://github.com/origama/tubo.git $ cd tubo $ docker compose up -d --build # Verify everything is healthy $ curl http://127.0.0.1:8092/healthz # relay $ curl http://127.0.0.1:8443/healthz # gateway $ curl http://127.0.0.1:8444/services # admin API # Run the end-to-end smoke test $ ./tests/smoke-compose.sh
Multi-host production setup
A typical 3-machine production setup:
- Relay host — public VPS (e.g. Linode, DigitalOcean). TCP 4001 open inbound.
- Service host — any machine, behind NAT or not. No inbound ports required.
- Client host — developer laptop, CI runner, etc. No inbound ports required.
Relay host setup
# Install tubo $ curl -fsSL https://www.tubo.click/install.sh | sh # Generate swarm key (do this only once, store securely) $ tubo keygen swarm --out /etc/tubo/swarm.key $ chmod 600 /etc/tubo/swarm.key # Start relay $ tubo relay \ --swarm-key /etc/tubo/swarm.key \ --public-addr /ip4/1.2.3.4/tcp/4001 \ -d # Note the printed multiaddr, e.g.: /ip4/1.2.3.4/tcp/4001/p2p/12D3KooWExample...
systemd unit (relay)
[Unit] Description=Tubo relay After=network-online.target Wants=network-online.target [Service] ExecStart=/usr/local/bin/tubo relay \ --swarm-key /etc/tubo/swarm.key \ --public-addr /ip4/1.2.3.4/tcp/4001 Restart=on-failure RestartSec=5s User=tubo ProtectSystem=strict PrivateTmp=true [Install] WantedBy=multi-user.target
Service host setup
# Copy swarm.key from relay host (use scp or your secrets manager) $ scp relay-host:/etc/tubo/swarm.key ~/.config/tubo/swarm.key $ chmod 600 ~/.config/tubo/swarm.key # Join the private swarm $ tubo join overlay/manual \ --relay /ip4/1.2.3.4/tcp/4001/p2p/12D3KooWExample... \ --swarm-key ~/.config/tubo/swarm.key # Publish a service $ tubo attach http://127.0.0.1:8080 --name myapi -d
systemd unit (service)
[Unit] Description=Tubo attach - myapi After=network-online.target myapi.service Wants=network-online.target [Service] ExecStart=/usr/local/bin/tubo attach http://127.0.0.1:8080 --name myapi Restart=on-failure RestartSec=10s User=myapp EnvironmentFile=/etc/tubo/env [Install] WantedBy=multi-user.target
/etc/tubo/env can contain LIBP2P_PRIVATE_NETWORK_KEY=/etc/tubo/swarm.key.
Firewall rules
| Host | Port | Direction | Purpose |
|---|---|---|---|
| Relay | tcp/4001 | Inbound | libp2p bootstrap + circuit relay |
| Relay | tcp/8092 | Inbound (optional) | Health check endpoint |
| Service host | none | Outbound only | Connects outbound to relay |
| Client host | none | Outbound only | Connects outbound to relay |
Health checks
$ curl http://RELAY_HOST:8092/healthz {"status":"ok","peer_id":"12D3KooW..."} $ tubo doctor # local connectivity checks
Process supervisors
When -d is not enough — for example, when you need restart-on-failure across reboots —
use the OS process supervisor directly. Tubo processes are plain executables with no daemon.
See the Process Supervisors guide
for systemd, launchd, and runit examples.
Next steps
- Private Swarms — PSK key setup
- Access Control — grants and leases in production
- Security Model — what Tubo guarantees and what it doesn't