How to Run Your Own Bitcoin Node

Running your own Bitcoin node gives you a private, fully-validating source of truth — every block and transaction verified from genesis, no third party trusted. This guide is the exact Bitcoin Core setup we run in production on Ubuntu 24.04, including the address-indexed API layer (Blockbook) that plain bitcoindcan’t give you. Copy-paste ready, with the operational gotchas we actually hit.

Hardware & disk requirements

Bitcoin’s chainstate is random-I/O heavy and the address index is compaction-heavy, so a fast NVMe SSD is mandatory — a SATA SSD will technically work but drags initial sync out for a week or more; an HDD is a non-starter. Note that the full chain with txindex is ~870 GB today and only grows (Ordinals/inscriptions inflated block sizes since 2023).

ResourceFull node (bitcoind + txindex)+ Address index (Blockbook)
CPU4+ cores8+ cores
RAM8–16 GB32–64 GB (index build is memory-hungry)
Disk~870 GB used → budget 1 TB+ NVMe+~700 GB → budget 2 TB NVMe
Initial sync1–3 days (IBD)+ several days to build the index

Don’t prune if you want to serve historical data — pruning frees disk but discards old blocks and is incompatible with txindex, so you lose getrawtransaction by hash and any historical lookups. An RPC-serving node runs unpruned with txindex=1.

Step 1 — Download and verify Bitcoin Core

Always verify the checksum — Bitcoin Core is a high-value target. Set VER to the current release shown on bitcoincore.org/en/download (we run 31.0.0):

VER=31.0.0
cd /tmp
curl -LO https://bitcoincore.org/bin/bitcoin-core-$VER/bitcoin-$VER-x86_64-linux-gnu.tar.gz
curl -LO https://bitcoincore.org/bin/bitcoin-core-$VER/SHA256SUMS

# Verify the download matches the published checksum:
sha256sum --ignore-missing --check SHA256SUMS
# -> bitcoin-$VER-x86_64-linux-gnu.tar.gz: OK

tar xzf bitcoin-$VER-x86_64-linux-gnu.tar.gz
sudo install -m755 bitcoin-$VER/bin/bitcoind bitcoin-$VER/bin/bitcoin-cli /usr/local/bin/
bitcoind --version | head -1

Step 2 — Dedicated user, data dir, and config

Run as a non-root user. Generate a strong RPC password and never commit it:

sudo useradd --system --no-create-home --shell /usr/sbin/nologin bitcoin
sudo mkdir -p /var/lib/bitcoin /etc/bitcoin
sudo chown bitcoin:bitcoin /var/lib/bitcoin
openssl rand -hex 32   # <- use this as your rpcpassword below

Create /etc/bitcoin/bitcoin.conf. RPC is bound to localhost — only widen rpcallowipto specific firewalled IPs if something off-box needs it. The ZMQ feeds are only required if you’ll add the Blockbook index in Step 5.

server=1
disablewallet=1          # RPC-only node, no wallet
txindex=1                # full tx index — needed for getrawtransaction by hash
prune=0                  # keep the whole chain

dbcache=4096             # bigger = faster initial sync, more RAM
maxmempool=300
listen=1
maxconnections=64

rpcbind=127.0.0.1
rpcallowip=127.0.0.1
rpcport=8332
rpcuser=bitcoin
rpcpassword=<PASTE_THE_GENERATED_PASSWORD>
rpcthreads=8
rpcworkqueue=64

datadir=/var/lib/bitcoin

# ZMQ feeds (only needed for an indexer like Blockbook/electrs):
zmqpubhashblock=tcp://127.0.0.1:38330
zmqpubrawblock=tcp://127.0.0.1:38330
zmqpubhashtx=tcp://127.0.0.1:38330
zmqpubrawtx=tcp://127.0.0.1:38330

Permissions gotcha: the bitcoinuser must be able to read the config or the daemon won’t start. Lock it down but keep it group-readable:

sudo chown -R root:bitcoin /etc/bitcoin
sudo chmod 750 /etc/bitcoin
sudo chmod 640 /etc/bitcoin/bitcoin.conf

Step 3 — systemd service

Create /etc/systemd/system/bitcoind.service. TimeoutStartSec=infinitymatters — the initial block download takes days and you don’t want systemd killing it as “failed to start”. TimeoutStopSec=600 gives bitcoind time to flush its UTXO cache on shutdown (kill it early and you risk a slow startup re-check).

[Unit]
Description=Bitcoin Core daemon
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=bitcoin
Group=bitcoin
ExecStart=/usr/local/bin/bitcoind -conf=/etc/bitcoin/bitcoin.conf
Restart=on-failure
RestartSec=10
TimeoutStartSec=infinity
TimeoutStopSec=600
PrivateTmp=true
NoNewPrivileges=true

[Install]
WantedBy=multi-user.target

Step 4 — Start and watch the sync

sudo systemctl daemon-reload
sudo systemctl enable --now bitcoind

# Sync status — verificationprogress reaches ~0.9999 when caught up:
bitcoin-cli -conf=/etc/bitcoin/bitcoin.conf getblockchaininfo \
  | grep -E '"blocks"|"headers"|"verificationprogress"|"size_on_disk"'

# A quick RPC smoke test:
bitcoin-cli -conf=/etc/bitcoin/bitcoin.conf getblockcount

Initial block download (IBD) validates every signature from genesis — it’s CPU- and disk-bound and takes 1–3 days on NVMe. When blocks equals headers and verificationprogress is 0.9999…, you’re at the tip. Point your apps at http://127.0.0.1:8332 with the RPC user/password.

Step 5 (optional) — Add an address-indexed API with Blockbook

Here’s the catch most guides skip: bitcoind cannot answer “what’s the balance and history of address X?” It has no address index — only per-transaction lookups. To serve wallet-style queries (address balances, full history, xpub/descriptor scanning, a REST + WebSocket API), you need a separate indexer. We run Trezor’s Blockbook (electrs is the lighter alternative if you only need Electrum-protocol queries).

Blockbook reads from your synced bitcoind over RPC + the ZMQ feeds you configured above, and builds its own RocksDB address index (~700 GB for Bitcoin). Build it from the repo’s packaging (it produces a per-coin install under /opt/coins/); it links RocksDB and ZMQ at runtime, so install those libraries first:

sudo apt-get install -y libsnappy1v5 liblz4-1 libzstd1 libbz2-1.0 \
  libgflags2.2 zlib1g libgomp1 libzmq5

Run Blockbook under systemd against your bitcoind. A minimal unit (it exposes a public REST/WS API on :9130 and an internal port on :9030):

[Unit]
Description=Blockbook daemon (Bitcoin)
After=network.target

[Service]
Type=simple
User=blockbook-bitcoin
ExecStart=/opt/coins/blockbook/bitcoin/bin/blockbook \
  -blockchaincfg=/opt/coins/blockbook/bitcoin/config/blockchaincfg.json \
  -datadir=/opt/coins/data/bitcoin/blockbook/db \
  -sync -internal=:9030 -public=:9130 \
  -dbcache=1073741824 -enablesubnewtx -extendedindex
Restart=on-failure
TimeoutStopSec=300
LimitNOFILE=500000

[Install]
WantedBy=multi-user.target

⚠️ The OOM lesson we learned the hard way: if Blockbook is killed mid-write (most commonly an out-of-memory kill during the initial index build), its RocksDB state is marked inconsistent and -repair / -fixutxowon’t clear it — you reindex from scratch, losing days. Give the box swapand cap Blockbook’s memory via a systemd drop-in (/etc/systemd/system/blockbook-bitcoin.service.d/limits.conf) so it can’t starve bitcoind or the OS:

[Service]
MemoryHigh=36G
MemoryMax=44G
OOMScoreAdjust=-100
RestartSec=60

The honest part: what running it actually costs

The install is an afternoon. The ongoing reality is the expensive part:

  • Initial sync is days, not minutes. Full validation of ~885k blocks is CPU-bound, and with the address index on top you’re looking at the better part of a week before you can serve a single query.
  • Address queries need a second heavyweight system. bitcoind alone can’t do it. Blockbook is another ~700 GB, its own multi-day build, and — as above — it reindexes from scratch if it ever gets OOM-killed mid-write.
  • The disk never stops growing. ~870 GB today and climbing; budget for migrations to bigger volumes (we’ve done exactly that). Pruning isn’t an option if you serve history.
  • Redundancy. One node is a single point of failure. Real uptime means two of everything plus a load balancer — double the disk, double the babysitting.
  • The real bill is engineer-hours, not the server. We did the full math in Self-Hosted Node vs RPC Provider.

…or skip all of it

SwiftNodes gives you a fully-synced Bitcoin endpoint plus the address-indexed API — balances, full transaction history, and xpub/descriptor scanning — without the ~870 GB chain, the ~700 GB index, or the OOM babysitting. Flat-rate pricing (no per-call metering), Bitcoin alongside dozens of other chains under one key, and a free tier to start.

Grab a key at swiftnodes.io and point your app at https://rpc.swiftnodes.io/rpc/btc?key=YOUR_API_KEY.