Ready-to-run Docker Compose configurations for self-hosting a Monero node (monerod) on Mainnet (pruned or full archival) and Stagenet, paired with a Caddy reverse proxy for automatic Let's Encrypt HTTPS, browser-wallet CORS headers, restricted RPC hardening, and optional Tor v3 .onion hidden service support.
When you connect a wallet to a third-party public node, that node never sees your private keys or spend amounts, but it can observe your IP address, the block heights your wallet requests, and the transactions you broadcast. Running your own monerod node keeps your network metadata on your own hardware while validating every block and transaction from genesis.
For a background explainer on how nodes work and the difference between pruned and full storage, see:
Browser-based Monero wallets (such as MonkStash) run inside an https:// browser context. Modern browsers enforce two rules when a web wallet connects directly to your remote node:
- HTTPS Required (Mixed Content): An
https://page cannot make requests to a plainhttp://IP address on another machine (excepthttp://127.0.0.1loopback orhttp://*.onioninside Tor Browser). - CORS Preflight (
OPTIONS): The node must returnAccess-Control-Allow-Originand handleOPTIONSpreflight requests.
The included config/Caddyfile and config/monerod-pruned.conf configure both out of the box while blocking administrative daemon endpoints (/stop_daemon, /start_mining, etc.) at the proxy edge.
| Profile | Compose File | Disk (SSD Required) | RAM | P2P Port | Restricted RPC Port | Best For |
|---|---|---|---|---|---|---|
| Mainnet Pruned (Default) | docker-compose.yml |
~95 GB | 2 GB | 18080 |
18089 / 443 |
Personal wallets, MonkStash, Feather, Cake Wallet, merchant checkouts |
| Mainnet Full (Archival) | docker-compose.full.yml |
~240+ GB | 4 GB | 18080 |
18089 / 443 |
Archival nodes, P2Pool mining (ZMQ 18083/18084), block explorers |
| Stagenet Pruned | docker-compose.stagenet.yml |
~18 GB | 1 GB | 38080 |
38089 |
Testing payment gateways, webhooks, and wallet flows without real XMR |
Tor v3 .onion Overlay |
docker-compose.tor.yml |
+10 MB | +64 MB | - | .onion:18089 |
Connecting over Tor without opening inbound router ports |
Important: Always store the
/home/monero/.bitmoneroLMDB volume on an SSD or NVMe drive. Syncing Monero on a spinning HDD takes weeks and frequently stalls during batch verification.
cp .env.example .envEdit .env:
- If you have a domain pointing to your server (e.g.
node.example.com), setMONERO_DOMAIN=node.example.com. Caddy will automatically obtain and renew a Let's Encrypt TLS certificate on port443. - If you are running on a local machine or LAN without a domain, leave
MONERO_DOMAIN=localhostand setRPC_BIND_IP=0.0.0.0if you want other devices on your LAN to reach port18089.
docker compose up -dFollow the initial blockchain sync logs:
docker compose logs -f monerodRun an unpruned Full Mainnet node (with local ZMQ for P2Pool):
docker compose -f docker-compose.full.yml up -dRun a Stagenet node (for development & checkout testing):
docker compose -f docker-compose.stagenet.yml up -dAdd a Tor v3 .onion hidden service to your Mainnet node:
docker compose -f docker-compose.yml -f docker-compose.tor.yml up -dTo print your generated .onion address:
docker exec monerod-tor-onion cat /var/lib/tor/hidden_service/monerod/hostnameUse the included zero-dependency script scripts/check-node.mjs to query /json_rpc (get_info), check sync percentage, verify --restricted-rpc is active, and confirm CORS headers:
# Local restricted RPC port
node scripts/check-node.mjs http://127.0.0.1:18089
# Remote HTTPS domain via Caddy
node scripts/check-node.mjs https://node.example.comExample output:
Checking Monero RPC node at http://127.0.0.1:18089 ...
Network : mainnet
Version : 0.18.3.4-release
Sync Progress : 3264120 / 3264120 (100.00%)
Restricted RPC : Yes (Safe)
Incoming / Out : 24 in / 32 out
CORS Header : *
Response Latency : 14 ms
| Wallet | Connection Address | Notes |
|---|---|---|
| MonkStash | https://node.example.com (remote) or http://127.0.0.1:18089 (same machine) |
Stored encrypted inside your local wallet vault |
| Feather Wallet | node.example.com:443 (SSL) or <ip>:18089 |
Settings → Node → Add custom node |
| Cake Wallet | node.example.com:443 (HTTPS) or <ip>:18089 |
Settings → Connection and sync → New Node |
| Monero GUI / CLI | node.example.com:443 (--daemon-ssl-allow-any-cert if self-signed) |
Settings → Node → Remote node |
If your wallet fails to connect or stays stuck on "Synchronizing", check the troubleshooting steps in Monero Wallet Not Connecting. If you also need fallback public Mainnet or Stagenet endpoints while your initial sync finishes, see 90ssolo/monero-rpc-nodes.
- Unrestricted RPC (
18081) is never exposed externally: In all three configurations (config/monerod-*.conf), port18081(38081on Stagenet) is bound strictly to127.0.0.1inside the container. Only the restricted wallet RPC port (18089/38089) is reachable by Caddy or wallets. - UPnP / NAT-PMP disabled (
no-igd=1): Preventsmonerodfrom automatically requesting port forwards from consumer routers. - DNS blocklist enabled (
enable-dns-blocklist=1): Drops known malicious/spy P2P peers published by the Monero community blocklist. - Graceful LMDB shutdown (
stop_grace_period: 2m): Givesmonerodup to 2 minutes to flush LMDB pages cleanly ondocker compose downor host reboot, avoiding database corruption.
Released under the MIT License.