# One-shot Portainer deploy: public tracker + relay No Dockerfile, no custom image, no registry. Portainer runs `python:3.12-slim`, downloads this repo tarball, installs tracker/relay, and starts both services. ## 1. One-time host setup DNS points to the Docker host, e.g. `cloud.neuron.d-popov.com`. Firewall exposes only `80`, `443`, and admin SSH. Nginx Proxy Manager and this stack share a Docker network, e.g. `npm_proxy`. If needed: ```bash docker network create npm_proxy docker network connect npm_proxy ``` ## 2. Portainer stack Portainer → Stacks → Add stack → Repository. Stack file: ```text deploy/portainer/meshnet-tracker-nobuild-stack.yml ``` Deploy only after the current branch has been pushed. The stack passes the current tracker billing flags (`--billing-db`, and optional treasury flags), so an older remote `master.tar.gz` will crash on startup with unrecognized arguments. Variables: ```text SOURCE_TARBALL_URL=https://git.d-popov.com///archive/master.tar.gz PUBLIC_TRACKER_URL=https://cloud.neuron.d-popov.com PUBLIC_PROXY_NETWORK=npm_proxy CLUSTER_PEERS=https://ai.neuron.d-popov.com PUBLIC_RELAY_URL=wss://cloud.neuron.d-popov.com/ws HEARTBEAT_TIMEOUT=30 ENABLE_BILLING_DB=1 ``` For first cloud-only test, use `CLUSTER_PEERS=`. Click **Deploy the stack**. `ENABLE_BILLING_DB=1` makes billing public behavior active: `/v1/chat/completions` requires `Authorization: Bearer `. Any key is accepted and starts with the configured starter credit; calls return `402` after that balance is exhausted. Set `ENABLE_BILLING_DB=0` if existing unauthenticated clients must keep working during the first redeploy. Optional Solana treasury settlement variables: ```text SOLANA_RPC_URL=https://api.devnet.solana.com USDT_MINT= MESHNET_TREASURY_KEYPAIR_B64= SETTLE_PERIOD=86400 PAYOUT_THRESHOLD=5 PAYOUT_DUST_FLOOR=0.01 ``` Set all three treasury identity values together (`SOLANA_RPC_URL`, `USDT_MINT`, `MESHNET_TREASURY_KEYPAIR_B64`) or leave all three empty. The stack writes the decoded keypair into the tracker data volume at startup and passes it to `meshnet-tracker`; do not put this key on relay-only hosts or non-settlement trackers. Expected containers: ```text meshnet-tracker internal 8081 meshnet-relay internal 8765 ``` ## 3. Nginx Proxy Manager One Proxy Host for `cloud.neuron.d-popov.com`: ```text Forward Hostname/IP: meshnet-tracker Forward Port: 8081 Websockets Support: ON SSL: Let's Encrypt + Force SSL ``` Custom locations on the same proxy host: ```text /ws -> http://meshnet-relay:8765 /rpc -> http://meshnet-relay:8765 ``` Leave sub-folder forwarding empty. If WebSockets fail, Advanced: ```nginx proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $http_connection; proxy_read_timeout 3600s; proxy_send_timeout 3600s; ``` ## 4. Smoke test ```bash curl -s https://cloud.neuron.d-popov.com/v1/health curl -s https://cloud.neuron.d-popov.com/v1/network/map | python3 -m json.tool curl -s https://cloud.neuron.d-popov.com/v1/raft/status | python3 -m json.tool ``` Plain curl to `/ws` or `/rpc/test-peer` may show `426 Upgrade Required`; OK. It must not show nginx `502`. Dashboard: ```bash curl -s https://cloud.neuron.d-popov.com/dashboard | head ``` Then open: ```text https://cloud.neuron.d-popov.com/dashboard ``` The dashboard is served by the tracker through the same proxy host; no extra NPM location is required. If you previously deployed the build-image variant before `/var/lib/meshnet` was created as the `meshnet` user, the named volume may already be root-owned. Recreate that volume or chown it once before retrying the fixed image. ## 5. Start a node `meshnet-node` is the worker/miner process. It will ask for a model and load a model shard. Do not use it to test the tracker dashboard. ```bash meshnet-node start --tracker https://cloud.neuron.d-popov.com --model Qwen/Qwen2.5-0.5B-Instruct ``` Expected: ```text Relay advertised by tracker — using outbound tunnel wss://cloud.neuron.d-popov.com/ws Relay connected — wss://cloud.neuron.d-popov.com/rpc/ ``` ## 6. Two tracker sync Cloud stack: ```text PUBLIC_TRACKER_URL=https://cloud.neuron.d-popov.com CLUSTER_PEERS=https://ai.neuron.d-popov.com PUBLIC_RELAY_URL=wss://cloud.neuron.d-popov.com/ws ``` Local tracker: ```bash meshnet-tracker start --host 0.0.0.0 --port 8081 \ --self-url https://ai.neuron.d-popov.com \ --cluster-peers https://cloud.neuron.d-popov.com \ --relay-url wss://ai.neuron.d-popov.com/ws ``` Two Raft peers are enough for sync testing; real HA needs three trackers.