# 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***](https://github.com/SubhanshuMG/bitcoin-regtest-devops)

---

## **TL;DR**

```plaintext
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:

```plaintext
🏦  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:

```plaintext
+----------------------------+         +----------------------------+
|        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)**

```plaintext
.
├─ 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**

```plaintext
git clone https://github.com/SubhanshuMG/bitcoin-regtest-devops.git
cd bitcoin-regtest-devops
docker compose up -d
```

### **Observe live logs**

```plaintext
docker compose logs -f --tail=100
```

### **Send a transaction (re-runnable)**

```plaintext
bash ./scripts/create-tx.sh 0.10
bash ./scripts/create-tx.sh 0.25  # send another, new address each run
```

### **Quick checks**

```plaintext
# 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**

```plaintext
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](http://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](http://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:

1. **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
        
2. **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**](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:
    
    ```plaintext
    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](http://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`](https://github.com/SubhanshuMG/bitcoin-regtest-devops/blob/main/scripts/create-tx.sh) | [`scripts/entrypoint.sh`](https://github.com/SubhanshuMG/bitcoin-regtest-devops/blob/main/scripts/entrypoint.sh) | [`scripts/wait-for-bitcoind.sh`](https://github.com/SubhanshuMG/bitcoin-regtest-devops/blob/main/scripts/wait-for-bitcoind.sh)
    
* *CI workflow:* [`.github/workflows/ci.yml`](https://github.com/SubhanshuMG/bitcoin-regtest-devops/blob/main/.github/workflows/ci.yml)
    
* *Demo workflow:* [`.github/workflows/demo.yml`](https://github.com/SubhanshuMG/bitcoin-regtest-devops/blob/main/.github/workflows/demo.yml)
    
* *MIT License:* [`LICENSE`](https://github.com/SubhanshuMG/bitcoin-regtest-devops/blob/main/LICENSE)
