Skip to main content

Overview

ZeroMQ (ZMQ) is a lightweight messaging library that enables Bitcoin Core to publish real-time notifications about blocks and transactions. This provides a push-based alternative to polling RPC methods for monitoring blockchain events.
ZMQ is a read-only notification interface. It requires no authentication and implements no two-way protocol. Subscribers should validate received data.

What is ZeroMQ?

ZeroMQ provides:
  • Message-oriented semantics: Publish/subscribe, request/reply, push/pull
  • Self-connecting sockets: Automatic connection recovery
  • Self-healing: Resilient to endpoint failures
  • No buffering needed: All-at-once message delivery
  • Multiple transports: TCP, IPC, inproc, Unix sockets

Key Features

  • Real-time notifications: Instant push when events occur
  • Multiple subscribers: Many clients can listen simultaneously
  • Lightweight: Minimal overhead on Bitcoin Core
  • Language agnostic: Works with any ZMQ client library
  • Resilient: Either end can start/stop independently

Prerequisites

1

Install ZeroMQ Library

Debian/Ubuntu:
sudo apt install libzmq3-dev
macOS:
brew install zeromq
Note: You only need libzmq, not the C++ wrapper.
2

Build Bitcoin Core with ZMQ Support

cmake -B build -DWITH_ZMQ=ON
cmake --build build
3

Install Python ZMQ (Optional)

For example client scripts:
pip install pyzmq
ZMQ support requires libzmq version 4.0.0 or higher. Check your package manager for the exact version.

Notification Types

Bitcoin Core supports five ZMQ notification topics:

Block Notifications

TopicDescriptionBody Content
hashblockBlock hash notification32-byte reversed block hash
rawblockFull block notificationSerialized block data

Transaction Notifications

TopicDescriptionBody Content
hashtxTransaction hash notification32-byte reversed tx hash
rawtxFull transaction notificationSerialized transaction data

Sequence Notifications

TopicDescriptionBody Content
sequenceMempool/block sequenceHash + event type + sequence

Configuration Options

Basic Configuration

# Publish block hashes
-zmqpubhashblock=tcp://127.0.0.1:28332

# Publish raw blocks
-zmqpubrawblock=tcp://127.0.0.1:28332

# Publish transaction hashes
-zmqpubhashtx=tcp://127.0.0.1:28333

# Publish raw transactions
-zmqpubrawtx=tcp://127.0.0.1:28333

# Publish sequence events
-zmqpubsequence=tcp://127.0.0.1:28334

Multiple Endpoints

You can publish the same notification to multiple addresses:
bitcoind -zmqpubhashtx=tcp://127.0.0.1:28332 \
         -zmqpubhashtx=tcp://192.168.1.2:28332 \
         -zmqpubhashblock="tcp://[::1]:28333"

High Water Mark (HWM)

Control the message queue size for each notification:
# Set HWM for hashtx (default: 1000)
-zmqpubhashtxhwm=10000

# Set HWM for other topics
-zmqpubhashblockhwm=1000
-zmqpubrawblockhwm=1000  
-zmqpubrawtxhwm=1000
-zmqpubsequencehwm=1000
The high water mark is the maximum number of messages to queue before dropping. Higher values use more memory but prevent message loss during bursts.

Transport Types

TCP Sockets

# IPv4
-zmqpubhashtx=tcp://127.0.0.1:28332

# IPv6  
-zmqpubhashtx=tcp://[::1]:28332

# All interfaces
-zmqpubhashtx=tcp://*:28332

Unix Domain Sockets

-zmqpubrawtx=unix:/tmp/bitcoind.tx.raw

IPC Sockets

-zmqpubrawblock=ipc:///tmp/bitcoind.rawblock

Message Format

All ZMQ messages have three parts:
[topic] [body] [sequence_number]

Message Structure

PartTypeDescription
TopicStringMessage topic (e.g., “hashtx”, “rawblock”)
BodyBinaryMessage payload (varies by topic)
Sequenceuint32Message counter (little-endian)

Topic-Specific Body Formats

rawtx:
[topic: "rawtx"] [serialized transaction] [4-byte LE sequence]
hashtx:
[topic: "hashtx"] [32-byte reversed tx hash] [4-byte LE sequence]
rawblock:
[topic: "rawblock"] [serialized block] [4-byte LE sequence]
hashblock:
[topic: "hashblock"] [32-byte reversed block hash] [4-byte LE sequence]
sequence:
[topic: "sequence"] [32-byte hash + event type + 8-byte data] [4-byte LE sequence]
All hashes are in reversed byte order (same format as RPC and block explorers), not internal byte order.

Sequence Notifications

The sequence topic provides ordered mempool and block events:

Event Types

TypeEventBody Format
CBlock connected[32-byte block hash]C
DBlock disconnected[32-byte block hash]D
RTx removed from mempool[32-byte tx hash]R[8-byte sequence]
ATx added to mempool[32-byte tx hash]A[8-byte sequence]

Sequence Number

Each topic maintains its own sequence counter:
  • Increments with each message
  • Allows detection of lost messages
  • Independent per topic
  • Wraps at 2^32
Sequence numbers are topic-specific. A gap in sequence numbers indicates lost messages for that topic.

Example Configurations

Monitor New Transactions

bitcoin.conf
# Get notified of new transactions
zmqpubhashtx=tcp://127.0.0.1:28332
zmqpubhashtxhwm=10000

Monitor New Blocks

bitcoin.conf
# Get notified of new blocks  
zmqpubhashblock=tcp://127.0.0.1:28333
zmqpubrawblock=tcp://127.0.0.1:28333

Full Monitoring Setup

bitcoin.conf
# All notification types on different ports
zmqpubhashtx=tcp://127.0.0.1:28332
zmqpubrawtx=tcp://127.0.0.1:28333
zmqpubhashblock=tcp://127.0.0.1:28334
zmqpubrawblock=tcp://127.0.0.1:28335
zmqpubsequence=tcp://127.0.0.1:28336

# Set high water marks
zmqpubhashtxhwm=10000
zmqpubrawtxhwm=10000
zmqpubhashblockhwm=1000
zmqpubrawblockhwm=100
zmqpubsequencehwm=10000

Unix Sockets for Local Apps

bitcoin.conf
# Use Unix sockets for local applications
zmqpubhashtx=unix:/tmp/bitcoind.hashtx
zmqpubrawtx=unix:/tmp/bitcoind.rawtx  
zmqpubhashblock=unix:/tmp/bitcoind.hashblock
zmqpubrawblock=unix:/tmp/bitcoind.rawblock

Client Implementation

Python Example

Bitcoin Core includes example Python clients in contrib/zmq/:
#!/usr/bin/env python3
import zmq

# Create ZMQ context and socket
ctx = zmq.Context()
sock = ctx.socket(zmq.SUB)

# Connect to Bitcoin Core
sock.connect("tcp://127.0.0.1:28332")

# Subscribe to topics
sock.setsockopt(zmq.SUBSCRIBE, b"hashtx")
sock.setsockopt(zmq.SUBSCRIBE, b"hashblock")

while True:
    # Receive multipart message
    topic, body, seq = sock.recv_multipart()
    
    sequence = int.from_bytes(seq, 'little')
    
    if topic == b"hashtx":
        tx_hash = body.hex()
        print(f"New TX: {tx_hash} (seq: {sequence})")
    
    elif topic == b"hashblock":
        block_hash = body.hex()
        print(f"New Block: {block_hash} (seq: {sequence})")
See contrib/zmq/zmq_sub.py in the Bitcoin Core repository for a complete working example.

Subscribe to Specific Topics

# Subscribe to all messages (not recommended)
sock.setsockopt(zmq.SUBSCRIBE, b"")

# Subscribe to specific topics
sock.setsockopt(zmq.SUBSCRIBE, b"hashtx")
sock.setsockopt(zmq.SUBSCRIBE, b"hashblock")

# Subscribe to hash messages only
sock.setsockopt(zmq.SUBSCRIBE, b"hash")
You must set ZMQ_SUBSCRIBE option or no messages will be received. Subscribing to b"" receives all topics.

Advanced Features

TCP Keepalive

ZMQ_TCP_KEEPALIVE is automatically enabled:
# Linux: Adjust TCP keepalive timing
sudo sysctl -w net.ipv4.tcp_keepalive_time=600
This prevents silent connection drops by network middleboxes.

IPv6 Support

ZMQ_IPV6 is enabled by default:
# IPv6 endpoint
-zmqpubhashtx=tcp://[::1]:28332

# Dual-stack (IPv4 and IPv6)
-zmqpubhashtx=tcp://*:28332

Lost Message Detection

Detect lost messages using sequence numbers:
last_seq = {}

while True:
    topic, body, seq = sock.recv_multipart()
    sequence = int.from_bytes(seq, 'little')
    
    if topic in last_seq:
        expected = (last_seq[topic] + 1) % (2**32)
        if sequence != expected:
            lost = (sequence - expected) % (2**32)
            print(f"Lost {lost} messages on {topic}")
    
    last_seq[topic] = sequence

Behavior and Limitations

Transaction Notifications

Transactions are notified multiple times:
  1. When added to mempool
  2. When included in a block
  3. In subsequent blocks if reorganizations occur
Track transaction state separately if you need to handle each only once.

Block Notifications

Block notifications occur when:
  • New block extends the active chain tip
  • Block reorganization changes the tip
During reorganizations, only the new tip is notified. Subscribers must track the chain themselves to detect and handle reorgs.
Notifications are NOT sent when:
  • invalidateblock RPC is called
  • Historical blocks are connected during sync (with assumeutxo)

Sequence Topic Behavior

The sequence topic publishes all block connections and disconnections, unlike hashblock. This enables:
  • Complete reorg detection
  • Mempool state tracking
  • Ordered event processing

Security Considerations

Important Security Notes:
  1. No authentication: Anyone who can connect can subscribe
  2. No authorization: No access control on connections
  3. Read-only: Subscribers cannot send commands
  4. No validation: Subscribers must validate all received data
  5. Trusted network only: Expose ZMQ ports only to trusted clients

Best Practices

Security Best Practices:
  • Use Unix sockets for local applications
  • Bind TCP sockets to localhost (127.0.0.1) only
  • Use firewall rules to restrict access
  • Validate all received data before use
  • Monitor for gaps in sequence numbers
  • Don’t expose ZMQ ports to the Internet

Firewall Configuration

# Allow only local connections
bitcoind -zmqpubhashtx=tcp://127.0.0.1:28332

# Allow specific subnet
# Use firewall to restrict:
sudo ufw allow from 192.168.1.0/24 to any port 28332

Troubleshooting

ZMQ Not Compiled In

Error: Invalid argument "-zmqpubhashtx"
Solution: Rebuild with ZMQ support:
cmake -B build -DWITH_ZMQ=ON
cmake --build build

No Messages Received

Check subscription:
# Must subscribe to topics!
sock.setsockopt(zmq.SUBSCRIBE, b"hashtx")
Check connection:
import zmq
ctx = zmq.Context()
sock = ctx.socket(zmq.SUB)  
sock.connect("tcp://127.0.0.1:28332")  # Correct port?
sock.setsockopt(zmq.SUBSCRIBE, b"")   # Subscribe to all

Port Already in Use

Error: Address already in use
Solution: Check if port is in use:
netstat -tlnp | grep 28332
Use a different port or stop the conflicting service.

Connection Refused

Check Bitcoin Core is running:
bitcoin-cli getblockcount
Verify ZMQ is configured:
ps aux | grep bitcoind | grep zmq

Performance Tuning

High Water Mark Sizing

# Low traffic: Small HWM
-zmqpubhashblockhwm=100

# High traffic: Large HWM  
-zmqpubhashtxhwm=100000
Set HWM based on expected message rate and acceptable memory usage:
  • Blocks: ~100-1000 (low rate)
  • Transactions: 10000-100000 (high rate)
  • Sequence: 10000+ (tracks everything)

Transport Selection

TransportUse CasePerformance
Unix socketLocal apps, same machineFastest
TCP localhostLocal apps, multiple languagesFast
TCP networkRemote subscribersNetwork dependent
IPCInter-process communicationVery fast

Use Cases

Real-Time Block Explorer

bitcoin.conf
zmqpubhashblock=tcp://127.0.0.1:28332
zmqpubrawblock=tcp://127.0.0.1:28332  
zmqpubhashtx=tcp://127.0.0.1:28332
zmqpubrawtx=tcp://127.0.0.1:28332

Payment Processing

bitcoin.conf
# Monitor specific transactions
zmqpubhashtx=tcp://127.0.0.1:28332
zmqpubrawblock=tcp://127.0.0.1:28333
zmqpubsequence=tcp://127.0.0.1:28334

Lightning Network Node

bitcoin.conf
# Real-time block notifications
zmqpubrawblock=tcp://127.0.0.1:28332
zmqpubrawtx=tcp://127.0.0.1:28333

Analytics Platform

bitcoin.conf
# All events with sequence tracking
zmqpubsequence=tcp://127.0.0.1:28332
zmqpubrawblock=tcp://127.0.0.1:28333
zmqpubrawtx=tcp://127.0.0.1:28334  
zmqpubsequencehwm=100000

See Also