Configuring the RPC connection
The five environment variables the Monero addon reads, how HTTP Digest authentication is negotiated against both daemons, why the backend re-points wallet-rpc at monerod itself, and what XMR_NETWORK actually controls.
The addon reads five environment variables and nothing else. Everything about how it reaches Monero — which endpoints, which credentials, which network — is in this handful of keys, and all five are read once when the service is first constructed. A change to any of them needs a backend restart.
The five keys
Both URLs are defaulted, so an install with none of these set will still try
127.0.0.1:18081 and 127.0.0.1:18083. That is usually right, and occasionally
the reason a misconfigured install appears to half-work.
URL rules that are enforced
Two shapes are rejected outright by the diagnostics, and both are rejected for the same reason — a bad URL would otherwise be echoed verbatim inside an error message and end up in a log or a support ticket.
- A URL without a scheme. It must start with
http://orhttps://. - A URL with credentials in it.
http://user:pass@127.0.0.1:18081/json_rpcis refused with "must not embed user:pass — use XMR_RPC_USER / XMR_RPC_PASSWORD instead".
Include the /json_rpc path. The Digest handshake signs the request URI, so a
truncated path does not merely 404 — it produces an authentication failure that
reads like wrong credentials.
How authentication actually works
Monero's RPC services use HTTP Digest, not Basic. The addon does not send
credentials pre-emptively. Every call is made anonymously first, and only when
the response is a 401 does it parse the WWW-Authenticate challenge and
retry with a Digest header.
Three consequences follow from that.
Unset credentials against an authenticated service produce a specific error.
If a 401 comes back and XMR_RPC_USER / XMR_RPC_PASSWORD are empty, the call
fails with "Monero daemon/wallet RPC requires authentication but
XMR_RPC_USER and XMR_RPC_PASSWORD are not configured", naming which of the two
services rejected it. That message is the fastest diagnosis you will get on this
chain — read it carefully.
Wrong credentials stop the deposit monitor immediately. A second 401 after the Digest retry raises an authentication failure, and the deposit monitor treats that as permanent: it unmonitors the wallet rather than retrying. Unlike a daemon outage, an auth fault does not heal itself.
Only the first Digest challenge is parsed, and only MD5. If a service offers
several challenges the addon uses the first, with algorithm=MD5. Stock
monerod and monero-wallet-rpc behave this way; a reverse proxy that
rewrites the challenge may not.
Because there is one credential pair for two services, both must share the
same --rpc-login. There is no way to give the daemon and the wallet RPC
different accounts.
The backend owns the wallet RPC's daemon connection
monero-wallet-rpc only syncs against the daemon it was told to use. If it was
started without --daemon-address, or pointed somewhere else, wallet refreshes
fail with error -38, "no connection to daemon" even though the backend can
reach monerod perfectly well on its own.
So the addon takes ownership. At startup it calls set_daemon on the wallet RPC
with the host and port parsed out of XMR_DAEMON_RPC_URL, trusted: true,
ssl_support: "autodetect", and the same username and password if they are set.
It repeats that call, once, whenever a wallet refresh fails with a daemon error
(codes -38 or -9), then retries the refresh.
That recovers three common situations without operator intervention: the wallet
RPC starting before monerod is listening, a daemon restart, and a wallet RPC
launched with no daemon flags at all. It also means the daemon URL in .env is
authoritative — editing the systemd unit's --daemon-address without editing
.env changes nothing that lasts.
What XMR_NETWORK does and does not do
It does not select an endpoint. There is no XMR_MAINNET_RPC in the code
path; the network you are on is whichever network monerod was started for.
What it does is decide which address prefixes a withdrawal destination may have:
XMR_NETWORK |
Accepted first characters |
|---|---|
mainnet |
4, 8 |
stagenet |
5, 7 |
testnet |
9, A, B |
An unrecognised value falls back to the mainnet prefix set. A destination that does not match is refused with "Invalid Monero address" before anything is signed.
Set XMR_NETWORK="testnet" against a mainnet daemon and every real customer
withdrawal address is rejected as invalid. Set it to mainnet against a
stagenet daemon and stagenet addresses are rejected instead.
The diagnostics catch this: they read nettype out of get_info and fail the
check with "XMR_NETWORK=… but the daemon runs … — valid withdrawal addresses
will be rejected". Run the test after any daemon change.
Deposit addresses are labelled differently and more defensively. When a wallet
is created, the network stamped on the address record is derived from the
address prefix returned by the wallet RPC, falling back to XMR_NETWORK
only if the prefix is unrecognised. The wallet's own output is treated as ground
truth, because XMR_NETWORK can be wrong or can change after addresses were
issued.
Timeouts, retries and back-off
Worth knowing before you tune anything upstream, because Monero calls are slow by nature and a proxy with a short idle timeout will break them.
- Ordinary RPC calls: 30 second timeout, up to 3 attempts, one second apart.
- Wallet refresh: 60 seconds by default. A background refresh of a dormant wallet gets 10 minutes. A withdrawal opens its wallet with 2 minutes if the wallet was refreshed recently, or 15 minutes if it has not been touched in over six hours.
- Balance reads refresh with a 120 second budget before returning a number.
- The withdrawal relay is deliberately 1 attempt, 120 seconds. Retrying a relay could broadcast a second transaction.
When the daemon is unreachable the monitoring loop applies exponential back-off — 30 s, then 60, 120, capped at five minutes — instead of hammering a wallet RPC that is itself blocked waiting on the daemon. Daemon errors do not count against a wallet's retry budget, so a monitor survives an outage and resumes.
Keys that do nothing
Set these and nothing changes. They circulate in copied .env files and cost
people hours during debugging.
| Key | Reality |
|---|---|
XMR_WALLET_PASSWORD |
Read by no code. Wallets are created and opened with an empty password regardless. The diagnostics raise a warning when it is set, because its presence implies a protection that does not exist |
XMR_WALLET_USER |
Listed in the admin diagnostics, but read by no runtime code |
XMR_MAINNET_RPC and siblings |
Display only. The Ecosystem overview badge checks whether XMR_<NETWORK>_RPC is present; the Monero service never reads it |
Verifying by hand
curl -s -X POST http://127.0.0.1:18083/json_rpc \
-H 'Content-Type: application/json' \
--digest -u "$XMR_RPC_USER:$XMR_RPC_PASSWORD" \
-d '{"jsonrpc":"2.0","id":"0","method":"get_version"}'curl -s -X POST http://127.0.0.1:18081/json_rpc \
-H 'Content-Type: application/json' \
--digest -u "$XMR_RPC_USER:$XMR_RPC_PASSWORD" \
-d '{"jsonrpc":"2.0","id":"0","method":"get_info"}'If get_version answers and get_info reports "synchronized": true on the
nettype you configured, the connection layer is correct and any remaining
problem is elsewhere. The same two probes are what Admin → Ecosystem →
Blockchains → Requirements runs for you.