Every Web3 developer hits the same wall: the script that catches on-chain events works fine on a laptop, then stops the moment the lid closes. Public RPC endpoints rate-limit you into silence, and your alerting logic disappears the second your machine sleeps.
Chainstack is a managed blockchain infrastructure platform that gives you private RPC endpoints across 70+ chains without running your own node. Instead of syncing a full Ethereum node (which needs 2+ TB of NVMe storage and weeks of sync time), you get a dedicated HTTPS and WSS URL that connects directly to Chainstack’s infrastructure. Your application talks to Chainstack; Chainstack talks to the chain.
This guide covers a real install on is*hosting’s Start VPS plan: a Python bot that subscribes to the Ethereum mempool via Chainstack’s WebSocket endpoint and runs continuously as a systemd service, catching large ETH transfers around the clock.
Hard requirements before starting:
The monitor script uses under 50 MB of RAM at rest. Any plan from Start upward covers it comfortably. If you plan to run additional services on the same VPS alongside the bot, the Medium plan gives you more headroom.
|
Use case |
RAM |
CPU |
is*hosting plan (annual billing) |
|
Monitor bot only |
2 GB |
2 CPU |
Start at $10.19/mo |
|
Bot + other services |
4 GB |
3 CPU |
Medium at $21.24/mo |
Dedicated IPv4, weekly backups, and enough headroom from the Start plan up to run the bot 24/7 in 40+ locations.
All is*hosting VPS plans include a dedicated IPv4 address by default and weekly VPS backups. Docker is not pre-installed on is*hosting VPS plans. This guide uses Python directly, so that is not a concern here.
Sign in at console.chainstack.com. From the Projects dashboard, click + Add project, enter a project name, and click Create.
Inside the project, click the Ethereum button under “Join network.” Chainstack provisions a Global Node and takes you directly to the node detail page. The node status shows Running within a few seconds.
Scroll down on the node page to the Access and credentials section. You need two values:
Both follow the pattern https://ethereum-mainnet.core.chainstack.com/<your-api-key> and wss://ethereum-mainnet.core.chainstack.com/<your-api-key>. Copy both.
Note: Scroll all the way down to the Access and credentials section to fetch the required endpoints.
At is*hosting checkout, select Ubuntu, then v. 24 for Ubuntu 24.04. Connect via SSH as root once provisioning completes.
apt update && apt install -y python3-pip python3-venv
This installs 63 packages and uses roughly 272 MB of disk space. The relevant output confirms success:
Setting up python3-venv (3.12.3-0ubuntu2.1) ...
Setting up python3-pip (24.0+dfsg-1ubuntu1.3) ...
Create the project directory and a virtual environment:
mkdir -p ~/eth-monitor
cd ~/eth-monitor
python3 -m venv venv
source venv/bin/activate
pip install web3
pip install web3 pulls in 38 packages including web3-7.16.0 and websockets-15.0.1. The final line confirms the install:
Successfully installed ... web3-7.16.0 websockets-15.0.1 ...
Replace the two endpoint placeholders with your actual Chainstack URLs:
cat > /root/eth-monitor/monitor.py << 'EOF'
import asyncio
from web3 import AsyncWeb3, WebSocketProvider
WSS_ENDPOINT = "wss://ethereum-mainnet.core.chainstack.com/<your-api-key>"
HTTPS_ENDPOINT = "https://ethereum-mainnet.core.chainstack.com/<your-api-key>"
THRESHOLD_ETH = 100
SAMPLE_EVERY = 20 # inspect 1 in 20 pending txs to stay within free plan RPS
counter = 0
async def handle_transaction(w3, tx_hash):
try:
tx = await w3.eth.get_transaction(tx_hash)
if tx and tx['value'] >= w3.to_wei(THRESHOLD_ETH, 'ether'):
eth_value = w3.from_wei(tx['value'], 'ether')
print(f"[ALERT] Large transfer: {eth_value:.2f} ETH | tx: {tx_hash.hex()} | from: {tx['from']} -> to: {tx['to']}", flush=True)
except Exception as e:
print(f"[ERROR] {e}", flush=True)
async def main():
global counter
print(f"[INFO] Connecting to Ethereum via Chainstack...", flush=True)
async with AsyncWeb3(WebSocketProvider(WSS_ENDPOINT)) as w3:
connected = await w3.is_connected()
print(f"[INFO] Connected: {connected}", flush=True)
print(f"[INFO] Watching for transfers >= {THRESHOLD_ETH} ETH (sampling 1 in {SAMPLE_EVERY} pending txs)...", flush=True)
await w3.eth.subscribe("newPendingTransactions")
print(f"[INFO] Subscribed to pending transactions", flush=True)
async for payload in w3.socket.process_subscriptions():
counter += 1
if counter % SAMPLE_EVERY != 0:
continue
tx_hash = payload['result']
asyncio.create_task(handle_transaction(w3, tx_hash))
if __name__ == "__main__":
asyncio.run(main())
EOF
SAMPLE_EVERY = 20 is the key parameter for the free plan. With roughly 200 to 300 pending transactions arriving per second on Ethereum mainnet, sampling one in twenty keeps your fetch rate around 10 to 15 RPS, well under the 25 RPS Developer plan cap. Increase THRESHOLD_ETH to raise the alert threshold; decrease SAMPLE_EVERY if you are on a paid plan with higher RPS limits.
The monitor makes outbound connections to Chainstack only, so no inbound ports need opening. UFW’s default outbound-allow policy covers it. Confirm SSH access is protected and the firewall is active:
ufw allow OpenSSH
ufw --force enable
ufw status verbose
The output confirms:
Status: active
Default: deny (incoming), allow (outgoing), disabled (routed)
To Action From
-- ------ ----
22/tcp (OpenSSH) ALLOW IN Anywhere
The service file runs the monitor as your user, restarts it on failure with a 10-second backoff, and sends all output to the system journal:
bash -c "cat > /etc/systemd/system/eth-monitor.service << 'EOF'
[Unit]
Description=Ethereum Large Transfer Monitor
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/root/eth-monitor
ExecStart=/root/eth-monitor/venv/bin/python /root/eth-monitor/monitor.py
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF"
systemctl daemon-reload
systemctl enable eth-monitor
systemctl start eth-monitor
Wait a few seconds, then verify:
systemctl status eth-monitor --no-pager
The output confirms the service is live:
● eth-monitor.service - Ethereum Large Transfer Monitor
Active: active (running) since Sun 2026-06-28 11:55:47 UTC; 8s ago
Main PID: 52171 (python)
Memory: 42.9M (peak: 43.2M)
Check the startup sequence in the journal:
journalctl -u eth-monitor -n 10 --no-pager
Jun 28 11:55:48 a240228623.local python[52171]: [INFO] Connecting to Ethereum via Chainstack...
Jun 28 11:55:48 a240228623.local python[52171]: [INFO] Connected: True
Jun 28 11:55:48 a240228623.local python[52171]: [INFO] Watching for transfers >= 100 ETH (sampling 1 in 20 pending txs)...
Jun 28 11:55:48 a240228623.local python[52171]: [INFO] Subscribed to pending transactions
Jun 28 11:55:50 a240228623.local python[52171]: [ERROR] Transaction with hash: '0x105799...' not found.
The not found errors are expected and harmless. Pending transactions can be dropped from the mempool before the fetch fires: replaced by a higher-fee version, cancelled by the sender, or evicted under load. The monitor skips them and continues. Only transactions that resolve with a value above your threshold produce an [ALERT] line.
The service starts automatically on reboot and restarts within 10 seconds if the script exits unexpectedly.
When a pending transaction matching your threshold appears, the journal logs it immediately:
[ALERT] Large transfer: 142.50 ETH | tx: 0xabc123... | from: 0xSender -> to: 0xReceiver
Follow alerts in real time with:
journalctl -u eth-monitor -f
Open ~/eth-monitor/monitor.py and edit the two constants at the top:
THRESHOLD_ETH = 100 # minimum ETH value to trigger an alert
SAMPLE_EVERY = 20 # sample 1 in N pending transactions
After editing, restart the service:
systemctl restart eth-monitor
On a Chainstack Growth plan (250 RPS), you can set SAMPLE_EVERY = 1 to inspect every pending transaction. On the free Developer plan, keep it at 20 or higher.
Chainstack supports 70+ chains. To monitor BNB Smart Chain transfers instead, deploy a BNB Smart Chain node in your Chainstack project, replace the WSS and HTTPS endpoint values in monitor.py, and restart the service. The script structure is chain-agnostic: newPendingTransactions and eth_getTransaction work identically across all EVM-compatible networks.
Before updating web3.py, back up the virtual environment’s package list:
pip freeze > /root/eth-monitor/requirements-backup.txt
is*hosting includes free weekly VPS backups on all plans. A targeted pip backup is faster to restore from than a full VPS snapshot. If a package update breaks the script, you can recreate the exact working environment with pip install -r requirements-backup.txt in minutes rather than waiting for a snapshot restore.
To update web3.py:
cd ~/eth-monitor
source venv/bin/activate
pip install --upgrade web3
systemctl restart eth-monitor
journalctl -u eth-monitor -n 10 --no-pager
Confirm the [INFO] Connected: True line appears in the journal after restart before closing the session.
Free weekly VPS backups on every plan — a full-server floor under your pip package list, so a broken web3.py update never takes the monitor down for long.
The install took about 4 minutes on a fresh Ubuntu 24.04.4 LTS instance on is*hosting’s Start VPS plan: apt pulled 63 packages in roughly 2 minutes, pip install web3 added 38 more packages in under a minute, and systemd brought the service up in under 5 seconds. At rest the monitor uses 42.9 MB of RAM against the Start VPS plan’s 2 GB, leaving the server free for anything else you want to run alongside it. Disk usage after the full install sits at 8.2 GB of the plan’s 30 GB SSD.
The service connects to Chainstack’s Ethereum Global Node over WSS, subscribes to the live pending transaction feed, and samples one in twenty hashes to stay within the free plan’s 25 RPS limit. Every matching transfer above 100 ETH is logged to the system journal and survives reboots and crashes without intervention.
From here you can extend the script to post alerts to Telegram or Discord using their respective webhooks, lower THRESHOLD_ETH to catch smaller transfers, or swap in a BNB Smart Chain or Base endpoint from your Chainstack project. The Start VPS plan at $10.19/month on is*hosting covers the compute; Chainstack’s free tier covers the node access.
For next steps on the server side, the Linux VPS setup guide covers SSH hardening and initial configuration, and the Uptime Kuma guide shows how to add uptime monitoring so you know immediately if the VPS itself goes offline.
The free Developer plan includes one Global Node endpoint, 3 million request units per month, and a 25-request-per-second cap. That is enough for scripts that sample the mempool rather than consuming every transaction hash. For higher-throughput workloads such as bots that need to inspect every pending transaction, indexers, or production DeFi backends, the Growth plan at $49/month raises the cap to 250 RPS and 20 million monthly request units.
The Ethereum mempool produces several hundred pending transaction hashes per second. At 25 RPS on the free plan, fetching every single transaction triggers error -32005 almost immediately. The fix is sampling: inspect one in every 20 pending transactions, which keeps your fetch rate well below the cap while still catching most large transfers within a few seconds of broadcast.
A self-managed Ethereum full node on dedicated hardware costs $80-$150/month in server fees alone, takes 2-4 TB of fast NVMe storage, and requires ongoing maintenance as the chain grows roughly 1 TB per year. Chainstack’s free tier costs nothing for low-frequency use cases. Even the Growth plan at $49/month is cheaper than the cheapest dedicated server that meets Ethereum’s storage requirements.
The case for running your own node is data sovereignty and zero rate limits. If your application makes millions of RPC calls per day or requires archive data going back to genesis, self-hosting eventually wins on cost. For monitoring scripts, bots, and development work, a managed endpoint on a VPS running your application logic is the practical combination: Chainstack handles the node, is*hosting handles the always-on compute.