Exchange node in four lines (Hive 1.28.8)

Words
1062
Reading
5 min
3h
Listen to this post
~7 min

How many commands does it take to run a Hive node for an exchange?

Four. One of them is cd, and the last one is for watching the logs while you drink your coffee ;-)

Remember Hive Node Setup for the Smart, the Dumb, and the Lazy? This one is for the lazy.

No scripts, one compose file

We were asked for a "cloud-native" way to run a node: no helper scripts, just one declarative file. Hive 1.28.8 has exactly that: docker/exchange, a Docker Compose setup for an exchange node.

I serve a copy of it at https://gtg.openhive.network/get/exchange/ - slightly modified from upstream: it tracks the accounts I chose to support, so it matches my exchange snapshot, and it keeps the block log and the snapshot in plain folders next to the compose file.

Cyberpunk-Hive-chan.pngCyberpunk Hive chan, generated by AI during HiveFest 2026

What's in the box

hive-exchange-1.28.8
├── .env          <- your settings: ports, BLOCK_LOG_DIR, SNAPSHOT_URL
├── compose.yml   <- the node itself, nothing to edit
├── config.ini    <- hived options and the tracked accounts
├── README.md
├── blockchain    <- your block_log, or block_log_part.* files
└── snapshot      <- the unpacked snapshot (downloaded here if empty)

You have a block log? If you had a Hive node running recently, you probably do. It's preferred and recommended to re-use it. Blocks never change. That's the whole point. Put it into blockchain: one big block_log file or a set of block_log_part.* files, both work. Or keep it where it is and point BLOCK_LOG_DIR in .env to it.

No block log? Leave blockchain empty. The node then runs pruned (see below).

The snapshot takes care of itself: if snapshot is empty, the node downloads my exchange snapshot (6.3 GB) into it. Already have it unpacked? Put it there.

The four lines

wget -qO- https://gtg.openhive.network/get/exchange/hive-exchange-1.28.8.tgz | tar xz
cd hive-exchange-1.28.8
docker compose up -d
docker compose logs -f hived

Unpack it on the disk where the block log is (or will be). If your block log stays where it is, add one line before docker compose up -d:

echo BLOCK_LOG_DIR=/path/to/your/blockchain >> .env

Four lines Five lines. The coffee is still the last one.

The node loads the snapshot, replays your block log from the snapshot's block on, and syncs the rest from the network. Without a block log, it does the same minus the replay. Minutes, not even hours.

When the log says entering live mode, press Ctrl+C. That stops the log, not the node. docker compose ps shows the node as healthy once it has caught up.

Exchange node in five linesThe real run with a block log, sped up

The log stopped at the same block for minutes?

Have you tried turning it off and on again?
- Roy, The IT Crowd

docker compose down && docker compose up -d

Your block log, three rules

  • Stop your old node first.
  • hived in the container runs as UID 1000, so it has to be able to write there: sudo chown -R 1000 blockchain (or your BLOCK_LOG_DIR).
  • Your block log has to be at least as new as the snapshot: it must contain the block the snapshot was made at. A node that was in sync until you stopped it is fine. An older one makes hived stop with "Chain state ... does not match block log", over and over.

All blocks, or pruned?

With a block log, the node has all blocks: either in one big file (block-log-split = -1) or in parts of a million blocks each (block-log-split = 9999, the hived default). config.ini says -1, and when the bundle finds block_log_part.* files it switches to 9999 for you. Want parts instead of one big file? Set block-log-split = 9999 in config.ini and hived splits it on the next start. It keeps the original file, so you need the space twice until you delete it.

Without a block log, the node is pruned: it runs with block-log-split = 0 and keeps only the newest block. State is complete, and so is the account history of the accounts on the list. That is enough to follow your deposits. But get_block and get_transaction need all blocks.

No block log of your own, but you want all blocks? Set this in .env before the first start:

BLOCK_LOG_URL=https://gtg.openhive.network/get/blockchain/block_log

The node then downloads the block log into blockchain, loads the snapshot, and replays only the blocks made after the snapshot.

Is your account on the list?

The snapshot has account history only for the accounts listed in config.ini (the account-history-rocksdb-track-account-range lines).

On the list? You are done. Keep the list as it is.

Not on the list? Then the snapshot cannot give you its history. Before the first start, add your account to config.ini and clear SNAPSHOT_URL in .env. With a block log the node replays all of it, with history for your account from the first block. That takes hours, not minutes. Without one, it syncs the whole chain from the P2P network, which takes even longer.

Running an exchange that people actually use, and your account isn't on the list? Let me know, with a good reason, and it can be in the next snapshot.

SNAPSHOT_URL points to latest-1.28.8.tgz, which is always my newest snapshot made with 1.28.8.

The block log rule

Never download the block log twice. Monolithic or split, keep it and reuse it for every replay and every upgrade. It is 587 GB today, and it does not get any smaller. That is what the blockchain folder is for.

docker compose down -v removes the volumes, that is the state and the account history. The blockchain and snapshot folders stay. Plain docker compose down keeps everything.

Bonus: asking the node

The API listens on 127.0.0.1:8091 only. Keep it off the public internet.

Where is the chain?

curl -s --data '{"jsonrpc":"2.0","method":"database_api.get_dynamic_global_properties","id":1}' http://127.0.0.1:8091/

Look for head_block_number and last_irreversible_block_num.

The last 10 operations of an account. Let's take binance-hot2, one of Binance's accounts on the list:

curl -s --data '{"jsonrpc":"2.0","method":"account_history_api.get_account_history","params":{"account":"binance-hot2","start":-1,"limit":10},"id":1}' http://127.0.0.1:8091/

Each entry has the operation, its block and its trx_id. By default you get only operations from irreversible blocks, so everything you see there is final. This one works on a pruned node too.

Did a transaction make it? Ask with its trx_id:

curl -s --data '{"jsonrpc":"2.0","method":"account_history_api.get_transaction","params":{"id":"TRX_ID"},"id":1}' http://127.0.0.1:8091/

If you get the transaction back, it is in an irreversible block. Until then the answer is "Unknown Transaction", and thanks to OBI that usually lasts only seconds. It needs all blocks, and it knows the transactions of the accounts on the list.

And a block:

curl -s --data '{"jsonrpc":"2.0","method":"block_api.get_block","params":{"block_num":BLOCK_NUM},"id":1}' http://127.0.0.1:8091/

That needs all blocks too. A pruned node answers "has been pruned" for everything but the newest one.

For deposits, account history is the better tool anyway: it also shows virtual operations, like recurrent transfers or transfers from savings, which you will not find in any block.

It's an exchange node, but other nodes run almost the same way: a seed node, a witness node, a personal wallet node, a broadcaster, or a basic API node for your simple bots. Same compose file, a different config.ini, and most likely the same snapshot. hived only warns that the snapshot has more plugins than it needs. Seed and API nodes need all the blocks, though, so bring your block log.

Exchange node in four lines (Hive 1.28.8) | Ecency