Electrum server
Build Electrs from source, index the blockchain for Electrum-protocol wallets, wrap the raw TCP port in TLS with stunnel so Sparrow, Electrum, and hardware wallets can connect privately.
Your own Bitcoin Core is great at answering questions about blocks. It's lousy at answering "what's the balance of this address?", because doing that means walking the entire chain every time. Electrs bridges the gap: it reads blocks from Bitcoin Core, builds an index keyed by address and script, and serves answers on the Electrum protocol that desktop wallets like Sparrow, the BitBoxApp, Electrum, or Specter already speak.
This is why running Electrs next to your node matters for privacy. Without it, a wallet querying "public" Electrum servers tells strangers exactly which addresses belong to you. With Electrs local, the query never leaves the Pi.
What you'll do
- Install Rust so you can build Electrs, the project doesn't ship prebuilt binaries for ARM.
- Compile and install
electrs0.11.1. - Put it behind stunnel for TLS on the LAN.
- Wire it up as a systemd service so it starts on boot and stays up.
Expect a long initial index
Hands-on setup is about 30 minutes. The slow part comes after: once
electrs starts, it reads the whole chain from bitcoind and builds
its own address index. On a Pi 5 with NVMe that's roughly 8 to 12
hours; on a Pi 4 with USB SSD it can stretch past a day. The Pi
will feel sluggish while it runs, disk I/O is the bottleneck.
Nothing to babysit, check back the next day. Make sure Bitcoin Core
is fully synced (verificationprogress at 1.0) before you start,
otherwise electrs will happily index a moving target and thrash.
stunnel for raw TCP, Caddy for HTTP
Earlier versions of this guide used NGINX's stream module to wrap
Electrum's plain-TCP protocol in TLS. In v4, HTTP services get
Caddy (you'll see that in the next page), but the Electrum protocol
isn't HTTP, it's JSON-RPC over a raw TCP socket. stunnel is the
time-tested, one-config-file tool for exactly that job.
Build dependencies
Electrs is written in Rust and links against clang and cmake for
its native dependencies (mostly RocksDB). Install them once:
sudo apt install cargo clang cmake build-essential libclang-dev pkg-configUsing Debian's packaged cargo avoids the extra step of installing
rustup just for this one crate. The tradeoff is a slightly older
Rust toolchain, still current enough for Electrs 0.11.1.
Build Electrs from source
No binaries, no shortcuts: you build from a signed tag. That's the only way to know the thing running on your node matches what the maintainer, Roman Zeyde, actually released.
-
As
admin, clone the repo at a specific tag:mkdir -p /home/admin/rust cd /home/admin/rust git clone --branch v0.11.1 https://github.com/romanz/electrs.git cd electrs -
Verify the release tag. Starting with v0.11.1 Roman signs tags with SSH instead of PGP; he publishes the signing key in a dedicated repo whose
README.mdis itself PGP-signed by his old GPG key, binding the transition.Register his SSH signing key as a trusted signer for
gitand point git's SSH-signature verification at that file:mkdir -p ~/.config/git echo 'git@romanzey.de ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAZVq/3fgkildjN/MqEnhrP5550sDpFzGxMwevr5q/9w' \ > ~/.config/git/allowed_signers git config --global gpg.format ssh git config --global gpg.ssh.allowedSignersFile ~/.config/git/allowed_signers git verify-tag v0.11.1Expected output:
Good "git" signature for git@romanzey.de with ED25519 key SHA256:GifMn7F2swVKyn6MewbQHrYCs4i/bPK7gnwxhuPz/YAThe fingerprint
SHA256:GifMn7F2swVKyn6MewbQHrYCs4i/bPK7gnwxhuPz/YAis what matters; compare it against what you see in your terminal and against the key published in Roman's keys repo. -
Build it. On a Pi 5 this takes somewhere between 30 minutes and an hour; the Pi will work hard, and that's fine:
cargo build --locked --release -
Install the resulting binary to
/usr/local/bin:sudo install -m 0755 -o root -g root -t /usr/local/bin ./target/release/electrs electrs --versionExpected output:
v0.11.1
Service user and data directory
Like bitcoind, Electrs runs as its own user with no shell password
and a tidy data directory on the SSD.
sudo adduser --disabled-password --gecos "" electrs
sudo adduser electrs bitcoin
sudo mkdir /data/electrs
sudo chown -R electrs:electrs /data/electrsThe bitcoin group membership is what lets Electrs read
/data/bitcoin/.cookie for RPC auth.
Configuration
-
Switch to the
electrsuser and open a fresh config:sudo su - electrs nano /data/electrs/electrs.conf -
Paste:
# RaspiBolt: electrs configuration # /data/electrs/electrs.conf # Bitcoin Core, uses the .cookie for auth (group-readable) network = "bitcoin" daemon_dir = "/data/bitcoin" daemon_rpc_addr = "127.0.0.1:8332" daemon_p2p_addr = "127.0.0.1:8333" # Electrum protocol endpoint electrum_rpc_addr = "127.0.0.1:50001" db_dir = "/data/electrs/db" # Logging log_filters = "INFO" timestamp = true -
Do a first dry run to make sure the config is sane and Electrs can talk to Bitcoin Core. It'll immediately start indexing, that is expected and will take hours, but you only need to see the first few lines of progress before you cancel:
electrs --conf /data/electrs/electrs.confWithin a few seconds you should see lines like
serving Electrum RPC on 127.0.0.1:50001andindexing 2000 blocks: [1..2000]. Good. HitCtrl-Cand exit back toadmin:exit
Systemd unit
-
As
admin, create the unit file:sudo nano /etc/systemd/system/electrs.service -
Paste:
# RaspiBolt: systemd unit for electrs # /etc/systemd/system/electrs.service [Unit] Description=Electrs daemon Wants=bitcoind.service After=bitcoind.service [Service] ExecStart=/usr/local/bin/electrs --conf /data/electrs/electrs.conf Type=simple Restart=always TimeoutSec=120 RestartSec=30 KillMode=process User=electrs RuntimeDirectory=electrs RuntimeDirectoryMode=0710 # Hardening PrivateTmp=true PrivateDevices=true MemoryDenyWriteExecute=true [Install] WantedBy=multi-user.target -
Enable and start:
sudo systemctl enable electrs sudo systemctl start electrs -
Watch it index in real time (
Ctrl-Cto exit):sudo journalctl -f -u electrs
Electrs must finish indexing before you connect a wallet
The first index pass walks the entire chain and builds the address index on disk. On a Pi 5 with an SSD plan on eight to twelve hours; on a Pi 4, closer to a day. Don't try to connect Sparrow until the log quiets down and Electrs reports a caught-up tip, pointing a wallet at an Electrum server that's still indexing will just time out.
TLS on the LAN with stunnel
Sparrow and other wallets connect to Electrum servers over TLS on
port 50002. Electrs itself serves plaintext on 50001, which is
fine inside the Pi, but you want the network traffic encrypted
even on your home LAN. stunnel is a tiny, purpose-built proxy that
wraps a plain TCP socket in TLS and has been doing exactly that for
two decades.
Install stunnel
sudo apt install stunnel4Mint a self-signed certificate
stunnel doesn't do automatic CA issuance the way Caddy does for HTTP. You generate a self-signed certificate once and point the config at it. This takes a single command:
sudo openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \
-keyout /etc/stunnel/electrs.key \
-out /etc/stunnel/electrs.crt \
-subj "/CN=raspibolt.local"
sudo chmod 600 /etc/stunnel/electrs.keyTen-year validity is fine for a LAN certificate. Wallets will warn once that the certificate isn't from a public CA, you accept it, and they remember it from then on.
Configure stunnel
-
Create the service config:
sudo nano /etc/stunnel/electrs.conf -
Paste:
# RaspiBolt: stunnel TLS wrapper for Electrs # /etc/stunnel/electrs.conf pid = /var/run/stunnel4/electrs.pid [electrs] accept = 50002 connect = 127.0.0.1:50001 cert = /etc/stunnel/electrs.crt key = /etc/stunnel/electrs.key -
Enable and start:
sudo systemctl enable stunnel4 sudo systemctl restart stunnel4 -
Open the firewall for Electrum over TLS:
sudo ufw allow 50002/tcp comment 'Electrum SSL'
Self-signed is fine on the LAN
Wallets will flag the certificate the first time, that's expected. You accept it manually once (Sparrow does this well) or pin the certificate fingerprint in the wallet config. Getting a public-CA certificate would require a real domain name and DNS setup that most home networks don't have. Internal CA plus first-use trust is the sane default here.
Main takeaway: Electrs is indexing on :50001, stunnel wraps it
in TLS on :50002, and the firewall lets LAN wallets in. The next
page points Sparrow at it.
Remote access over Tor (optional)
You can reach your Electrum server from outside the home network by exposing it as a Tor hidden service. The wallet will need Tor running locally too.
-
Add a stanza to
/etc/tor/torrc:sudo nano /etc/tor/torrc# Hidden service: Electrum HiddenServiceDir /var/lib/tor/hidden_service_electrs/ HiddenServiceVersion 3 HiddenServicePort 50002 127.0.0.1:50002 -
Reload Tor and read the generated
.onionaddress:sudo systemctl reload tor sudo cat /var/lib/tor/hidden_service_electrs/hostnameSave that
abcdefg...xyz.onionin your password manager alongside the one you may already have for SSH. The desktop-wallet page shows how to connect Sparrow to it.
Updating Electrs later
Upgrades follow the same pattern as the first build: check the
release notes,
bump 0.11.1 in lib/versions.ts, then:
cd /home/admin/rust/electrs
git clean -xfd
git fetch
git checkout v0.11.1
git verify-tag v0.11.1
cargo clean
cargo build --locked --release
sudo cp /usr/local/bin/electrs /usr/local/bin/electrs.old
sudo install -m 0755 -o root -g root -t /usr/local/bin ./target/release/electrs
sudo systemctl restart electrsKeep the old binary around until the new one has served traffic
for a few hours, rolling back is as simple as copying
electrs.old back on top.
Bitcoin client
Install and run Bitcoin Core (bitcoind), GPG-verify the release, tune bitcoin.conf for a Pi 5 (dbcache, maxmempool), and sync the full blockchain (IBD, initial block download) through Tor.
Blockchain explorer
Run BTC RPC Explorer on Node.js 22 behind Caddy HTTPS. Private lookup for blocks, transactions, mempool, fee estimates, UTXO queries. No leakage to public explorers.

