Skip to main content

Backing up and restoring a node

The most important thing to back up is your node’s .demos_identity file — it holds the ed25519 private key that defines your node on the network. Lose it and you lose that identity permanently. Chain data (Postgres) is recoverable by resyncing from peers; identity is not. The procedure differs by install track:
  • Track 1 — Docker Compose (recommended). Identity, peerlist, TLSNotary key, and runtime artefacts live inside the named volume demos_node_state. You back up the volume; chain data lives separately in demos_pgdata.
  • Track 2 — bare metal ./run (advanced). Identity and peerlist are plain files in the repo root; Postgres state lives inside the sidecar container’s volume.
Treat .demos_identity like an SSH private key. Store backups encrypted, and never commit them to a repo.

What’s in each volume

VolumeHolds
demos_node_state.demos_identity, demos_peerlist.json, .tlsnotary-key, output/
demos_pgdataPostgreSQL data directory (chain state, indexes)
demos_node_dataBundled bootstrap data (genesis.json, evmChains, l2ps) plus runtime artefacts
demos_node_logsNode logs
demos_grafana_dataGrafana dashboards, users, settings
demos_prometheus_dataPrometheus TSDB
The demos_ prefix is set explicitly in docker-compose.yml, so the volume names are stable regardless of the directory you ran compose from.

Back up the node identity (and node-side state)

The minimum you must back up is demos_node_state. From the repo root:
docker run --rm \
  -v demos_node_state:/src \
  -v "$PWD":/dst \
  alpine tar czf /dst/node-state-backup.tar.gz -C /src .
That writes node-state-backup.tar.gz into the current directory. The node does not need to be stopped to read this volume, but for a consistent snapshot of output/ and .tlsnotary-key, prefer to run the backup while the node is stopped (docker compose down). Store the resulting tarball off-host. It contains your private key.

Back up chain state (optional)

Chain state (demos_pgdata) is recoverable by resyncing from peers, so backing it up is optional. If you want a fast restart without resync:
docker compose down                                # quiesce Postgres for a clean snapshot
docker run --rm \
  -v demos_pgdata:/src \
  -v "$PWD":/dst \
  alpine tar czf /dst/pgdata-backup.tar.gz -C /src .
docker compose up -d
For a logical (pg_dump) backup instead, see the Track 2 section below — the same pg_dump works against the compose Postgres if you exec into the container.

Restore

Restoring reverses the backup: stop the stack, recreate empty volumes, untar into them, and start.
# Stop and DELETE the existing volumes (this is destructive — you are
# about to overwrite identity + chain state with the backup contents).
docker compose down -v

# Recreate the volume(s) and restore. Docker auto-creates the volume on
# first mount, so no explicit `docker volume create` is needed.
docker run --rm \
  -v demos_node_state:/dst \
  -v "$PWD":/src \
  alpine tar xzf /src/node-state-backup.tar.gz -C /dst

# Optional: restore chain state too
docker run --rm \
  -v demos_pgdata:/dst \
  -v "$PWD":/src \
  alpine tar xzf /src/pgdata-backup.tar.gz -C /dst

# Bring the stack back up
docker compose up -d
The node will reuse the restored .demos_identity instead of generating a new one, and (if you restored demos_pgdata) skip resyncing from genesis.

Track 2: Bare metal ./run (advanced)

In Track 2 the identity and peerlist are plain files in the repo root, and Postgres lives in the ./run-managed sidecar (host port 5332).

Back up identity and peerlist

# From the node repo root, with the node STOPPED (Ctrl+C / Q)
tar czf node-files-backup.tar.gz \
  .demos_identity \
  publickey_* \
  demos_peerlist.json \
  .tlsnotary-key \
  output/
Set tight permissions on the backup:
chmod 600 node-files-backup.tar.gz

Back up the Postgres sidecar

Use pg_dump against the host-mapped port:
pg_dump -h localhost -p 5332 -U demosuser -d demos -Fc -f demos.dump
(The -Fc flag writes Postgres’s custom binary dump format, which is the fastest to restore.)

Restore

# 1. Stop the node and the sidecar
#    Press Ctrl+C / Q in the ./run terminal, then:
cd postgres_5332 && ./stop.sh && cd ..

# 2. Restore identity and peerlist
tar xzf node-files-backup.tar.gz

# 3. Bring the sidecar back, then restore Postgres
cd postgres_5332 && ./clean.sh && ./start.sh && cd ..
pg_restore -h localhost -p 5332 -U demosuser -d demos --clean --if-exists demos.dump

# 4. Start the node
./run

Reset chain state without losing identity

If you want to wipe blocks and transactions but keep your identity (and any GCR accounts the node knows about), use the ./run backup/restore flag together with the clean-DB flag:
./run -b true        # restore from the most recent dump in output/
./run -c true        # clean Postgres on startup
The -b true flow dumps GCR accounts to JSON in output/ before clearing Postgres, then re-imports them when the node restarts; -c true triggers the wipe. After the next genesis block is forged, the dumped accounts are re-created at their previous balances.
To attach the restored node to an existing network, see Joining the testnet using a custom genesis. Re-importing accounts changes your local genesis hash, so any peer you join must agree on that hash.