Skip to content

Commit 8cf0fed

Browse files
committed
feat: add Reth Backup Helper script for MDBX database snapshots
1 parent fdc59ab commit 8cf0fed

2 files changed

Lines changed: 202 additions & 0 deletions

File tree

scripts/reth-backup/README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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+
## Prerequisites
7+
8+
- Docker access to the container running `ev-reth` (defaults to the service name
9+
`ev-reth` from `docker-compose`).
10+
- The `mdbx_copy` binary available inside that container. If it is not provided
11+
by the image, compile it once inside the container (see [libmdbx
12+
documentation](https://libmdbx.dqdkfa.ru/)).
13+
- `jq` installed on the host to parse the JSON output.
14+
15+
## Usage
16+
17+
```bash
18+
./scripts/reth-backup/backup.sh \
19+
--container ev-reth \
20+
--datadir /home/reth/eth-home \
21+
--mdbx-copy /tmp/libmdbx/build/mdbx_copy \
22+
/path/to/backups
23+
```
24+
25+
This creates a timestamped folder under `/path/to/backups` with:
26+
27+
- `db/mdbx.dat` – consistent MDBX snapshot.
28+
- `db/mdbx.lck` – placeholder lock file (empty).
29+
- `static_files/` – static files copied from the node.
30+
- `stage_checkpoints.json` – raw StageCheckpoints table.
31+
- `height.txt` – extracted block height (from the `Finish` stage).
32+
33+
Additional flags:
34+
35+
- `--tag LABEL` to override the timestamped folder name.
36+
- `--keep-remote` to leave the temporary snapshot inside the container (useful
37+
for debugging).
38+
39+
The script outputs the height at the end so you can coordinate other backups
40+
with the same block number.

scripts/reth-backup/backup.sh

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
usage() {
6+
cat <<'EOF'
7+
Usage: backup.sh [OPTIONS] <destination-directory>
8+
9+
Create a consistent backup of the ev-reth database using mdbx_copy and record
10+
the block height captured in the snapshot.
11+
12+
Options:
13+
--container NAME Docker container name running ev-reth (default: ev-reth)
14+
--datadir PATH Path to the reth datadir inside the container
15+
(default: /home/reth/eth-home)
16+
--mdbx-copy CMD Path to the mdbx_copy binary inside the container
17+
(default: mdbx_copy; override if you compiled it elsewhere)
18+
--tag LABEL Custom label for the backup directory (default: timestamp)
19+
--keep-remote Leave the temporary snapshot inside the container
20+
-h, --help Show this help message
21+
22+
Requirements:
23+
- Docker access to the container running ev-reth.
24+
- mdbx_copy available inside that container (compile it once if necessary).
25+
- jq installed on the host (used to parse StageCheckpoints JSON).
26+
27+
The destination directory will receive:
28+
<dest>/<tag>/db/mdbx.dat MDBX snapshot
29+
<dest>/<tag>/db/mdbx.lck Empty lock file placeholder
30+
<dest>/<tag>/static_files/... Static files copied from the node
31+
<dest>/<tag>/stage_checkpoints.json
32+
<dest>/<tag>/height.txt Height extracted from StageCheckpoints
33+
EOF
34+
}
35+
36+
require_cmd() {
37+
if ! command -v "$1" >/dev/null 2>&1; then
38+
echo "error: required command '$1' not found in PATH" >&2
39+
exit 1
40+
}
41+
}
42+
43+
DEST=""
44+
CONTAINER="ev-reth"
45+
DATADIR="/home/reth/eth-home"
46+
MDBX_COPY="mdbx_copy"
47+
BACKUP_TAG=""
48+
KEEP_REMOTE=0
49+
50+
while [[ $# -gt 0 ]]; do
51+
case "$1" in
52+
--container)
53+
CONTAINER="$2"
54+
shift 2
55+
;;
56+
--datadir)
57+
DATADIR="$2"
58+
shift 2
59+
;;
60+
--mdbx-copy)
61+
MDBX_COPY="$2"
62+
shift 2
63+
;;
64+
--tag)
65+
BACKUP_TAG="$2"
66+
shift 2
67+
;;
68+
--keep-remote)
69+
KEEP_REMOTE=1
70+
shift
71+
;;
72+
-h|--help)
73+
usage
74+
exit 0
75+
;;
76+
--)
77+
shift
78+
break
79+
;;
80+
-*)
81+
echo "unknown option: $1" >&2
82+
usage >&2
83+
exit 1
84+
;;
85+
*)
86+
if [[ -z "$DEST" ]]; then
87+
DEST="$1"
88+
shift
89+
else
90+
echo "unexpected argument: $1" >&2
91+
usage >&2
92+
exit 1
93+
fi
94+
;;
95+
esac
96+
done
97+
98+
if [[ -z "$DEST" ]]; then
99+
echo "error: destination directory is required" >&2
100+
usage >&2
101+
exit 1
102+
fi
103+
104+
require_cmd docker
105+
require_cmd jq
106+
107+
if [[ -z "$BACKUP_TAG" ]]; then
108+
BACKUP_TAG="$(date +'%Y%m%d-%H%M%S')"
109+
fi
110+
111+
REMOTE_TMP="/tmp/reth-backup-${BACKUP_TAG}"
112+
HOST_DEST="$(mkdir -p "$DEST" && cd "$DEST" && pwd)/${BACKUP_TAG}"
113+
114+
echo "Creating backup tag '$BACKUP_TAG' into ${HOST_DEST}"
115+
116+
# Prepare temporary workspace inside the container.
117+
docker exec "$CONTAINER" bash -c "rm -rf '$REMOTE_TMP' && mkdir -p '$REMOTE_TMP/db' '$REMOTE_TMP/static_files'"
118+
119+
# Verify mdbx_copy availability.
120+
if ! docker exec "$CONTAINER" bash -lc "command -v '$MDBX_COPY' >/dev/null 2>&1 || [ -x '$MDBX_COPY' ]"; then
121+
echo "error: unable to find executable '$MDBX_COPY' inside container '$CONTAINER'" >&2
122+
exit 1
123+
fi
124+
125+
echo "Running mdbx_copy inside container..."
126+
docker exec "$CONTAINER" bash -lc "'$MDBX_COPY' --compact '${DATADIR}/db' '$REMOTE_TMP/db/mdbx.dat'"
127+
docker exec "$CONTAINER" bash -lc "touch '$REMOTE_TMP/db/mdbx.lck'"
128+
129+
echo "Copying static_files..."
130+
docker exec "$CONTAINER" bash -lc "if [ -d '${DATADIR}/static_files' ]; then cp -a '${DATADIR}/static_files/.' '$REMOTE_TMP/static_files/' 2>/dev/null || true; fi"
131+
132+
echo "Querying StageCheckpoints height..."
133+
STAGE_JSON=$(docker exec "$CONTAINER" ev-reth db --datadir "$REMOTE_TMP" list StageCheckpoints --len 20 --json)
134+
HEIGHT=$(echo "$STAGE_JSON" | jq -r '.[] | select(.[0]=="Finish") | .[1].block_number' | tr -d '\r\n')
135+
136+
if [[ -z "$HEIGHT" || "$HEIGHT" == "null" ]]; then
137+
echo "warning: could not determine height from StageCheckpoints" >&2
138+
fi
139+
140+
echo "Copying snapshot to host..."
141+
mkdir -p "$HOST_DEST/db"
142+
docker cp "${CONTAINER}:${REMOTE_TMP}/db/mdbx.dat" "$HOST_DEST/db/mdbx.dat"
143+
docker cp "${CONTAINER}:${REMOTE_TMP}/db/mdbx.lck" "$HOST_DEST/db/mdbx.lck"
144+
145+
if docker exec "$CONTAINER" test -d "${REMOTE_TMP}/static_files"; then
146+
mkdir -p "$HOST_DEST/static_files"
147+
docker cp "${CONTAINER}:${REMOTE_TMP}/static_files/." "$HOST_DEST/static_files/"
148+
fi
149+
150+
echo "$STAGE_JSON" > "$HOST_DEST/stage_checkpoints.json"
151+
if [[ -n "$HEIGHT" && "$HEIGHT" != "null" ]]; then
152+
echo "$HEIGHT" > "$HOST_DEST/height.txt"
153+
echo "Backup height: $HEIGHT"
154+
else
155+
echo "Height not captured (see stage_checkpoints.json for details)"
156+
fi
157+
158+
if [[ "$KEEP_REMOTE" -ne 1 ]]; then
159+
docker exec "$CONTAINER" rm -rf "$REMOTE_TMP"
160+
fi
161+
162+
echo "Backup completed: $HOST_DEST"

0 commit comments

Comments
 (0)