Deploying a Bitcoin Regtest Network with Docker and CI/CD Tools


Hands-on + R&D guide.
We’ll stand up a private Bitcoin regtest network with two nodes, peer them, mine spendable coins and send/confirm transactions; wrapped in a clean repo with CI and an optional demo workflow.
📦 Repo: https://github.com/SubhanshuMG/bitcoin-regtest-devops
TL;DR
git clone https://github.com/SubhanshuMG/bitcoin-regtest-devops.git
cd bitcoin-regtest-devops
# Start both nodes (node1 mines 101 blocks on first run)
docker compose up -d
# Watch logs live while services get healthy
docker compose logs -f --tail=100
# Create & confirm a tx from node1 ➜ node2
bash ./scripts/create-tx.sh 0.10
# Tear down
docker compose down -v
Expected output:
🏦 Creating + broadcasting 0.10 BTC from node1 ➜ node2
🪙 TX broadcast – <txid>
✅ Confirmed in block (confirmations: 1)
What you’ll build
A two-node regtest network that’s easy to run and observe:
+----------------------------+ +----------------------------+
| btc-node1 | | btc-node2 |
| bitcoind (regtest) | <-----> | bitcoind (regtest) |
| RPC : 18443 | P2P | RPC : 18445 |
| P2P : 18444 | | P2P : 18446 |
| Wallet "wallet" | | Wallet "wallet" |
| Mines initial 101 blocks | | Peers via addnode |
+----------------------------+ +----------------------------+
Why regtest? Instant blocks, infinite coins (mine on demand), deterministic behavior—perfect for Ops, testing, and fee/mempool experiments.
Project layout (overview)
.
├─ docker-compose.yml # 2 Bitcoin Core nodes, healthchecks (with start_period)
├─ Dockerfile # extends bitcoin/bitcoin:25 with jq
├─ scripts/
│ ├─ entrypoint.sh # builds bitcoind args from env; idempotent 101-block bootstrap
│ ├─ create-tx.sh # re-runnable: fresh address + confirm block each run
│ └─ wait-for-bitcoind.sh # RPC readiness probe for healthchecks
└─ .github/workflows/
├─ ci.yml # lint + build + up + live logs + tx (with timings)
└─ demo.yml # on-demand “Run workflow” demo; uploads node logs as artifact
└─ LICENSE # MIT
Step-by-step: try it locally
Pre-requisites
Docker Engine with docker compose v2
Internet for the first image pull
Clone & start
git clone https://github.com/SubhanshuMG/bitcoin-regtest-devops.git
cd bitcoin-regtest-devops
docker compose up -d
Observe live logs
docker compose logs -f --tail=100
Send a transaction (re-runnable)
bash ./scripts/create-tx.sh 0.10
bash ./scripts/create-tx.sh 0.25 # send another, new address each run
Quick checks
# Chain & peers
docker exec btc-node1 bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass -rpcport=18443 getblockchaininfo
docker exec btc-node2 bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass -rpcport=18445 getpeerinfo
# Balances
docker exec btc-node1 bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass -rpcport=18443 getbalance
docker exec btc-node2 bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass -rpcport=18445 getbalance
Clean up
docker compose down -v
Under the hood
Compose wires two nodes with distinct P2P/RPC ports and persistent volumes. Healthchecks call wait-for-bitcoind.sh to poll getblockchaininfo until RPC is ready.
Entrypoint script assembles all bitcoind flags from environment variables (robust—no brittle env expansion inside Compose). On first run, node1 mines 101 blocks so coinbase UTXOs mature and become spendable.
Transaction helper (create-tx.sh) asks node2 for a fresh address, sends coins from node1, mines one block on node1 to confirm, and reports confirmations via jq.
CI/CD that doubles as documentation
The repo ships with two workflows:
CI (ci.yml) – runs on every push/PR
Lints Bash (ShellCheck) & Dockerfile (Hadolint)
Builds the image and brings the network up
Streams container logs live until both nodes are healthy (no silent hangs)
Sends a real transaction and prints timing metrics for build/up/tx
Demo (demo.yml) – click Run workflow in GitHub Actions
Same bring-up, then broadcasts a tx (configurable amount)
Uploads docker logs from both nodes as an artifact—great for reviewers
See them in action here: https://github.com/SubhanshuMG/bitcoin-regtest-devops/actions
Tip: The repo badges in the README point to CI and the Demo workflow, so status is always visible at a glance.
R&D lab: experiments to try
Fee policy: tweak the -fallbackfee in the entrypoint or experiment with manual fees using sendtoaddress/fundrawtransaction/walletcreatefundedpsbt, then watch how confirmations behave when you mine.
Mining cadence: mine variable numbers of blocks between transactions to simulate different confirmation latencies:
docker exec btc-node1 bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass -rpcport=18443 \ generatetoaddress 3 "$(docker exec btc-node1 bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass -rpcport=18443 getnewaddress)"
Resilience: restart containers mid-run and confirm idempotency (node1 won’t re-mine its initial 101 blocks thanks to a .bootstrapped flag).
Scaling out: add node3..n in docker-compose.yml and set ADDNODE to point at node1 (or build a small mesh).
Deeper wallet ops: list UTXOs, craft raw transactions, try PSBT flows (walletcreatefundedpsbt, analyzepsbt, finalizepsbt).
Design choices & trade-offs
Compose over Kubernetes for local ergonomics; the same container pattern ports cleanly to Helm/k8s later.
One image for both nodes to minimize cache misses and binary drift.
Demo-level creds via env for clarity; prefer rpcauth or a secret manager in real deployments.
Observability-first CI: live logs + timeouts + explicit health waits—no mysterious red builds.
Idempotency: first-run mining happens once per data volume; every create-tx.sh invocation produces a fresh on-chain transaction.
Troubleshooting quick hits
Container unhealthy at start → RPC may not be ready yet; CI/healthchecks will wait. If it persists, check docker compose logs -f and verify env vars.
ShellCheck warnings → scripts are lint-clean; use _ for intentionally unused loop vars.
Hadolint DL3008 → we intentionally avoid pinning packages for CI portability; an inline ignore is documented.
Badges stale → ensure badges include ?branch=main (and &event=workflow_dispatch for the demo); run the workflow once on main.
Where to go next?
Add rpcauth or inject secrets from Vault/SOPS.
Enable txindex for advanced queries.
Export metrics/logs to your observability stack (Loki/Promtail, CloudWatch, etc.).
Integration tests: assert balances/confirmations post-tx as part of CI.
Links
Automation:
scripts/create-tx.sh
|scripts/entrypoint.sh
|scripts/wait-for-bitcoind.sh
CI workflow:
.github/workflows/ci.yml
Demo workflow:
.github/workflows/demo.yml
MIT License:
LICENSE
Subscribe to my newsletter
Read articles from Subhanshu Mohan Gupta directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Subhanshu Mohan Gupta
Subhanshu Mohan Gupta
A passionate AI DevOps Engineer specialized in creating secure, scalable, and efficient systems that bridge development and operations. My expertise lies in automating complex processes, integrating AI-driven solutions, and ensuring seamless, secure delivery pipelines. With a deep understanding of cloud infrastructure, CI/CD, and cybersecurity, I thrive on solving challenges at the intersection of innovation and security, driving continuous improvement in both technology and team dynamics.