Deploy OpenZeppelin Monitor
OpenZeppelin Monitor 1.7.0 alerting a signed webhook, Slack or Discord
Just deployed
/app/data
Deploy and Host OpenZeppelin Monitor on Railway
OpenZeppelin Monitor is open-source software (AGPL-3.0) that watches blockchain activity and sends alerts when transactions, events or function calls match the conditions you set. This template runs the official Monitor 1.7.0 image and builds its configuration from template variables, so a deploy needs only the address to watch and where to send alerts.
This is not an official OpenZeppelin template. Nonceworks publishes and maintains it, and Nonceworks is not affiliated with OpenZeppelin.
About Hosting OpenZeppelin Monitor
The template deploys one service from the official openzeppelin/openzeppelin-monitor image, version 1.7.0, pinned by digest. The image includes Node.js, Python and jq, so JavaScript, Python and Bash filter scripts run as written. The Monitor's network, monitor and trigger files are built from template variables and written when the container starts. Its block checkpoint (data/) is kept on a volume. Railway's health check calls the Monitor's /metrics endpoint.
Common Use Cases
- Alert a webhook, Slack, Discord, Telegram or email when a contract or wallet is used
- Watch for large transfers, admin calls or failed transactions on your own contracts
- Filter matches with a JavaScript, Python or Bash script before anything is sent
- Replace the alerts you had in OpenZeppelin Defender Monitor (Sentinels) after its shutdown
Dependencies for OpenZeppelin Monitor Hosting
- RPC endpoints for your chain:
RPC_URL, used first, andRPC_FALLBACK_URL, used when the first fails. The defaults are two free public Base Sepolia endpoints, so a default deploy works as is. Public endpoints rate-limit a Monitor that runs all day, so for real use we recommend a provider URL with your own key inRPC_URL - An HTTPS endpoint you run that receives the alerts: a JSON body with
titleandbody, signed in anX-Signatureheader. For Slack or Discord, change the trigger type as shown under Implementation Details - The address you want to watch
Deployment Dependencies
- OpenZeppelin Monitor documentation: https://docs.openzeppelin.com/monitor/
- OpenZeppelin Monitor source: https://github.com/OpenZeppelin/openzeppelin-monitor
- OpenZeppelin Monitor image: https://hub.docker.com/r/openzeppelin/openzeppelin-monitor
Implementation Details
What it watches by default. Every transaction that involves WATCH_ADDRESS (sent to it, sent from it, or carrying a log it emitted), successful or failed, on the chain set by CHAIN_ID and RPC_URL, checked once a minute. Each alert is a POST to WEBHOOK_URL with X-Signature and X-Timestamp headers. The signature is a hex HMAC-SHA256, keyed with the generated WEBHOOK_SECRET, over the JSON payload followed by the timestamp, so your endpoint can check where an alert came from. On its first start the Monitor begins at the latest block; it does not go back through older blocks.
Another chain. Set CHAIN_ID, NETWORK_SLUG, RPC_URL, RPC_FALLBACK_URL and BLOCK_TIME_MS to the chain's values, and keep MAX_PAST_BLOCKS at two polls' worth of blocks or more, so one late poll skips no blocks. The defaults fit Base Sepolia, which makes a block every 200 ms (700 blocks). For Base mainnet, which makes one every 2 seconds, 70 is enough.
Making it yours. The three config files are variables:
MONITOR_NETWORK_JSONbecomesconfig/networks/.json. Monitor finds a network by that file name, so thenetworkslist in the monitor must use the same name (the defaults do).MONITOR_JSONbecomesconfig/monitors/monitor.json. Add an ABI underaddresses[].contract_specto match events or functions, and expressions such asvalue > 1000000.MONITOR_TRIGGERS_JSONbecomesconfig/triggers/triggers.json. Add Slack, Discord, Telegram, email or script triggers here.
Slack or Discord. The default trigger has type webhook, which posts only {"title": ..., "body": ...}. Slack and Discord incoming webhooks need other fields, so a Slack or Discord URL in WEBHOOK_URL with the default trigger delivers nothing. For Slack, put your incoming webhook URL in WEBHOOK_URL, set MONITOR_TRIGGERS_JSON to
{
"slack": {
"name": "Slack",
"trigger_type": "slack",
"config": {
"slack_url": {"type": "environment", "value": "WEBHOOK_URL"},
"message": {
"title": "${monitor.name} triggered",
"body": "Transaction ${transaction.hash} matched"
}
}
}
}
and change "triggers": ["webhook"] to "triggers": ["slack"] in MONITOR_JSON. For Discord, do the same with discord in place of slack as the key in both files, "trigger_type": "discord", and discord_url in place of slack_url. The Monitor accepts only Slack URLs that start with https://hooks.slack.com/ and Discord URLs that start with https://discord.com/api/webhooks/. Slack and Discord alerts carry no X-Signature, so WEBHOOK_SECRET is not used for them. More trigger examples are in the Monitor repository under examples/config/triggers/.
Filter scripts. Put a script in MONITOR_FILTER_SCRIPT, give it a file name with the right extension in MONITOR_FILTER_FILE (filter.js, filter.py or filter.sh), and reference it from trigger_conditions in MONITOR_JSON:
"trigger_conditions": [
{"script_path": "./config/filters/filter.js", "language": "JavaScript", "timeout_ms": 5000}
]
The script reads the match as JSON on stdin. Monitor drops the match when the script's last line of output is true and sends it when the last line is false. A script that fails also counts as false, so the match is sent. In our local test of this template, a JavaScript filter that drops transfers under 0.5 ETH let a 1 ETH transfer through and dropped a 0.1 ETH one.
Changing the configuration. Edit the variables and redeploy; the files are rewritten on every start. The image is pinned to Monitor 1.7.0 by digest; newer releases are not picked up automatically.
What it costs on Railway. Railway bills usage at its published rates: $10 per GB of RAM a month, $20 per vCPU a month, $0.15 per GB of volume a month and $0.05 per GB of egress. The Hobby plan is $5 a month and includes $5 of usage. In our local tests, polling every 5 seconds on a local chain, the Monitor used 10 to 74 MB of RAM and under 3 percent of one CPU, which at those rates is under $1.50 a month. Watching busy contracts on a fast chain uses more.
Why Deploy OpenZeppelin Monitor on Railway?
Railway runs the Monitor from one click, keeps its checkpoint on a persistent volume, restarts it if it fails, and lets you change what it watches by editing variables.
Hosted help
If you would rather not run it yourself, Nonceworks can host OpenZeppelin Monitor and Relayer for your team, with your existing Monitor configuration imported as it is, and offers fixed-price moves off Defender or off a raw server key: https://nonceworks.com
Template Content
WEBHOOK_URL
HTTPS endpoint you run that receives each alert as a JSON POST with title and body, signed in the X-Signature and X-Timestamp headers. For Slack or Discord, change the trigger in MONITOR_TRIGGERS_JSON to trigger_type slack (slack_url) or discord (discord_url), as the template overview shows: with the default webhook trigger, a Slack or Discord URL here delivers nothing.
WATCH_ADDRESS
Contract or account to watch. Every transaction that involves it (sent to it, sent from it, or carrying a log it emitted), successful or failed, sends an alert.

