monostr / Watcher
Host a watcher
Run a watcher like watcher.monostr.com on your own server: with your own Monero node, under your own domain, for yourself or for others.
Only for yourself? A personal watcher is much simpler: no node, no domain, no open port, and it runs on a Raspberry Pi.
What it does
The watcher holds the private view keys of the tip wallets registered with it, detects incoming tips and publishes signed receipts on Nostr. Everything runs on one host with Docker Compose: a pruned Monero node (monerod), the scanner monero-lws, the watcher, and Caddy for HTTPS.
What you need
- A Linux server with Docker, the Compose plugin and Git, reachable from the internet on ports 80 and 443 (18080 for Monero P2P is recommended)
- Disk: about 110 GB for the pruned blockchain today; plan for 150 GB. An SSD makes the first sync much faster.
- RAM: 4 GB (2 GB plus swap works, slower)
- A domain or subdomain whose DNS record (A/AAAA) points to the server, for example
watcher.example.com - Time: the first blockchain sync takes from several hours to a few days
Already running a Monero node? See Using an existing node.
1. Install
git clone https://github.com/schmidt1024/monostr.git
cd monostr/watcher
cp .env.selfhost.example .env
chmod 600 .env
sed -i "s/^WATCHER_SECRET_KEY=.*/WATCHER_SECRET_KEY=$(openssl rand -hex 32)/" .env
Open .env in an editor and fill in:
WATCHER_DOMAIN- your domain, for example
watcher.example.com WATCHER_ALLOWED_PUBKEYS- your npub (several separated by commas). Only these accounts can register a wallet. Leave it empty only if you want to offer the watcher to everyone.
WATCHER_RELAYS- optional: the relays the watcher reads tip intents from and publishes receipts to. The default is fine.
2. Start
docker compose -f compose.selfhost.yml up -d --build
curl https://watcher.example.com/v1/info
/v1/info answers right away with the watcher's pubkey, network and height. Keep the secret key in .env safe: it is the watcher's identity, and the receipts are signed with it.
3. Wait for the sync
docker compose -f compose.selfhost.yml logs -f monerod
monerod is done when it logs You are now synchronized with the network. Then height in /v1/info is close to the current block height (compare with any block explorer). Connect the app only after that.
4. Connect the app
In monostr: Settings → Monero tips → Switch watcher (or Set up receiving Monero tips if you have not set up tips yet). Enter https://watcher.example.com as the watcher URL, tap Check watcher and register your tip wallet: its primary address and private view key.
The app removes your account from the previous watcher and announces the new one in your profile, so senders use it from then on.
Use a wallet only for tips: the view key shows the watcher every incoming payment of that wallet, not only tips. It can never spend anything.
Updates
cd monostr && git pull
cd watcher && docker compose -f compose.selfhost.yml up -d --build
The images of monerod, monero-lws and Caddy are pinned by digest in compose.selfhost.yml; a git pull brings tested new versions.
Backup
Back up .env: it holds the secret key. The watcher database lives in the volume monostr-watcher_watcher-data (watcher.db, SQLite) and holds open tips and receipts.
Losing it is not fatal: on the next start the watcher reads the last day's tip intents from the relays again and takes over the receipts it had already published instead of publishing them twice. Tips older than a day that were still open are lost. Wallets registered in monero-lws are added again the next time the app registers. The blockchain does not need a backup.
Using an existing node
monero-lws needs monerod's ZMQ interface, which public nodes do not offer. If you run monerod yourself, start it with:
--zmq-rpc-bind-ip=0.0.0.0 --zmq-rpc-bind-port=18082 --confirm-zmq-rpc-external-bind
--zmq-pub=tcp://0.0.0.0:18083
Never publish these ports to the internet. Then remove the monerod service and the monerod-data volume from your copy of compose.selfhost.yml, and point --daemon, --sub (monero-lws) and MONEROD_RPC_URL (watcher, restricted RPC) at your node, for example by attaching its Docker network.
Troubleshooting
/v1/info does not answer
Check the DNS record and that ports 80 and 443 are open. The Caddy log shows certificate errors:
docker compose -f compose.selfhost.yml logs caddy
"this watcher only accepts accounts of its operator"
Your npub is not in WATCHER_ALLOWED_PUBKEYS. After changing .env, apply it:
docker compose -f compose.selfhost.yml up -d
Tips stay unconfirmed
monerod or monero-lws is still syncing; check the logs of both.
More
The technical reference is SELFHOST.md in the repository. Questions or problems: open an issue or mention monostr on Nostr.