Running the network
What we need
Section titled “What we need”- Docker, with about 4 GB of memory free. The stack uses about 2.5 GB when it is idle.
- Go 1.27 or newer, for the faucet.
- Python 3, which the scripts use to read the node’s JSON replies.
Starting the network
Section titled “Starting the network”Let’s get a copy of the stack and start it:
git clone https://github.com/twostack/localnet-teranode.gitcd localnet-teranode./scripts/start.shThere is a bit of work behind that one command. The first time we run it, start.sh:
- writes a
.envfile with fresh secrets (the RPC password, the Postgres password, and the node’s libp2p identity), - starts the containers,
- switches Teranode from
IDLEtoRUNNING, since a fresh node mines nothing until it is told to, - creates the faucet’s Private Key,
- mines to height 101, so that the faucet has a block reward it can spend,
- waits for Arcade to answer.
The first run also has to pull the images, so it takes a few minutes. After that, start.sh is safe to run again at any time. It only does the steps that still need doing.
Do note that the chain is persistent. It survives
stop.sh, container restarts and rebooting the machine. Onlyreset.shwipes it.
Checking on it
Section titled “Checking on it”status.sh tells us whether everything is up:
./scripts/status.shIt lists the containers, the node’s state, the chain tip, whether Arcade is healthy, whether Merkle Service is connected to Teranode’s gossip, and how much the faucet can spend.
The Merkle Service line is the one to keep an eye on. Without that connection Arcade can’t follow a Transaction past ACCEPTED_BY_NETWORK, and no merkle proofs come back.
Mining a block
Section titled “Mining a block”The autominer mines a block every 10 minutes. When we don’t want to wait, we mine one ourselves:
./scripts/mine.sh # one block./scripts/mine.sh 10 # ten blocksThe block reward goes to the faucet, unless we give mine.sh an Address as its second argument. We will make use of that in Getting coins.
The interval is MINE_INTERVAL in .env, in seconds. Setting it to 0 turns the autominer off, which is what we want for tests that must not see a block they didn’t ask for. Run ./scripts/start.sh again after changing it.
Stopping and resetting
Section titled “Stopping and resetting”./scripts/stop.sh # stop the stack, keep the chain./scripts/reset.sh # stop the stack and wipe the chainreset.sh keeps the faucet’s key and .env. The next start.sh mines a fresh chain to height 101.
Where everything listens
Section titled “Where everything listens”Everything listens on 127.0.0.1. Most applications only need the first two rows.
| Port | Service | What it’s for |
|---|---|---|
| 23011 | Arcade | The ARC-compatible API. There is no /v1 prefix. Health check on 23012, SSE stream on 23013 |
| 18090 | DataHub | http://127.0.0.1:18090/api/v1 for blocks, headers, Transactions and UTXOs, with a block explorer at / |
| 19292 | RPC | Teranode’s JSON-RPC, with basic auth (rpc_user and rpc_pass in .env) |
| 18444 | Wire protocol | Bitcoin P2P, for header sync |
| 18084 | Propagation | Teranode’s own POST /tx, which bypasses Arcade |
| 19905 | libp2p | Teranode’s gossip |
| 28080 | Merkle Service | Used by Arcade. We rarely need it |
| 25432 / 29092 / 23000 | Postgres / Redpanda / Aerospike | Teranode’s data stores |
Testing from a phone? Set
HOST_IP=0.0.0.0in.envand run./scripts/start.shagain. Arcade, its SSE stream, the DataHub and the wire protocol then listen on every interface, and the app on the phone uses our computer’s LAN address. Everything else stays on127.0.0.1.
A service that runs in Docker itself can join the localnet-teranode network instead, and use the container names:
services: my-test-service: networks: [localnet-teranode] environment: ARC_URL: http://arcade:8080networks: localnet-teranode: external: trueThe full list of endpoints, from the host and from inside the network, is in the repository’s CONSUMING.md.

