goPool is a from-scratch Go solo Bitcoin mining pool. It connects directly to Bitcoin Core over JSON-RPC and ZMQ, exposes Stratum v1 with optional TLS, and includes a status UI and JSON APIs for monitoring.
- A solo Bitcoin pool server written in Go from the ground up for this project.
- A direct integration with Bitcoin Core (no external pool engine dependency).
- A self-hosted stack: Stratum endpoint, web status UI, and JSON APIs.
-
Simple live dashboard to quickly see pool health and mining activity.
-
Worker pages so you can check each rig's speed and recent performance.
-
Online/offline status so you can quickly see which miners are up or down.
-
Saved workers so your favorite rigs are easy to revisit anytime.
-
24-hour hashrate charts to spot drops, spikes, and stability trends.
-
Best share tracking so you can see standout shares over time.
-
Discord pings (optional): alerts when a saved worker goes offline, when it has been back online long enough to count as recovered, and when a saved worker finds a block.
-
Easy connect details shown on the site for miner setup.
-
Solo mining core: builds and submits Bitcoin blocks directly against Bitcoin Core (
getblocktemplate+submitblock) with JSON-RPC and optional ZMQ acceleration. -
Stratum v1 server: supports
mining.subscribe,mining.authorize,mining.submit,mining.configure, CKPool-stylemining.auth, and optional Stratum TLS. -
Compatibility controls: CKPool subscribe-response emulation, version-rolling support, suggest-difficulty handling, optional
mining.set_extranonceandmining.set_version_masknotifications. -
Share-policy controls: duplicate checks, nTime and version-rolling checks, worker-match enforcement, stale-job freshness modes, and optional inline submit processing.
-
VarDiff + hashrate telemetry: configurable difficulty clamps/targets, EMA smoothing, worker/pool hashrate history, and best-share tracking.
-
Safety and resilience: node/job-feed health gating (disconnect/refuse when unsafe), reconnect/invalid-submit banning, pending submission replay, and stale-feed safeguards.
-
Web status UI + JSON APIs: live overview, node/pool/server pages, worker pages, and
/api/*endpoints for monitoring/automation. -
Operator controls: optional admin panel for live settings updates, persist-to-disk controls, log tooling, and guarded reboot action.
-
Storage and backups: SQLite state store with atomic snapshots and optional Backblaze B2 upload workflow.
-
Auth/integrations: optional Clerk auth flows, saved-worker pages, Discord notification toggles, and one-time worker linking codes.
-
Performance options: Stratum socket buffer tuning, optional SIMD JSON/hash paths, and built-in profiling hooks.
The table below lists direct Go module dependencies from go.mod and the license declared in each dependency's upstream LICENSE* file.
| Library | Version | License |
|---|---|---|
| github.com/Backblaze/blazer | v0.7.2 | Apache-2.0 |
| github.com/btcsuite/btcd | v0.25.0 | ISC |
| github.com/btcsuite/btcd/btcec/v2 | v2.5.0 | ISC |
| github.com/btcsuite/btcd/btcutil | v1.2.0 | ISC |
| github.com/btcsuite/btcd/chaincfg/chainhash | v1.2.0 | ISC |
| github.com/bwmarrin/discordgo | v0.29.0 | BSD-3-Clause |
| github.com/bytedance/sonic | v1.15.2 | Apache-2.0 |
| github.com/clerk/clerk-sdk-go/v2 | v2.7.0 | MIT |
| github.com/golang-jwt/jwt/v5 | v5.3.1 | MIT |
| github.com/hako/durafmt | v0.0.0-20210608085754-5c1018a4e16b | MIT |
| github.com/martinhoefling/goxkcdpwgen | v0.1.1 | MIT |
| github.com/minio/sha256-simd | v1.0.1 | Apache-2.0 |
| github.com/pebbe/zmq4 | v1.4.0 | BSD-3-Clause |
| github.com/pelletier/go-toml | v1.9.5 | Apache-2.0 |
| github.com/remeh/sizedwaitgroup | v1.0.0 | MIT |
| golang.org/x/sys | v0.47.0 | BSD-3-Clause |
| modernc.org/sqlite | v1.56.0 | BSD-3-Clause |
Additional third-party asset notices are in THIRD_PARTY_NOTICES.md.
Stratum notes:
- goPool accepts both
mining.authorizeand CKPool-stylemining.auth, and tolerates authorize-before-subscribe (work starts after subscribe completes). - On startup and during runtime, Stratum is gated only when the node/job feed reports errors or the node is in a non-usable syncing/indexing state: new connections are refused and existing miners are disconnected to avoid wasted hashing.
Current mining.submit and mining.notify results for 100, 1000, and
10000 miners are in benchmarks/go/. The heat map below is
from the August 15, 2026 production-profile rerun.
The production benchmark profile keeps normal submit-validation checks,
vardiff, telemetry, and startup maintenance enabled. Connection-rate limits and
invalid-submit bans are relaxed so synthetic 10000-miner reject-load runs can
complete. All simulated connections use unique workers; per-share disk logging
is disabled while errors remain enabled; CPU pinning and scheduler limits are
not applied.
The reproducible suite supports goPool, pogolo, ckpool, dvb-WarpPool, and
public-pool.
It uses pogolo v1.1.4-hotfix, CKPool v1.2.0, WarpPool's main branch, and
public-pool's master branch. WarpPool is built unmodified with the stock
Enterprise profile; its 10,000-client rows are reported as failures because that profile's
shipped connection cap is 4,096.
Reproduce the benchmark and regenerate the SVG with:
./scripts/run-go-benchmarks.sh- Install Go 1.26.8 or later and ZeroMQ (
libzmq3-devor equivalent depending on your platform). - Clone the repo and build the pool:
git clone https://github.com/M45Core/M45-goPool.git cd M45-goPool go build -o goPool - Run
./goPoolonce to generate example config files underdata/config/examples/, then copy the base example intodata/config/config.tomland edit it. - Set the required
node.payout_address,node.rpc_url, and ZMQ addresses (node.zmq_hashblock_addr/node.zmq_rawblock_addr; leave empty to run RPC/longpoll-only) before restarting the pool.
You can run goPool using Docker or Docker Compose for easy deployment and reproducibility.
docker build -t gopool:local .
docker run --rm -it \
-e BUILD_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
-e BUILD_VERSION="v0.0.0-dev" \
-p 3333:3333 -p 80:80 -p 443:443 \
-v "$PWD/data:/app/data" \
gopool:local -stdoutCopy env.example to .env and adjust it as needed. Then:
docker compose up -d --build
docker compose logs -f
docker compose downFor the friendly install walkthrough, miner settings, fee disclosure, and troubleshooting, visit m45core.com/umbrel.
Tagged releases are published automatically for both linux/amd64 and
linux/arm64. The release workflow pushes an immutable versioned image to
GitHub Container Registry, starts that exact image with deterministic demo
metrics, captures its current dashboard template and CSS for the Umbrel gallery,
then dispatches the image digest and screenshot URL to the
M45Core Umbrel Community App Store.
The store verifies the source tag and image before committing its own app
version, release notes, and digest pin. This remains a community-store release;
it does not submit the app to Umbrel's official store.
The Umbrel package defaults to a 2% pool fee paid to
3B86bWqfjdQeLEr8nkeeWU6ygksc2K7MoL. When the worker supplies a valid payout
wallet through Stratum, the remaining 98% is paid to that wallet. The package
currently targets Bitcoin mainnet.
Add this community-store URL in the umbrelOS App Store UI:
https://github.com/M45Core/M45-Umbrel-Community-App-Store
Create an UMBREL_STORE_TOKEN fine-grained token limited to the community-store
repository with Contents read/write access, then add it as an Actions secret in
both repositories. The source uses it to dispatch the release; the store uses
it to push the verified metadata update because the M45Core organization
enforces read-only built-in workflow tokens. The
ghcr.io/m45core/m45-gopool package must also be public so umbrelOS can pull it
without registry credentials. Once those one-time settings are in place,
pushing a semantic version tag such as v0.1.1 creates the GitHub release,
multi-platform image, and community-store update. The workflow also supports a
manual run for an existing tag.
The source and Umbrel package can have independent versions. Before tagging, set
UMBREL_VERSION to the package version you want umbrelOS to
display. Release v0.1.1 updates the existing 0.1.0 bootstrap package and
keeps the source and Umbrel version numbers aligned.
For a checked release that runs the test suite, validates both version numbers,
creates an annotated tag, and pushes main plus the tag, use:
./scripts/release.sh v0.1.1data/config/config.tomlcontrols listener ports, core branding, node endpoints, fee percentages, and most runtime behavior.- TLS on the status UI is driven by
server.status_tls_listen(default:443). Leave it empty ("") to disable HTTPS and rely solely onserver.status_listenfor HTTP; leavingserver.status_listenempty disables HTTP entirely. data/certbot-webroot/.well-known/acme-challenge/is served on the status HTTP listener before HTTP-to-HTTPS redirects, so certbot HTTP-01 webroot validation can run while goPool is up. The bundled UI files underdata/wwware embedded into the goPool binary and are separate from this runtime certbot webroot.data/config/config.tomlalso covers bitcoind settings such asnode.rpc_url,node.rpc_cookie_path, and ZMQ addresses (node.zmq_hashblock_addr/node.zmq_rawblock_addr; leave empty to disable ZMQ and rely on RPC/longpoll). First run writes helper examples todata/config/examples/.- Optional split files:
data/config/services.tomlfor service/integration settings (auth,backblaze_backup,discord,statuslinks).data/config/policy.tomlfor submit-policy/version/bans/timeouts.data/config/tuning.tomlfor rate limits, vardiff, EMA tuning, and peer-cleaning controls.data/config/version_bits.tomlfor explicit per-bit block-version overrides (read-only; never rewritten by goPool).data/config/policy.toml[version].share_allow_out_of_mask_version_bitsallows unnegotiated or out-of-mask miner submit versions for legacy compatibility (defaulttrue).data/config/secrets.tomlfor sensitive credentials (RPC user/pass, Discord/Clerk secrets, Backblaze keys).
data/config/admin.tomlcontrols the optional admin UI at/admin. The file is auto-generated on first run withenabled = falseand a random password (read the file to see the generated secret). Update it to enable the panel, pick fresh credentials, and keep the file private. goPool writespassword_sha256on startup and clears the plaintext password after the first successful login; subsequent logins use the hash. The admin UI provides a field-based editor for the in-memory config, can force-writeconfig.toml+ split override files, and includes a reboot control; reboot requests require typingREBOOTand resubmitting the admin password.[logging]uses boolean toggles:debugenables verbose runtime logs, andnet_debugenables raw network tracing (net-debug.log). You can also force these at startup with-debugand-net-debug.share_*validation toggles live indata/config/policy.toml[mining](for exampleshare_check_duplicate).
Flags like -network, -rpc-url, -rpc-cookie, and -secrets override the corresponding config file values for a single run—they are not written back to config.toml.
-
Build directly with
go build -o goPool. Use hardware-acceleration tags such asnoavxornojsonsimdonly when necessary; see documentation/operations.md for guidance. -
GitHub Releases provide source archives only; build the executable locally.
-
To embed
build_timeandbuild_version, pass them with-ldflags:go build -ldflags="-X main.buildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ) -X main.buildVersion=v0.0.0-dev" -o goPool
- Documentation index
- Operations guide
- Stratum v1 reference
- Version-bit overrides
- JSON API reference
- Testing guide
- License
Need help? Open an issue on GitHub or refer to the documentation in documentation/ before asking for assistance.
