run-morph-node is a repository designed to facilitate the deployment and management of Morph nodes using Docker. Morph is an innovative platform that enhances Ethereum Layer 2 scalability by combining optimistic rollups and zk technology, aiming to revolutionize consumer blockchain applications.
- Dockerized Deployment: Simplifies the process of setting up Morph nodes using Docker containers.
- Network Support: Provides configurations for both Mainnet and Hoodi testnet environments.
- Execution Client Choice: Run the node with either geth (
go-ethereum) or reth (morph-reth) as the execution client. - Snapshot Synchronization: Supports synchronizing node data from snapshots to expedite the setup process.
Before setting up a Morph node, ensure you have the following installed:
- Docker: Containerization platform to run the node.
- Docker Compose: Tool for defining and running multi-container Docker applications.
-
Clone the Repository:
git clone https://github.com/morph-l2/run-morph-node.git
-
Navigate to the Project Directory:
cd run-morph-node -
Configure Environment Variables:
Edit the
.envfile to set the appropriate values for your setup if needed. For instance, specify theMORPH_HOMEif you plan to user your specified directory as your node data home. By default, it takes./mainnetas the node data home for mainnet network. -
Download and Decompress Snapshot (Optional but Recommended):
-
To expedite synchronization, download the latest snapshot:
make download-and-decompress-mainnet-snapshot
-
For Hoodi testnet, use the corresponding command:
make download-and-decompress-hoodi-snapshot
-
After downloading the snapshot, you need to manually place the decompressed data files in the appropriate node data directories. Alternatively, use the
quickstart-*targets (e.g.make quickstart-mainnet-node) which handle snapshot download and placement automatically.-
make download-and-decompress-*now extracts snapshots under network directories (../mainnetor../hoodi). -
For example, if the snapshot folder is named
snapshot-20260415-1, move the directory to theMORPH_HOMEdirectories:mv ${MORPH_HOME}/snapshot-20260415-1/geth ${MORPH_HOME}/geth-data mv ${MORPH_HOME}/snapshot-20260415-1/data/* ${MORPH_HOME}/node-data/data -
The folder structure will be like
└── ${MORPH_HOME} ├── geth-data // data directory for geth │ └── geth // directory from snapshot/geth └── node-data // data directory for node ├── config │ ├── config.toml │ └── genesis.json └── data // data directory from snapshot/node -
Reth uses a different snapshot (the
*-reth-*rows in the snapshot table) with a different execution-client layout. Its tarball contains areth-data/tree plus the samedata/for the node.static-nodes.jsonis not in the snapshot; thequickstart-*-reth-*/setup-snapshot-data-rethhelpers copy it fromgeth-data/intoreth-data/for--trusted-peers. For example, if the reth snapshot folder is namedsnapshot-archive-reth-20260902-1, move the directories into theMORPH_HOMEdirectories:mv ${MORPH_HOME}/snapshot-archive-reth-20260902-1/reth-data/* ${MORPH_HOME}/reth-data mv ${MORPH_HOME}/snapshot-archive-reth-20260902-1/data/* ${MORPH_HOME}/node-data/dataThe folder structure will be like
└── ${MORPH_HOME} ├── reth-data // data directory for reth │ ├── db // from snapshot/reth-data │ ├── static_files // from snapshot/reth-data │ ├── rocksdb // from snapshot/reth-data │ ├── morph // from snapshot/reth-data │ └── static-nodes.json // copied from geth-data, parsed into --trusted-peers └── node-data // data directory for node ├── config │ ├── config.toml │ └── genesis.json └── data // data directory from snapshot/node
-
- Run the Node:
-
Start the node using Docker Compose:
make run-node
For Hoodi testnet, run
make run-hoodi-node
-
This command will set up and run the node based on the configurations specified in your .env file.
The node can run with reth (morph-reth) as its execution client instead of
geth. Reth uses a separate execution-client data directory (${MORPH_HOME}/reth-data)
and, importantly, a different snapshot than geth (see the Snapshot Information
table — reth snapshots are the *-reth-* rows). The morph-node (derivation) side
is identical; only the execution client and its snapshot differ.
-
Start a reth-backed node with Docker Compose:
make run-reth-node # mainnet make run-hoodi-reth-node # hoodi
Stop / remove:
make stop-reth-node make rm-reth-node
-
Or use a one-command quickstart (downloads the reth snapshot, places the data, and runs):
make quickstart-mainnet-reth-node # mainnet, Docker make quickstart-hoodi-reth-node # hoodi, Docker
-
Binary mode (build
morph-rethfrom source withcargo, then run the binaries directly):make build-reth-all # builds morph-reth + morphnode into ./bin make run-reth-node-binary # mainnet make run-hoodi-reth-node-binary # hoodi make stop-binary # stops geth/reth/morphnode # or one-command: make quickstart-mainnet-reth-node-binary / quickstart-hoodi-reth-node-binary
build-rethclonesmorph-rethinto../morph-rethand checks out a pinned tagv$(RETH_VERSION)(default1.3.0, kept in sync with the image indocker-compose.reth.yml) so the binary and Docker paths run the same version. Bump it in one place withmake set-versions RETH_VERSION=1.4.0 ...(rewrites the compose image tag), or build a one-off withmake build-reth RETH_VERSION=1.4.0.
morph-reth reads the boot nodes from static-nodes.json and passes them to
--trusted-peers (discovery is disabled). The snapshot does not contain
static-nodes.json, so the snapshot setup copies it from ${MORPH_HOME}/geth-data/static-nodes.json
into ${MORPH_HOME}/reth-data/. The reth launch command used by the entrypoint is:
morph-reth node \
--chain <mainnet|hoodi> \
--datadir $RETH_DATA_DIR \
--http --http.addr 0.0.0.0 --http.port 8545 --http.corsdomain '*' \
--http.api web3,eth,txpool,net,trace \
--ws --ws.addr 0.0.0.0 --ws.port 8546 --ws.origins '*' \
--ws.api web3,eth,txpool,net,trace \
--authrpc.addr 0.0.0.0 --authrpc.port 8551 --authrpc.jwtsecret /jwt-secret.txt \
--disable-discovery --nat none \
--metrics 0.0.0.0:6060 \
--log.file.directory $LOG_DIR --log.file.filter info \
--trusted-peers $TRUSTED_PEERS # parsed from static-nodes.jsonThere is no separate validator service anymore — every node self-verifies L1 batches. The
mode is controlled by DERIVATION_VERIFY_MODE (--derivation.verify-mode) in the env file:
local(default): rebuild the blob from local L2 blocks and compare versioned hashes against L1. No beacon fetch on the happy path.layer1: pull the L1 beacon blob, decode it, and derive via the engine — equivalent to the former validator node.
L1_BEACON_CHAIN_RPC is required in both modes — the node exits at startup without it.
local just doesn't fetch the blob on the happy path; layer1 fetches it every batch.
layer1 is what the former validator node did. Two ways to enable it:
- Validator commands (kept for backward compatibility):
make run-validator/make run-hoodi-validator(or the-binaryvariants) run the single node inlayer1mode. Stop/remove withmake stop-validator/make rm-validator(aliases ofmake stop-node/make rm-node). - Env var: set
DERIVATION_VERIFY_MODE=layer1in.env/.env_hoodi, thenmake run-node.
There is no separate validator container anymore — both paths run the same morph-node.
L1_SEQUENCER_CONTRACTandCONSENSUS_SWITCH_HEIGHTare hardcoded per-network defaults in the binary and must not be set as operator config.
The table below provides the node snapshot data and corresponding download URLs. Ensure DERIVATION_START_HEIGHT, L1_MSG_START_HEIGHT, and L2_BASE_HEIGHT in .env/.env_hoodi match the selected snapshot.
geth vs reth snapshots: rows whose name contains
reth(e.g.snapshot-archive-reth-*) are for the reth execution client and must be used with the*-reth-*make targets; the other rows are geth snapshots. The two are not interchangeable. SetMAINNET_SNAPSHOT_NAME/HOODI_SNAPSHOT_NAMEfor geth andMAINNET_RETH_SNAPSHOT_NAME/HOODI_RETH_SNAPSHOT_NAMEfor reth in the env files.Because the two clients use different snapshots, the height variables are also split: the geth path uses
DERIVATION_START_HEIGHT/L1_MSG_START_HEIGHT/L2_BASE_HEIGHT, and the reth path usesRETH_DERIVATION_START_HEIGHT/RETH_L1_MSG_START_HEIGHT/RETH_L2_BASE_HEIGHT. Ensure each set matches the height row of its selected snapshot.
For mainnet (reth is currently in an internal testing phase and is not yet recommended for production use):
| Snapshot Name | Derivation Start Height | L1 Msg Start Height | L2 Base Height |
|---|---|---|---|
| snapshot-archive-20260902-1 | 25885664 | 25880870 | 26137782 |
| snapshot-20260902-1 | 25885664 | 25880870 | 26137782 |
| snapshot-archive-reth-20260902-1 | 25885664 | 25880870 | 26137782 |
| snapshot-archive-20260817-1 | 25770865 | 25751519 | 25380612 |
| snapshot-20260817-1 | 25770865 | 25751519 | 25380612 |
| snapshot-archive-reth-20260817-1 | 25770865 | 25751519 | 25380612 |
| snapshot-archive-20260803-1 | 25671158 | 25658857 | 24979932 |
| snapshot-20260803-1 | 25671158 | 25658857 | 24980065 |
| snapshot-archive-reth-20260803-1 | 25671367 | 25658857 | 24980107 |
For hoodi testnet (reth is currently in an internal testing phase and is not yet recommended for production use):
| Snapshot Name | Derivation Start Height | L1 Msg Start Height | L2 Base Height |
|---|---|---|---|
| snapshot-archive-20260915-1 | 3594839 | 3593802 | 8761644 |
| snapshot-20260915-1 | 3594839 | 3593802 | 8761644 |
| snapshot-archive-reth-20260915-1 | 3625153 | 3621708 | 8974058 |
| snapshot-archive-20260901-1 | 3533250 | 3529538 | 8346379 |
| snapshot-20260901-1 | 3533250 | 3529538 | 8346379 |
| snapshot-archive-reth-20260901-1 | 3533250 | 3529538 | 8346700 |
| snapshot-archive-20260815-1 | 3420448 | 3413824 | 7593270 |
| snapshot-archive-reth-20260815-1 | 3420448 | 3413824 | 7593270 |
| snapshot-archive-20260801-1 | 3327231 | 3316267 | 7223425 |
Each snapshot has a SHA-256 checksum file at the same URL with a .sha256
suffix. Compute the hash and compare it against the .sha256 file — use
whichever command is available on your system:
shasum -a 256 snapshot.tar.gz
# or
openssl dgst -sha256 snapshot.tar.gzFor detailed information on Morph and its ecosystem, refer to the official documentation:
By following these steps, you can set up and run a Morph node efficiently using Docker. For any questions or support, please refer to the official Morph community channels.