Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Packaging & Installation

dvb-WarpPool ships in multiple formats. The release pipeline (.github/workflows/release.yml, executed by the self-hosted Forgejo runner) builds the Linux + Docker artifacts in parallel on every v* tag and signs them with the project Cosign key (cosign.pub in the repo root).

macOS and Windows are not built by CI — there are no macOS/Windows runners on the forge. The macOS .dmg + tarballs are produced per release on a Mac with packaging/macos/build-macos-release.sh and attached manually (which also means SHA256SUMS gets regenerated over the full asset set and re-signed — see §8 of docs/ci-forgejo-porting.md in the repository; that note is not part of this handbook). The Windows .msi is paused.

Update paths: After the initial install via one of the formats below, you can apply updates in two ways:

  1. Package manager (apt/dnf/docker pull/Umbrel app updater) — classic, idempotent, controlled
  2. Built-in auto-update since Phase 8 — the admin UI shows an update banner; one click runs download + sha256 verify + optional cosign verify + atomic_swap + systemd hint. See Auto-Update for the workflow.

Overview

FormatArchitectureStatusNotes
Dockeramd64 + arm64✅git.warppool.org/dvb-projekt/dvb-warppool:<tag>
.debamd64 + arm64✅apt/dpkg, systemd unit included
.rpmamd64 + arm64✅dnf/rpm, systemd unit included
.AppImagex86_64 + aarch64✅Portable, no root required (RPi 5)
.dmgaarch64 + x86_64⚠ unsigned, built manuallyNo macOS runner — built per release via packaging/macos/build-macos-release.sh; Phase B: Notarization (Dev-ID)

Linux cross-build probe (2026-09-07, branch ci/macos-cross-probe): building the macOS binaries on the Linux runner with cargo-zigbuild fails at the link step — sysinfo (via warppool-hwdetect, in every binary) binds the Objective-C runtime plus the IOKit / OpenDirectory frameworks, and core-foundation-sys needs CoreFoundation. zig ships libSystem stubs only; those libraries exist solely in Apple’s SDK, which is not redistributable. Consequently the macOS assets stay a manual step until a machine running macOS is registered as a Forgejo runner (label macos) — the release workflow’s original macos-dmg job shape can then be revived. Dropping sysinfo on macOS would remove the objc linkage but not CoreFoundation. | .msi | x64 | ⏸ paused | Not built since the forge move (no Windows runner) — WiX sources maintained | | Tarball | Linux + macOS | ✅ | Manual installs | | Umbrel | amd64 + arm64 | ✅ | packaging/umbrel/dvb-warppool/ |

Every format ships the same five binaries: dvb-warppool-daemon, dvb-warppool-cli, dvb-warppool-setup, dvb-warppool-sim and — since v1.31.0 — dvb-warppool-tune, the dvb-WarpTune auto-clock sidecar (setup guide). The AppImage carries all but the simulator. The macOS .app keeps the sidecar in Contents/Resources/ next to the daemon; the launchd agent from scripts/setup-warptune.sh --binary <path> runs it from there or from the tarball. Nothing starts the sidecar automatically: it is opt-in, per miner.

Linux .deb (Debian / Ubuntu / Mint)

# download from the release page on git.warppool.org:
wget https://git.warppool.org/dvb-projekt/dvb-WarpPool/releases/download/v1.39.2/dvb-warppool_1.39.2-1_amd64.deb
sudo apt install ./dvb-warppool_1.39.2-1_amd64.deb

# first-run wizard (Bitcoin Core RPC, pool profile, payout address):
sudo dvb-warppool-setup

# start the daemon:
sudo systemctl enable --now dvb-warppool

# UI: http://localhost:18334

What the postinst does:

  • creates the system user warppool
  • creates /var/lib/dvb-warppool/ (data, mode 0700, owner warppool) and /var/log/dvb-warppool/
  • chgrp warppool /etc/dvb-warppool so the setup wizard can atomically write into it via sudo
  • systemctl daemon-reload

Logs: journalctl -u dvb-warppool -f

Linux .rpm (Fedora / RHEL / openSUSE)

sudo dnf install ./dvb-warppool-1.39.2-1.x86_64.rpm
sudo dvb-warppool-setup
sudo systemctl enable --now dvb-warppool

post_install_script does the same as the .deb postinst (system user, directories, daemon-reload).

Linux AppImage (portable, no-root)

chmod +x dvb-WarpPool-1.39.2-x86_64.AppImage
./dvb-WarpPool-1.39.2-x86_64.AppImage

Also available as an aarch64 AppImage for ARM boards (Raspberry Pi 5, Pine64, Rock-Pi):

chmod +x dvb-WarpPool-1.39.2-aarch64.AppImage
./dvb-WarpPool-1.39.2-aarch64.AppImage

The first launch opens the setup wizard if no ~/.config/dvb-warppool/config.toml exists. After that the daemon starts directly and the UI is reachable at http://localhost:18334.

Raspberry Pi 5 (Raspberry Pi OS / Ubuntu 24.04 for arm64)

dvb-WarpPool runs very well on the Pi 5 — the Cortex-A76 cores have significantly better single-thread performance than the Pi 4 (relevant for Stratum connection handling and Bitcoin RPC). 8 GB RAM is enough for the gross (Large) profile with a few hundred workers; with 16 GB, enterprise is realistic.

Fastest path: .deb package

# Raspberry Pi OS / Ubuntu / Debian for arm64:
wget https://git.warppool.org/dvb-projekt/dvb-WarpPool/releases/download/v1.39.2/dvb-warppool_1.39.2-1_arm64.deb
sudo apt install ./dvb-warppool_1.39.2-1_arm64.deb
sudo dvb-warppool-setup
sudo systemctl enable --now dvb-warppool

Alternative: Docker (with Bitcoin Core sidecar)

docker run -d --name warppool \
  --restart unless-stopped \
  -p 3333:3333 -p 18334:18334 \
  -v /etc/dvb-warppool:/config:ro \
  -v warppool-data:/data \
  git.warppool.org/dvb-projekt/dvb-warppool:1.39.2

Multi-arch image — Docker automatically pulls the arm64 variant on the Pi.

Storage recommendation

  • NVMe HAT (e.g. Pimoroni NVMe Base, Geekworm X1003) strongly recommended. The pool DB (SQLite WAL + shares_raw eviction every 60s) puts heavy pressure on SD cards — a typical SD only lasts months under pool load, an NVMe lasts years.
  • If using SD: high-quality industrial-grade SD (e.g. Sandisk Industrial, Western Digital Endurance) — no cheap consumer SDs.
  • A USB-3 SSD is the middle-ground option if no NVMe HAT is available.

Bitcoin Core on the same Pi

8 GB RAM is enough for both (pool + Bitcoin Core with prune=10000), as long as the mempool isn’t extremely full. 16 GB is more comfortable. Bitcoin Core on its own needs 5–7 GB under normal load.

With a separate Bitcoin Core host (e.g. an Umbrel / Start9 on the LAN), 4 GB RAM is also enough on the Pi 5 for the pool alone.

hwdetect detects the Pi automatically

The setup wizard reads /proc/device-tree/model and identifies the Pi model. On the Pi 5, Environment::RaspberryPi is set, and the profile recommendation system offers gross (Large) as the highest recommended tier for that hardware — a recommendation, not an enforced cap — plus the NVMe storage hint as Severity::Info.

Pi 4 works too

The Pi 4 is tested, but the Pi 5 is recommended (higher per-core performance). Pi 3 and Pi Zero only make sense for klein (Small) profiles and are not intended for production use.

macOS .dmg

⚠ Unsigned — Gatekeeper warning on first launch. Notarization with an Apple Developer ID is planned for Phase B.

  1. Open the .dmg → drag dvb-WarpPool.app into the Applications folder
  2. Right-click → “Open” → confirm “Open” (Gatekeeper override)
  3. The first launch opens a Terminal with the setup wizard
  4. Subsequent launches: double-clicking the app opens the UI in the browser automatically

State: ~/Library/Application Support/dvb-WarpPool/ (config + data + logs)

Windows .msi

⏸ Releases currently ship no .msi. There has been no Windows build runner since the move to the project’s own forge, so the release pipeline skips this job (.github/workflows/release.yml, job 6). The WiX packaging (packaging/windows/main.wxs) is still maintained and the artifact returns as soon as a Windows runner exists. Until then, build it yourself with cargo wix on a Windows machine, or run the pool via Docker or the Linux packages. The steps below apply to an .msi you built yourself.

⚠ Unsigned — SmartScreen warning on first launch. Code-signing with an EV certificate is planned for Phase B.

  1. Double-click the .msi → “More info” → “Run anyway”
  2. Installs to C:\Program Files\dvb-WarpPool\
  3. Start the setup wizard manually: Start menu → dvb-warppool-setup
  4. Daemon via Task Scheduler or Command Prompt: dvb-warppool-daemon.exe --config C:\ProgramData\dvb-warppool\config.toml

Docker

docker run -d --name warppool \
  -p 3333:3333 -p 18334:18334 \
  -v /etc/dvb-warppool:/config:ro \
  -v warppool-data:/data \
  git.warppool.org/dvb-projekt/dvb-warppool:1.39.2 \
  --config /config/config.toml --data-dir /data

Stopping the container

docker stop sends SIGTERM (via tini, PID 1). Since v1.26.0 the daemon treats it exactly like Ctrl-C: listeners close, sessions end cleanly, the coalesced share counters are flushed, IPC templates are released. Give it the default 10 s grace period; the Umbrel manifest sets stop_grace_period: 1m.

First run: the admin account needs /config writable — once

The first visit to /admin asks you to create an admin account, and the daemon writes those credentials to secrets.toml next to your config.toml. With /config mounted read-only (as above) that write fails and the UI reports that the secrets file is not writable.

The write is atomic (temp file + rename), so it needs the directory writable — bind-mounting only secrets.toml read-write is not enough. Two ways round it:

a) Open a window for the first run, then close it. Drop :ro (and make the directory writable by the container’s uid if you run with user:), create the account, then restore :ro and recreate the container. Only the one-time bootstrap needs this: everything the admin UI stores afterwards — donation addresses, tab toggles, suppressed warnings — lives in the database under --data-dir, so read-only config is fine from then on.

b) Pre-provision secrets.toml. If it already carries admin_username, admin_password_hash and jwt_secret, no bootstrap is needed and /config can stay read-only from the start.

If you run the container as a non-root user: — note that the daemon only reads secrets.toml, but it must be able to read it. Leave the file owned by that uid with mode 600; a root-owned 600 file locks the daemon out.

Multi-arch (amd64 + arm64) is selected automatically — no --platform needed.

Signature verification (Cosign)

Every release includes SHA256SUMS plus a Cosign signature bundle (SHA256SUMS.bundle), verified against the project cosign key (cosign.pub in the repo root). Cosign 3.x writes the bundle format rather than a bare .sig, so pass --bundle:

cosign verify-blob \
  --key cosign.pub \
  --bundle SHA256SUMS.bundle \
  SHA256SUMS
# must print: Verified OK

# then against the individual files:
sha256sum --check SHA256SUMS

GitHub-era releases (<= v1.15.0) were keyless-OIDC-signed and shipped a SHA256SUMS.sig — those artifacts are gone with the account, see the repo history for the steps they used.

Build provenance

Releases ≤ v1.15.0 (GitHub era) shipped SLSA-3 provenance via slsa-github-generator (*.intoto.jsonl in the release assets). That generator is GitHub-OIDC-bound and does not run on the self-hosted forge, so newer releases rely on the project Cosign signature (above) plus reproducible builds (rebuild byte-for-byte and compare SHA256SUMS, see reproducible builds) for supply-chain assurance instead.

SBOM (SPDX)

sbom.spdx.json in every release lists all crates with their versions and licenses (via anchore/sbom-action). Usable with syft / grype for vulnerability audits.

Phase B — open loose ends

  • macOS Notarization: xcrun notarytool submit with a Developer ID certificate + Apple ID + app-specific password. Since the .dmg is currently built manually on a Mac (no macOS runner), notarization would be a local step there; if a macOS runner is ever added, APPLE_ID, APPLE_TEAM_ID and APPLE_NOTARY_PASSWORD would go into the Forgejo Actions secrets.
  • Windows code-signing: signtool sign with an EV certificate. Azure Code Sign is an alternative for CI-friendly token-based signing.
  • Logo / icon design: Currently only a 1×1 placeholder in the AppImage; macOS and Windows have no icon. Once the design is finalized (see ROADMAP.md), it needs to be placed under packaging/{appimage,macos,windows}/.

Zero-downtime restart (listener handover)

Every restart of the daemon used to leave the Stratum ports closed for the 10–15 s it takes the new process to come up — long enough for a monitoring probe to record a missed block delivery. With [server] reuse_port = true all listeners (Stratum plain and TLS, Sv2, API) bind with SO_REUSEPORT, so a second daemon can bind the same ports while the first is still serving. The kernel spreads new connections across both; once the old process closes its listeners, every new connection lands on the new one.

The handover sequence, on the host or with network_mode: host containers:

  1. Start the new daemon (same config, reuse_port = true in both).
  2. Wait until its /readyz reports rpc_ready — it now serves jobs.
  3. Send the old daemon SIGTERM (or SIGINT). It stops accepting, closes its sessions cleanly, flushes the share buffers and exits.
  4. Miners whose session was on the old process reconnect within seconds and land on the new one; they keep their last job until then.

What this does and does not remove: there is never a moment without a listener, and no half-written share is lost. Existing sessions are still cut once — that reconnect is the residual gap, typically 1–5 s per miner and bounded by the miner’s own retry timer. Docker’s default port publishing (-p 3333:3333) cannot share a port between two containers; use network_mode: host for the pool container, or run the daemon from a package. reuse_port is Unix-only and off by default.