Seed / bootnode
A seed is a CometBFT seed/bootnode for peer discovery — the smallest, cheapest role. It runs the same beacond + bera-reth stack as every node, non-validating (PAYLOAD_BUILDER=false) and with tight memory limits, and helps new validators, RPC, and edge/light nodes find peers. This page covers install → deploy → operate on the public testnet (chain-id 473374).
Why a seed
Seeds are the cheapest box on the network — a Pi 5 or a mini-PC behind a VPN tunnel is enough. They hold no key and tolerate restarts, so a residential line is fine.
Hardware
| vCPU | RAM | Disk | AWS | Baremetal |
|---|---|---|---|---|
| 2 | 8 GB | 100 GB SSD | t3.large / m6i.large | 2-core, 8 GB, 100 GB SSD |
Still NVMe/SSD required for the EL — bera-reth's MDBX corrupts/wears slow or SD-card storage even for a small pruned node. A seed runs pruned (--full). Full matrix and cost tiers: Hardware specs.
Install
Every host needs the shared base — see Prerequisites: Docker Engine + Compose v2, an NVMe/SSD volume at DATA_DIR, NTP time-sync, a host firewall, and the non-root UID (10000:10000).
git clone https://github.com/foreseerco/block-l1-evm-mainnet krypton
sudo mkdir -p /opt/krypton/deploy
sudo cp krypton/l1/from-source/deploy/{docker-compose.yml,.env.example,bootstrap.sh,nftables.conf,krypton-node.service} /opt/krypton/deploy/
cd /opt/krypton/deploy
sudo cp .env.example .envImages (pin by @sha256 digest in production — see Building the images):
| Layer | Image |
|---|---|
| CL | ghcr.io/foreseerco/krypton-beacond:v1.3.9-473374 |
| EL | ghcr.io/foreseerco/krypton-bera-reth:v1.3.3 |
Deploy
A seed always joins an existing testnet — no genesis ceremony, no validator key. You need the published bundle URL and the EL_BOOTNODES / CL_SEEDS peer strings. See Genesis & the network bundle.
Configure .env (seed preset)
Set the shared core (MONIKER, EXT_IP, EL_BOOTNODES, CL_SEEDS, paths), then uncomment the seed preset:
| Var | Value | Why |
|---|---|---|
KRYPTON_ROLE | seed | documentation only |
PAYLOAD_BUILDER | false | non-validating |
RPC_HOST | 127.0.0.1 | RPC stays on loopback |
RPC_API | eth,net,web3 | minimal namespace |
EL_SYNC_MODE | --full | pruned |
EL_MEM_LIMIT | 6g | small — per the HW matrix |
CL_MEM_LIMIT | 3g | small |
EXT_IP must be reachable
A seed exists to be dialled by other nodes, so its EXT_IP (advertised via --p2p.external-address on the CL and --nat=extip: on the EL) must be a reachable public, LAN, or Tailscale/WireGuard address. Behind dynamic IP / CGNAT, set EXT_IP to a VPN tunnel address rather than paying for a static IP — fine for this non-validating role.
Bring it up
BUNDLE_URL=https://…/krypton-testnet-bundle.tar.gz sudo -E ./bootstrap.sh
# then run under systemd:
sudo cp krypton-node.service /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable --now krypton-node
journalctl -u krypton-node -fOnce it is up, publish its peer addresses so others can use it as a seed: the EL enode (port 30303) and the CometBFT node-id (<nodeid>@<ip>:26656). These feed other operators' EL_BOOTNODES / CL_SEEDS.
Operate
Config recap
A seed is the simplest role: non-validating, pruned, loopback RPC, small memory limits. Co-locating a seed with one or two full nodes on a capable box is fine to amortize cost; do not co-locate multiple validators.
Monitoring signals
See Monitoring. Quick checks:
| Signal | Command | Healthy |
|---|---|---|
| Sync | curl -s http://127.0.0.1:26657/status | jq '.result.sync_info' | catching_up: false, height climbing |
| Peers (CL) | curl -s http://127.0.0.1:26657/net_info | jq '.result.n_peers' | > 0 and serving — a seed with no peers is doing nothing |
| EL height | cast block-number --rpc-url http://127.0.0.1:8545 | climbing |
| Chain-id | cast chain-id --rpc-url http://127.0.0.1:8545 | 473374 |
For a seed, peer count is the primary signal — a healthy seed maintains and hands out peers. docker compose ps shows the compose healthchecks; logs are JSON-file, rotated 20m × 5.
Upgrade & rollback
Same coordinated, pinned-digest flow: edit the image digests in .env, then sudo systemctl restart krypton-node. Stop order CL first, then EL:
docker compose stop cl # 5m grace
docker compose stop el # 2m graceA seed holds no key — no double-sign hazard, and restarts are low-risk. If its EXT_IP changes (dynamic IP), re-publish its peer strings.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Seed has no peers / nobody connects | Wrong/unreachable EXT_IP; firewall not opening 30303/26656 | Confirm EXT_IP is reachable; open 30303 tcp+udp + 26656 tcp from 0.0.0.0/0; re-publish the enode/node-id |
| Stuck sync | Few peers; slow disk | Check peers; confirm NVMe/SSD; check EL logs for MDBX/IO errors |
| Dynamic IP keeps changing | Residential/CGNAT line | Use a WireGuard/Tailscale address for EXT_IP and publish that |
| Engine auth 401s | EL/CL JWT mismatch | Both mount the same JWT_PATH; restart both; 0600/correct owner |
| EL OOM | EL_MEM_LIMIT=6g too low under load | Raise modestly; ensure NVMe/SSD |
More in Troubleshooting.
Security
- Public surface is only the P2P ports (
30303,26656) — which a seed must expose to do its job. JSON-RPC (8545), engine API (8551), CometBFT RPC (26657), and metrics (9001) stay loopback/VPN-only. See Ports & firewall. - Containers run as a non-root UID;
DATA_DIRis0700; the JWT is0600; the bundle is mounted read-only; images pinned by digest; SSH to an admin CIDR. - A seed holds no signing key and no state worth attacking beyond availability — the main risk is exposing RPC/engine ports, which the firewall prevents.
Status
These deploy artifacts are config-validated but not yet run end-to-end on real cloud/baremetal hardware. Treat your first seed as a bring-up — verify chain-id, sync, and that peers actually connect. Mainnet (47337) reuses this procedure post-audit. See Networks & chain IDs.