Skip to content
atori.tech

Light wallet server (monero-lws)

Difficulty: Hard Tested v0.18.5.3

A normal Monero wallet downloads and scans every block to find your outputs. On a phone that's slow and drains the battery. A light wallet hands its view key to a server, which scans the chain in the background, all the time, and answers "what's my balance?" right away.

monero-lws by vtnerd is a light wallet server that speaks the Monero light-wallet REST API (MyMonero-compatible). Running it on your own node keeps the speed of a light wallet without giving your view key to a third party.

What the server learns

The view key lets the server see every incoming payment and your balance, forever, for that wallet. It can't spend: the spend key never leaves your wallet app. That's exactly why this server should be yours and nobody else's.

Why it's rated Hard

  • There are no release binaries. You compile monero-lws, plus the Monero code it depends on, from source. This takes 30โ€“90 minutes and ~4 GB of RAM, and every Monero upgrade means rebuilding.
  • Few wallet apps support a custom light wallet server. Check that yours does before you start (see Connect a wallet).

Requirements

  • 2.1 Monero node synced, with zmq-pub=tcp://127.0.0.1:18083 in monerod.conf (part of the 2.1 config). monerod's ZMQ RPC on 127.0.0.1:18082 is on by default.
  • 2.2 Anonymous networking: wallets reach the server only over Tor
  • ~10 GB of free disk space for the build

Preparations

Create a dedicated user and data directory:

sudo adduser --disabled-password --comment "" monero-lws
sudo mkdir /data/monero-lws
sudo chown monero-lws:monero-lws /data/monero-lws
sudo chmod 700 /data/monero-lws

chmod 700 matters here: the database in this folder holds your view keys.

Install the build dependencies, the same set as the Monero build guide for Ubuntu. monero-lws's own CI builds on Ubuntu 26.04 with these system packages, including CMake 4 and Boost 1.90:

sudo apt install -y build-essential cmake pkg-config git libssl-dev libzmq3-dev libunbound-dev \
  libsodium-dev libunwind-dev liblzma-dev libreadline-dev libexpat1-dev libpgm-dev \
  libhidapi-dev libusb-1.0-0-dev libprotobuf-dev protobuf-compiler libudev-dev \
  libboost-all-dev

Build

Build in a temporary folder as nodeadmin. Pin the release and its exact commit. Check the latest at https://github.com/vtnerd/monero-lws/tags.

cd /tmp
VERSION=1.0.4
COMMIT=969b959dc16980316e34b5dcfd7c35a718684e9a
git clone --branch v$VERSION https://github.com/vtnerd/monero-lws.git
cd monero-lws

Verify the source

Make sure you got exactly the release commit:

git rev-parse HEAD

The output must equal the COMMIT value above.

The release tag is signed by its author (Lee Clagett), but at the time of writing the signing key isn't published on GitHub, a keyserver or the Monero repository. So you can check which key signed it, but not tie that key to the author through an independent source:

git cat-file tag v$VERSION | sed -n '/BEGIN PGP/,/END PGP/p' > /tmp/lws-tag.sig
gpg --list-packets /tmp/lws-tag.sig | grep 'issuer fpr'

Expected: issuer fpr v4 CFC71BC34CA6BD25BE086FA33AC8C072BE0FEEF3. If the author publishes the key, import it and run git verify-tag v$VERSION for a full check.

Danger

If the commit or the fingerprint doesn't match, stop and ask for help in the community.

Compile

The release pins its Monero dependency (v0.18.5.3) as a git submodule:

git submodule update --init --recursive
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
make -j$(nproc)

Out of memory?

If the build gets killed, use fewer jobs (make -j2), or make sure you have the 8 GB swap file from 1.4 Configuration.

Install

sudo install -m 0755 -o root -g root -t /usr/local/bin src/monero-lws-daemon src/monero-lws-admin
monero-lws-daemon --version
cd /tmp && rm -rf /tmp/monero-lws /tmp/lws-tag.sig

Autostart on boot

sudo nano /etc/systemd/system/monero-lws.service
# MoneroBox: systemd unit for monero-lws
# /etc/systemd/system/monero-lws.service

[Unit]
Description=Monero light wallet server (monero-lws)
Requires=monerod.service
After=monerod.service

[Service]
User=monero-lws
Group=monero-lws
Type=simple
ExecStart=/usr/local/bin/monero-lws-daemon \
  --db-path=/data/monero-lws \
  --daemon=tcp://127.0.0.1:18082 \
  --sub=tcp://127.0.0.1:18083 \
  --rest-server=http://127.0.0.1:8443 \
  --auto-accept-creation \
  --log-level=1
Restart=on-failure
RestartSec=30

# Hardening
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
NoNewPrivileges=true
PrivateDevices=true

[Install]
WantedBy=multi-user.target
  • --daemon: monerod's ZMQ RPC, which the server uses to fetch blocks.
  • --sub: monerod's ZMQ pub feed, so new blocks are scanned the moment they arrive instead of every 20 seconds. P2Pool can share this same feed.
  • --rest-server: the wallet API. It listens on localhost only and is reached over Tor below. The view key travels in this traffic, so it never crosses your network unencrypted.
  • --auto-accept-creation: a wallet that logs in for the first time gets its account right away, with no approval step. See Accounts.
sudo systemctl daemon-reload
sudo systemctl enable --now monero-lws
journalctl -fu monero-lws

Reach it over Tor

Add an onion service for the wallet API:

sudo nano /etc/tor/torrc
# MoneroBox: monero-lws wallet API
HiddenServiceDir /var/lib/tor/monero-lws/
HiddenServicePort 80 127.0.0.1:8443
sudo systemctl reload tor
sudo cat /var/lib/tor/monero-lws/hostname

Save it in your password manager as your LWS onion, and keep it private. Your wallets use http://YOUR_LWS_ONION.onion. Tor encrypts the connection end to end, so plain http is fine here.

The onion address is the only lock

New accounts are accepted automatically, so anyone who has this address can register wallets on your server and make it scan for them, using your CPU and disk. Only give it to people you trust, and if it leaks, delete /var/lib/tor/monero-lws and reload Tor to get a new address.

Connect a wallet

Support for a custom light wallet server varies between apps and versions. Look for a setting like "light wallet server", "LWS" or "MyMonero-compatible server", then enter your LWS onion (with Tor enabled in the app or through Orbot).

Wallets that have supported monero-lws servers include MyMonero and Edge. Check the app's current documentation before relying on it.

Accounts

The first time a wallet logs in, the server creates its account automatically and starts scanning from the current block. Check the accounts and their scan progress:

sudo -u monero-lws monero-lws-admin --db-path=/data/monero-lws list_accounts | jq

Older wallets: scan from the restore height

A new account only sees payments from now on. For a wallet that already has history, rescan from its restore height (the block height when the wallet was created):

sudo -u monero-lws monero-lws-admin --db-path=/data/monero-lws \
  rescan RESTORE_HEIGHT YOUR_PRIMARY_ADDRESS

Some apps instead send an import request, asking the server to rescan the wallet from the start of the chain. --auto-accept-creation doesn't cover these, so they wait for you:

sudo -u monero-lws monero-lws-admin --db-path=/data/monero-lws list_requests | jq
sudo -u monero-lws monero-lws-admin --db-path=/data/monero-lws accept_requests import YOUR_PRIMARY_ADDRESS

To accept those automatically too, add --auto-accept-import to the service. Each import is a full-chain scan, though, so leave it off unless you trust everyone who has the onion address.

Add a wallet by hand

You can also add a wallet without logging in from an app. You need its primary address and secret view key (Feather: Wallet โ†’ Keys; monero-wallet-cli: viewkey):

sudo -u monero-lws monero-lws-admin --db-path=/data/monero-lws \
  add_account YOUR_PRIMARY_ADDRESS YOUR_SECRET_VIEW_KEY

Warning

This puts your view key in the shell history. Clear it afterwards with history -c (and remove the line from ~/.bash_history), or put a space before the command so the shell skips it (Ubuntu's default HISTCONTROL=ignoreboth does).

Remove an account

Deleting accounts isn't supported yet. To stop scanning one, deactivate it:

sudo -u monero-lws monero-lws-admin --db-path=/data/monero-lws \
  modify_account_status inactive YOUR_PRIMARY_ADDRESS

Upgrade

Repeat Build with the new VERSION and COMMIT, then:

sudo systemctl restart monero-lws

Rebuild after every Monero network upgrade too. New monero-lws releases bump their pinned Monero version. Releases with a new major number (1.x โ†’ 2.x) can change the database format. Read the release notes, and back up /data/monero-lws first.

Uninstall

sudo systemctl disable --now monero-lws
sudo rm /etc/systemd/system/monero-lws.service /usr/local/bin/monero-lws-*
sudo rm -rf /data/monero-lws        # deletes the stored view keys
sudo deluser --remove-home monero-lws

Remove the monero-lws block from /etc/tor/torrc, then run sudo systemctl reload tor and sudo rm -rf /var/lib/tor/monero-lws.