Skip to content

Commit 4c99426

Browse files
committed
include reth backup
1 parent f176ff1 commit 4c99426

4 files changed

Lines changed: 697 additions & 0 deletions

File tree

scripts/reth-backup/Dockerfile

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
FROM ghcr.io/evstack/ev-reth:latest
2+
3+
ARG LIBMDBX_REPO=https://github.com/erthink/libmdbx.git
4+
ARG LIBMDBX_REF=master
5+
6+
RUN set -eux; \
7+
apt-get update; \
8+
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
9+
build-essential \
10+
ca-certificates \
11+
cmake \
12+
git \
13+
jq \
14+
; \
15+
rm -rf /var/lib/apt/lists/*
16+
17+
RUN set -eux; \
18+
git clone --depth 1 --branch "${LIBMDBX_REF}" "${LIBMDBX_REPO}" /tmp/libmdbx; \
19+
cmake -S /tmp/libmdbx -B /tmp/libmdbx/build -DCMAKE_BUILD_TYPE=Release; \
20+
cmake --build /tmp/libmdbx/build --target mdbx_copy mdbx_dump mdbx_chk; \
21+
install -m 0755 /tmp/libmdbx/build/mdbx_copy /usr/local/bin/mdbx_copy; \
22+
install -m 0755 /tmp/libmdbx/build/mdbx_dump /usr/local/bin/mdbx_dump; \
23+
install -m 0755 /tmp/libmdbx/build/mdbx_chk /usr/local/bin/mdbx_chk; \
24+
rm -rf /tmp/libmdbx

scripts/reth-backup/README.md

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
# Reth Backup Helper
2+
3+
Script to snapshot the `ev-reth` MDBX database while the node keeps running and
4+
record the block height contained in the snapshot.
5+
6+
The script supports two execution modes:
7+
8+
- **local**: Backup a reth instance running directly on the host machine
9+
- **docker**: Backup a reth instance running in a Docker container
10+
11+
## Prerequisites
12+
13+
### Common requirements
14+
15+
- The `mdbx_copy` binary available in the target environment (see [libmdbx
16+
documentation](https://libmdbx.dqdkfa.ru/)).
17+
- `jq` installed on the host to parse the JSON output.
18+
19+
### Docker mode
20+
21+
- Docker access to the container running `ev-reth` (defaults to the service name
22+
`ev-reth` from `docker-compose`).
23+
24+
### Local mode
25+
26+
- Direct filesystem access to the reth datadir.
27+
- Sufficient permissions to read the database files.
28+
29+
## Usage
30+
31+
### Local mode
32+
33+
When reth is running directly on your machine:
34+
35+
```bash
36+
./scripts/reth-backup/backup.sh \
37+
--mode local \
38+
--datadir /var/lib/reth \
39+
--mdbx-copy /usr/local/bin/mdbx_copy \
40+
/path/to/backups
41+
```
42+
43+
### Docker mode
44+
45+
When reth is running in a Docker container:
46+
47+
```bash
48+
./scripts/reth-backup/backup.sh \
49+
--mode docker \
50+
--container ev-reth \
51+
--datadir /home/reth/eth-home \
52+
--mdbx-copy /tmp/libmdbx/build/mdbx_copy \
53+
/path/to/backups
54+
```
55+
56+
### Output structure
57+
58+
Both modes create a timestamped folder under `/path/to/backups` with:
59+
60+
- `db/mdbx.dat` – consistent MDBX snapshot.
61+
- `db/mdbx.lck` – placeholder lock file (empty).
62+
- `static_files/` – static files copied from the node.
63+
- `stage_checkpoints.json` – raw StageCheckpoints table.
64+
- `height.txt` – extracted block height (from the `Finish` stage).
65+
66+
Additional flags:
67+
68+
- `--tag LABEL` to override the timestamped folder name.
69+
- `--keep-remote` to leave the temporary snapshot in the target environment
70+
(useful for debugging).
71+
72+
The script outputs the height at the end so you can coordinate other backups
73+
with the same block number.
74+
75+
## Architecture
76+
77+
The backup script is split into two components:
78+
79+
- **`backup-lib.sh`**: Abstract execution layer providing a common interface for
80+
different execution modes (local, docker). This library defines functions like
81+
`exec_remote`, `copy_from_remote`, `copy_to_remote`, and `cleanup_remote`
82+
that are implemented differently for each backend.
83+
- **`backup.sh`**: Main script that uses the library and orchestrates the backup
84+
workflow. It's mode-agnostic and works with any backend that implements the
85+
required interface.
86+
87+
This separation allows easy extension to support additional execution
88+
environments (SSH, Kubernetes, etc.) without modifying the core backup logic.
89+
90+
## End-to-end workflow with `apps/evm/single` (Docker mode)
91+
92+
### Prerequisites
93+
94+
1. Build the reth image with MDBX tooling:
95+
96+
```bash
97+
docker build -t ghcr.io/evstack/ev-reth:latest scripts/reth-backup
98+
```
99+
100+
2. Build the ev-node image with backup/restore commands:
101+
102+
```bash
103+
docker build -t ghcr.io/evstack/ev-node-evm-single:main -f apps/evm/single/Dockerfile .
104+
```
105+
106+
3. Start the stack:
107+
108+
```bash
109+
cd apps/evm/single && docker compose up -d
110+
```
111+
112+
### Backup
113+
114+
1. Backup reth (captures MDBX snapshot at current height):
115+
116+
```bash
117+
./scripts/reth-backup/backup.sh --mode docker backups/full-run/reth
118+
```
119+
120+
Note the printed TAG (e.g., `20251013-104816`) and height.
121+
122+
2. Backup ev-node (captures complete Badger datastore):
123+
124+
```bash
125+
TAG=<TAG> # from previous step
126+
HEIGHT=$(cat backups/full-run/reth/${TAG}/height.txt)
127+
128+
mkdir -p backups/full-run/ev-node
129+
130+
docker exec evolveevm-ev-node-evm-single-1 \
131+
evm-single backup \
132+
--output /tmp/backup-${TAG}.badger \
133+
--force
134+
135+
docker cp evolveevm-ev-node-evm-single-1:/tmp/backup-${TAG}.badger \
136+
backups/full-run/ev-node/
137+
138+
echo ${HEIGHT} > backups/full-run/ev-node/target-height.txt
139+
```
140+
141+
### Restore
142+
143+
1. Stop services and recreate containers:
144+
145+
```bash
146+
cd apps/evm/single
147+
docker compose down
148+
docker compose up --no-start
149+
```
150+
151+
2. Restore reth volume:
152+
153+
```bash
154+
TAG=<TAG>
155+
156+
# From apps/evm/single directory, use relative path to backups
157+
docker run --rm \
158+
--volumes-from ev-reth \
159+
-v "$PWD/../../backups/full-run/reth/${TAG}:/backup:ro" \
160+
alpine:3.18 \
161+
sh -c 'rm -rf /home/reth/eth-home/db /home/reth/eth-home/static_files && \
162+
mkdir -p /home/reth/eth-home/db /home/reth/eth-home/static_files && \
163+
cp /backup/db/mdbx.dat /home/reth/eth-home/db/ && \
164+
cp /backup/db/mdbx.lck /home/reth/eth-home/db/ && \
165+
cp -a /backup/static_files/. /home/reth/eth-home/static_files/ || true'
166+
```
167+
168+
3. Restore ev-node volume:
169+
170+
```bash
171+
TAG=<TAG>
172+
173+
# From apps/evm/single directory, use relative path to backups
174+
docker run --rm \
175+
--volumes-from evolveevm-ev-node-evm-single-1 \
176+
-v "$PWD/../../backups/full-run/ev-node:/backup:ro" \
177+
ghcr.io/evstack/ev-node-evm-single:main \
178+
restore \
179+
--input /backup/backup-${TAG}.badger \
180+
--home /root/.evm-single \
181+
--app-name evm-single \
182+
--force
183+
```
184+
185+
4. Align ev-node to reth height using rollback (before starting):
186+
187+
```bash
188+
HEIGHT=$(cat backups/full-run/ev-node/target-height.txt)
189+
190+
docker run --rm \
191+
--volumes-from evolveevm-ev-node-evm-single-1 \
192+
ghcr.io/evstack/ev-node-evm-single:main \
193+
rollback \
194+
--home /root/.evm-single \
195+
--height ${HEIGHT} \
196+
--sync-node
197+
```
198+
199+
> **Note:** The rollback may report errors for p2p header/data stores with invalid
200+
> ranges. This is expected and can be ignored. The main state will be correctly
201+
> rolled back to the target height. The `--sync-node` flag is required for
202+
> non-aggregator mode rollback.
203+
204+
5. Start reth and local-da services:
205+
206+
```bash
207+
docker compose start ev-reth local-da
208+
```
209+
210+
6. Start ev-node with cache cleared (first time only):
211+
212+
```bash
213+
# Remove the stopped container and start with --evnode.clear_cache
214+
docker rm evolveevm-ev-node-evm-single-1
215+
216+
docker run -d \
217+
--name evolveevm-ev-node-evm-single-1 \
218+
--network evolveevm_evolve-network \
219+
-p 7676:7676 -p 7331:7331 \
220+
-v evolveevm_evm-single-data:/root/.evm-single/ \
221+
-e EVM_ENGINE_URL=http://ev-reth:8551 \
222+
-e EVM_ETH_URL=http://ev-reth:8545 \
223+
-e EVM_JWT_SECRET=f747494bb0fb338a0d71f5f9fe5b5034c17cc988c229b59fd71e005ee692e9bf \
224+
-e EVM_GENESIS_HASH=0x2b8bbb1ea1e04f9c9809b4b278a8687806edc061a356c7dbc491930d8e922503 \
225+
-e EVM_BLOCK_TIME=1s \
226+
-e EVM_SIGNER_PASSPHRASE=secret \
227+
-e DA_ADDRESS=http://local-da:7980 \
228+
ghcr.io/evstack/ev-node-evm-single:main \
229+
start --evnode.clear_cache
230+
```
231+
232+
> **Important:** Use `--evnode.clear_cache` on first start after restore to clear
233+
> any cached p2p data that may be inconsistent after rollback. On subsequent restarts,
234+
> you can use `docker compose up -d` normally.
235+
236+
7. Verify both nodes are at the same height:
237+
238+
```bash
239+
HEIGHT=$(cat backups/full-run/ev-node/target-height.txt)
240+
echo "Expected restored height: ${HEIGHT}"
241+
242+
# Check ev-node is producing blocks from the restored height
243+
docker logs evolveevm-ev-node-evm-single-1 2>&1 | grep "produced block" | head -10
244+
245+
# Check reth current height
246+
docker exec ev-reth curl -s -X POST -H "Content-Type: application/json" \
247+
--data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
248+
http://localhost:8545 | jq -r '.result' | xargs printf "%d\n"
249+
```
250+
251+
## Known Limitations
252+
253+
### Rollback P2P Store Errors
254+
255+
When rolling back to a height significantly lower than the current state, the p2p
256+
header and data sync stores may report "invalid range" errors. This occurs because
257+
these stores track sync progress independently. The errors can be safely ignored as:
258+
259+
1. The main blockchain state is correctly rolled back
260+
2. Using `--evnode.clear_cache` on restart clears the inconsistent cache
261+
3. The node will resync p2p data from the restored height
262+
263+
### Timestamp Consistency
264+
265+
After a restore, if significant real-world time has passed since the backup was created,
266+
you may encounter timestamp validation errors when the node attempts to continue block
267+
production. This occurs because:
268+
269+
- Reth stores block timestamps based on when blocks were originally created
270+
- After restore, the restored timestamps may be in the past relative to system time
271+
- Block validators may reject new blocks with timestamps earlier than parent blocks
272+
273+
**Workaround:** In production environments, coordinate restore operations to minimize
274+
time between backup and restore, or ensure the entire network is restored simultaneously.
275+
276+
## Summary
277+
278+
This backup/restore workflow enables point-in-time recovery for both reth (MDBX) and
279+
ev-node (Badger) datastores. Key points:
280+
281+
- **Backup**: Hot backup while nodes are running (no downtime)
282+
- **Restore**: Requires stopping services, restoring volumes, and aligning heights
283+
- **Rollback**: May show p2p store errors that can be safely ignored
284+
- **Production**: Test the full workflow in staging before deploying to production
285+
286+
The process has been validated to correctly restore state and resume block production
287+
from the backup point, with known limitations around p2p store consistency and timestamp
288+
validation that can be mitigated with proper operational procedures.

0 commit comments

Comments
 (0)