Build one device
Step-by-step and copy/paste friendly. This builds a local Monero node + P2Pool + XMRig on a Raspberry Pi with external SSD storage.
Do not expose Monero RPC to the internet. This guide binds RPC to 127.0.0.1 only.
- Hostname you chose (example:
hachiko) - How you connect (mDNS
hachiko.localor IP address) - External drive filesystem UUID (from
blkid) - Mountpoint you use (this guide uses
/mnt/hachiko) - Your Monero wallet address (starts with
4…)
Assumptions
- Raspberry Pi OS (64-bit) or Debian-based Pi image
- Ethernet recommended (Wi-Fi works, but can be less consistent during heavy sync IO)
- External SSD recommended (USB 3.x). Storage speed and stability matter.
- This guide assumes your normal Linux user is
hachiko. If your username is different, replacehachikoaccordingly.
Step 1 — Hardware
Raspberry Pi 5 (8GB) + active cooling recommended. External storage recommended: 1TB+ SSD.
If your SSD specs are fast but the Pi is slow, it’s often the cable/adapter. Prefer a known-good USB 3.x cable, and plug into a blue USB 3 port.
Step 2 — Flash OS + enable SSH
- Use Raspberry Pi Imager to flash Raspberry Pi OS (64-bit) to microSD.
- In Imager settings, set:
- Hostname (example:
hachiko) - Enable SSH
- Set username/password
- Configure Wi-Fi (optional)
- Hostname (example:
- Boot the Pi and SSH in.
ssh hachiko@hachiko.local
If .local doesn’t resolve, use your router’s device list to find the IP and SSH to that.
Step 3 — OS updates + required packages
3.1 Update the OS
sudo apt update
sudo apt full-upgrade -y
sudo apt autoremove -y
sudo reboot
3.2 Install baseline + build dependencies (required)
sudo apt update
sudo apt install -y \
curl wget ca-certificates gnupg jq htop tmux ufw \
lsof psmisc dnsutils iputils-ping net-tools \
hdparm iotop iperf3 \
python3 python3-venv \
git build-essential cmake pkg-config \
libssl-dev \
libzmq3-dev libuv1-dev libcurl4-openssl-dev \
libsodium-dev libpgm-dev
For iperf3 select No if prompted.
These extra dev packages prevent P2Pool build failures like missing CURL_LIBRARY, UV_LIBRARY, and ZMQ_INCLUDE_DIR.
Step 4 — Prepare external storage
Drive names can change (example: /dev/sda becomes /dev/sdb).
Don’t depend on /dev/sdX. Mount by filesystem UUID in /etc/fstab.
4.1 Identify the drive + check USB speed
lsblk -d -o NAME,MODEL,SIZE,ROTA,TRAN
lsusb -t
USB speed check: you want your storage line to show 5000M (USB 3).
If it shows 480M, you’re on USB 2 speed — swap cable/adapter/port.
4.2 Disk throughput sanity check
# Replace sdX with the current disk name shown by lsblk (example: /dev/sda)
sudo hdparm -Tt /dev/sdX
4.3 Unmount anything auto-mounted (if needed)
# Replace sdX with the current disk name shown by lsblk (example: /dev/sda)
sudo umount /dev/sdX1 2>/dev/null || true
sudo umount /dev/sdX2 2>/dev/null || true
4.4 Repartition + format (aligned, repeatable)
This erases the external drive. Double-check MODEL matches your SSD.
# Replace sdX with the current disk name shown by lsblk (example: /dev/sda)
# 1) Fresh GPT (DESTROYS existing partitions)
sudo parted -s /dev/sdX mklabel gpt
# 2) One aligned EXT4 partition
sudo parted -s -a optimal /dev/sdX mkpart primary ext4 0% 100%
# 3) Confirm you now have sdX1
lsblk -o NAME,MODEL,SIZE,MOUNTPOINT,FSTYPE,LABEL /dev/sdX
# 4) Format EXT4 + label
sudo mkfs.ext4 -F -L hachiko /dev/sdX1
4.5 Mount + persist via /etc/fstab (use filesystem UUID)
sudo mkdir -p /mnt/hachiko
sudo mount /dev/sdX1 /mnt/hachiko
# Show filesystem UUID (use this in fstab)
sudo blkid /dev/sdX1
Use UUID="..." (filesystem UUID). Ignore PARTUUID="...".
sudo nano /etc/fstab
# Add a line like this (replace YOUR-FILESYSTEM-UUID):
UUID=YOUR-FILESYSTEM-UUID /mnt/hachiko ext4 defaults,noatime,nofail 0 2
# Test it
sudo mount -a
df -h /mnt/hachiko
Step 5 — Install Monero CLI (monerod + wallet)
Current working shortcut for ARM:
https://downloads.getmonero.org/cli/linuxarm8
cd /tmp
wget -O monero.tar.bz2 "https://downloads.getmonero.org/cli/linuxarm8"
tar -xjf monero.tar.bz2
sudo mkdir -p /opt/monero
sudo mv monero-*/ /opt/monero/
cd /opt/monero/monero-*/
sudo install -m 0755 -t /usr/local/bin monerod monero-wallet-cli
which monerod
monerod --version
which monero-wallet-cli
monero-wallet-cli --version
The binaries live inside /opt/monero/monero-*/. Make sure you cd into that folder before running install.
Step 6 — Configure monerod
6.1 Create a dedicated service user
sudo useradd --system --no-create-home --shell /usr/sbin/nologin monero || true
6.2 Create directories (data on SSD, logs on rootfs)
sudo mkdir -p /etc/monero
sudo mkdir -p /mnt/hachiko/monero
sudo mkdir -p /var/log/monero
sudo chown -R monero:monero /mnt/hachiko/monero /var/log/monero
sudo chmod 0755 /var/log/monero
6.3 Create config file
sudo nano /etc/monero/monerod.conf
# /etc/monero/monerod.conf
# Data on external SSD
data-dir=/mnt/hachiko/monero
# Logging
log-file=/var/log/monero/monerod.log
max-log-file-size=104857600
log-level=0
# During initial sync you may use fast
# After initial sync completes, switch back to safe
db-sync-mode=fast
# Optional pruning
prune-blockchain=1
# Local-only RPC (do not expose)
rpc-bind-ip=127.0.0.1
rpc-bind-port=18081
# ZMQ pub (required for P2Pool)
zmq-pub=tcp://127.0.0.1:18083
Step 7 — systemd service for monerod
If the service runs as User=monero, pre-start commands also run as that user and may fail creating
/var/log/monero. PermissionsStartOnly=true runs pre-start as root.
sudo nano /etc/systemd/system/monerod.service
[Unit]
Description=Monero Daemon (monerod)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=monero
Group=monero
PermissionsStartOnly=true
ExecStartPre=/usr/bin/mkdir -p /mnt/hachiko/monero /var/log/monero
ExecStartPre=/usr/bin/chown -R monero:monero /mnt/hachiko/monero /var/log/monero
ExecStartPre=/usr/bin/chmod 0755 /var/log/monero
ExecStart=/usr/local/bin/monerod --config-file=/etc/monero/monerod.conf --non-interactive
Restart=on-failure
RestartSec=5
TimeoutStopSec=120
KillSignal=SIGINT
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now monerod
systemctl status monerod --no-pager -l
Check logs and confirm RPC + ZMQ are listening:
sudo journalctl -u monerod -n 120 --no-pager
sudo ss -ltnp | egrep ':(18081|18083)\b' || echo "not listening yet"
Step 8 — Sync the blockchain
Initial sync is the long part. Let it run. Avoid power cycling. Expect hours to days depending on SSD + network.
8.1 Watch logs
sudo journalctl -u monerod -f
8.2 Quick health checks
# Ports (monerod RPC + ZMQ)
sudo ss -ltnp | egrep ':(18081|18083)\b' || echo "not listening yet"
# Height should respond even while syncing
curl -s http://127.0.0.1:18081/get_height | jq .
Switch db-sync-mode back to safe (Step 8.3). This matters for long-term stability.
Step 8a — Install the web status server (server.py)
pip install -r requirements.txt is only needed if your server.py imports third-party packages.
If your script uses only the Python standard library, there is nothing to install.
8a.1 Create a location + put server.py on the Pi
sudo mkdir -p /opt/hachiko
sudo chown -R "$USER":"$USER" /opt/hachiko
cd /opt/hachiko
nano /opt/hachiko/server.py
- Paste the full
server.pycontents - Save: Ctrl + O → Enter
- Exit: Ctrl + X
chmod +x /opt/hachiko/server.py
ls -la /opt/hachiko/server.py
head -n 3 /opt/hachiko/server.py
scp ./server.py hachiko@hachiko.local:/opt/hachiko/server.py
chmod +x /opt/hachiko/server.py
8a.2 Create a virtualenv
cd /opt/hachiko
python3 -m venv .venv
source .venv/bin/activate
python -V
pip -V
8a.3 Run it once (quick test)
When you’re done testing, stop it with Ctrl + C and continue to 8a.4.
cd /opt/hachiko
source .venv/bin/activate
python server.py
From a browser on the same network:
http://hachiko.local:8080 (or the Pi’s IP).
8a.4 Enable the status page at boot (required)
sudo nano /etc/systemd/system/hachiko-server.service
[Unit]
Description=Hachiko local status server
After=network-online.target monerod.service
Wants=network-online.target
[Service]
Type=simple
User=hachiko
WorkingDirectory=/opt/hachiko
Environment=PYTHONUNBUFFERED=1
ExecStart=/opt/hachiko/.venv/bin/python /opt/hachiko/server.py
Restart=always
RestartSec=3
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now hachiko-server
systemctl status hachiko-server --no-pager -l
Reboot once. After it comes back up, open http://hachiko.local:8080.
Step 8.3 — After sync (required)
sudo nano /etc/monero/monerod.conf
# change:
# db-sync-mode=fast
# to:
# db-sync-mode=safe
sudo systemctl restart monerod
systemctl status monerod --no-pager
Step 9 — Validate network quality (Wi-Fi / Ethernet)
If you’re on Wi-Fi, validate link state and throughput to avoid sync stalls/timeouts.
9.1 Wi-Fi link (if applicable)
iw dev wlan0 link || true
9.2 Throughput test (Mac ↔ Pi)
On the Pi:
iperf3 -s
On the Mac (replace with your Pi’s IP):
iperf3 -c 192.168.1.123 -t 15
Step 10 — Create a wallet (required for mining payouts)
The 25-word seed is the wallet. Write it down offline and store it safely. If you lose it, funds are not recoverable.
10.1 Create the wallets folder (SSD) + lock permissions
sudo mkdir -p /mnt/hachiko/wallets
sudo chown -R "$USER":"$USER" /mnt/hachiko/wallets
sudo chmod 700 /mnt/hachiko/wallets
10.2 Create a new wallet file (interactive)
cd /mnt/hachiko/wallets
monero-wallet-cli --generate-new-wallet hachiko-wallet
Follow the prompts. When asked about background mining, choose No (this build uses P2Pool + XMRig). When it shows the 25-word seed, write it down.
10.3 Get your wallet address (for P2Pool)
Important: once you launch monero-wallet-cli, you are in an interactive prompt.
Do not paste shell commands there. To exit, type exit.
monero-wallet-cli --wallet-file /mnt/hachiko/wallets/hachiko-wallet --daemon-address 127.0.0.1:18081
Inside the wallet, run:
address
exit
Step 11 — Install + run P2Pool (required)
1) Clone with submodules. 2) Build as your normal user (not root). 3) P2Pool needs monerod RPC + ZMQ listening (18081/18083) or it won’t start stratum.
11.1 Confirm monerod RPC + ZMQ are listening
sudo ss -ltnp | egrep ':(18081|18083)\b' || echo "monerod not listening yet"
11.2 Clone with submodules (required)
sudo mkdir -p /opt/p2pool
sudo chown -R "$USER":"$USER" /opt/p2pool
cd /opt/p2pool
git clone --recurse-submodules https://github.com/SChernykh/p2pool.git .
git submodule update --init --recursive
11.3 Build (no sudo)
cd /opt/p2pool
rm -rf build
mkdir -p build
cd build
cmake ..
make -j"$(nproc)"
# Install the binary
sudo install -m 0755 /opt/p2pool/build/p2pool /usr/local/bin/p2pool
p2pool --version || true
Install these and run cmake .. again:
sudo apt update
sudo apt install -y libcurl4-openssl-dev libuv1-dev libzmq3-dev
11.4 Create runtime directories (required)
Keep this on the SSD.
sudo mkdir -p /mnt/hachiko/p2pool /mnt/hachiko/p2pool-api
sudo chown -R "$USER":"$USER" /mnt/hachiko/p2pool /mnt/hachiko/p2pool-api
11.5 Start P2Pool at boot (required)
sudo nano /etc/systemd/system/p2pool.service
[Unit]
Description=P2Pool (Monero decentralized pool)
After=network-online.target monerod.service
Wants=network-online.target
[Service]
Type=simple
User=hachiko
WorkingDirectory=/mnt/hachiko/p2pool
# Replace WALLET_ADDRESS with your wallet address from Step 10 (starts with 4...)
# --mini is recommended for small miners (Pi)
ExecStart=/usr/local/bin/p2pool \
--host 127.0.0.1 --rpc-port 18081 --zmq-port 18083 \
--wallet WALLET_ADDRESS \
--data-api /mnt/hachiko/p2pool-api \
--stratum 127.0.0.1:3333 \
--mini
Restart=always
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now p2pool
systemctl status p2pool --no-pager -l
11.6 Confirm stratum is listening
sudo ss -ltnp | egrep ':(3333)\b' || echo "p2pool stratum not listening yet"
sudo journalctl -u p2pool -n 120 --no-pager
Step 12 — Install + run XMRig (required)
XMRig connects to your local P2Pool stratum at 127.0.0.1:3333. P2Pool pays your wallet address from Step 10.
12.1 Build XMRig from source (recommended)
sudo mkdir -p /opt/xmrig
sudo chown -R "$USER":"$USER" /opt/xmrig
cd /opt/xmrig
git clone https://github.com/xmrig/xmrig.git src
cd /opt/xmrig/src
mkdir -p build
cd build
cmake ..
make -j"$(nproc)"
sudo install -m 0755 xmrig /usr/local/bin/xmrig
xmrig --version || true
12.2 Create XMRig config for local P2Pool
sudo mkdir -p /etc/xmrig
sudo nano /etc/xmrig/config.json
{
"autosave": true,
"donate-level": 0,
"cpu": true,
"opencl": false,
"cuda": false,
"pools": [
{
"url": "127.0.0.1:3333",
"user": "x",
"pass": "x",
"keepalive": true,
"tls": false
}
]
}
12.3 Start XMRig at boot (required)
sudo nano /etc/systemd/system/xmrig.service
[Unit]
Description=XMRig (CPU miner) -> local P2Pool
After=network-online.target p2pool.service
Wants=network-online.target
[Service]
Type=simple
User=hachiko
ExecStart=/usr/local/bin/xmrig --config=/etc/xmrig/config.json
Restart=always
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now xmrig
systemctl status xmrig --no-pager -l
12.4 Sanity checks
# XMRig should say it's connected to 127.0.0.1:3333
sudo journalctl -u xmrig -n 160 --no-pager
# P2Pool should show miners connected / shares
sudo journalctl -u p2pool -n 160 --no-pager
Step 13 — UFW (LAN-only firewall)
Allow SSH + status page from your LAN. Do not expose RPC (18081). Keep P2Pool stratum local-only (3333 bound to 127.0.0.1).
13.1 Check if UFW is active
sudo ufw status verbose
13.2 Find your LAN subnet (quick)
This prints the Pi’s IPv4 + CIDR. Most home LANs are 192.168.x.x/24 or 10.x.x.x/24.
ip -4 addr show | egrep 'inet ' | awk '{print $2}'
13.3 Enable UFW (recommended)
Safe path: add SSH allow first, then enable.
# Replace 192.168.0.0/16 if your LAN uses something else
LAN_CIDR="192.168.0.0/16"
# Allow SSH from LAN
sudo ufw allow from ${LAN_CIDR} to any port 22 proto tcp
# Allow status page from LAN (8080)
sudo ufw allow from ${LAN_CIDR} to any port 8080 proto tcp
# Defaults
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Enable
sudo ufw enable
# Review
sudo ufw status numbered
If you want to accept inbound Monero P2P connections, allow TCP 18080 from anywhere. (RPC is still local-only, and should not be opened.)
sudo ufw allow 18080/tcp
sudo ufw status numbered
Step 14 — Final “plug and play” verification
14.1 Confirm services are enabled + running
systemctl is-enabled monerod p2pool xmrig hachiko-server 2>/dev/null || true
systemctl status monerod p2pool xmrig hachiko-server --no-pager -l
14.2 Confirm ports (local-only)
# monerod RPC + ZMQ should listen on 127.0.0.1
sudo ss -ltnp | egrep ':(18081|18083)\b' || true
# p2pool stratum should listen on 127.0.0.1:3333
sudo ss -ltnp | egrep ':(3333)\b' || true
14.3 Reboot test (the appliance test)
sudo reboot
After reboot, confirm:
http://hachiko.local:8080loads on your LANsystemctl status monerod p2pool xmrig hachiko-servershows all running- XMRig logs show connection to
127.0.0.1:3333
Legal
You are responsible for complying with local laws and regulations. This project is not affiliated with the Linux Foundation, Monero Project, or any other organization.
Read the legal page