---
title: "Deploy OpenZeppelin Monitor"
description: "OpenZeppelin Monitor 1.7.0 alerting a signed webhook, Slack or Discord"
category: "Observability"
url: https://railway.com/deploy/openzeppelin-monitor
---

# Deploy OpenZeppelin Monitor

OpenZeppelin Monitor 1.7.0 alerting a signed webhook, Slack or Discord

**[Deploy OpenZeppelin Monitor on Railway](https://railway.com/template/openzeppelin-monitor)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/openzeppelin-monitor/manifest.json

- **Creator:** Nonceworks
- **Category:** Observability
- **Total deploys:** 2

## Template content

### Monitor

- **Image:** openzeppelin/openzeppelin-monitor:v1.7.0@sha256:0d5ac20151328d691cfcb90eeec77d9ce4d9f2f962ddbbb90988c8e0e7c2d616
- **Start command:** `/bin/sh -c 'mkdir -p config/networks config/monitors config/triggers config/filters data && printenv MONITOR_NETWORK_JSON > config/networks/$NETWORK_SLUG.json && printenv MONITOR_JSON > config/monitors/monitor.json && printenv MONITOR_TRIGGERS_JSON > config/triggers/triggers.json && if [ -n "$MONITOR_FILTER_SCRIPT" ]; then printenv MONITOR_FILTER_SCRIPT > config/filters/$MONITOR_FILTER_FILE; fi && exec /app/openzeppelin-monitor'`
- **Health check:** /metrics

## Documentation

# 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, and `RPC_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 in `RPC_URL`
- An HTTPS endpoint you run that receives the alerts: a JSON body with `title` and `body`, signed in an `X-Signature` header. 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_JSON` becomes `config/networks/.json`. Monitor finds a network by that file name, so the `networks` list in the monitor must use the same name (the defaults do).
- `MONITOR_JSON` becomes `config/monitors/monitor.json`. Add an ABI under `addresses[].contract_spec` to match events or functions, and expressions such as `value &gt; 1000000`.
- `MONITOR_TRIGGERS_JSON` becomes `config/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

```json
{
  "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`:

```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


## Similar templates

- [Rootprint](https://railway.com/deploy/rootprint-1) — Open-source logs and traces with full-text search on object-storage.
- [VPS Fleet Watch Hub with Beszel](https://railway.com/deploy/vps-fleet-watch-hub-with-beszel) — A persistent Beszel hub for external VPS agents and fleet alerts.
- [Pyroscope profiling](https://railway.com/deploy/pyroscope-profiling) — Protected continuous profiling with durable Pyroscope storage.

Open this page in a browser: https://railway.com/deploy/openzeppelin-monitor
