penumbra-1 · maintained by Rotko Networks

Penumbra Validator Kit

Everything needed to bring a new validator onto the Penumbra mainnet: hardware, the pruned snapshot so you sync in an hour instead of a week, the exact commands, and the rules that get you jailed.

pd 2.0.8 current release CometBFT 0.37.18 required (reports itself as 0.37.16) APP_VERSION 11 Block time ~5 s Epoch 34,560 blocks (~2 days)
0

Requirements

Penumbra's own guidance is 2 to 4 vCPU, 8 GB RAM and ~200 GB of disk growing 20 GB a week. With the pruned snapshot and a pruned pd, the disk figure drops to about 100 GB growing roughly 2 GB a week. Disk latency matters more than CPU: the state store is RocksDB and a virtualized cloud SSD will make you fall behind.

CPU
4 vCPU 2 minimum
Memory
16 GB 8 GB minimum; the prune peaks at 4.7 GB
Disk
200 GB NVMe ~100 GB used after a pruned restore
OS
Debian stable x86_64 or arm64
Network
Public IPv4 26656/tcp inbound
File limit
LimitNOFILE=65536 default 1024 is fatal
PortServiceExposure
26656CometBFT P2PPublic. The only inbound port you need.
26657CometBFT RPCPrivate
26658pd ABCIPrivate, localhost only
8080pd gRPCPrivate unless you run an RPC service
9000, 26660MetricsPrivate
1

Stake and the rules

These are the live chain parameters for penumbra-1, read from state at height 12,562,477 on 2026-09-06. Amounts are in UM; the chain counts in upenumbra, one millionth of a UM.

ParameterValueWhat it means for you
Minimum validator stake100 UMYour validator needs at least this much delegated to it, self-delegation included, before it can be active.
Active set100Only the top 100 validators by voting power sign blocks and earn. Rank is recomputed every epoch.
Epoch34,560 blocksAbout 2 days. Delegations, rank changes and your activation take effect at the next epoch boundary.
Signed-blocks window10,000 blocksUptime is judged over a sliding window of about 14 hours.
Missed-blocks maximum9,500Miss more than this within the window and you are jailed. In practice, an outage longer than about 13 hours.
Unbonding delay120,960 blocksAbout 7 days for delegations to leave. Also the window in which past misbehaviour can still be punished.
Downtime penalty0.01%Applied once when you are jailed for missing blocks. Small, but you stop earning until you re-enable the validator with a new definition upload.
Misbehaviour penalty10%Double-signing slashes 10% of everything delegated to you and tombstones the validator. Never run the same consensus key on two machines.

Budget for the stake itself, a small amount for transaction fees, and one funding-stream decision: Penumbra has no flat commission. Your validator definition declares one or more streams, each a recipient address and a rate in basis points, and the rest flows to delegators.

2

Install pd and CometBFT

5 minutes

Releases come from Rotko's repository. The installer puts pd in ~/.cargo/bin; move it wherever your unit files expect.

# pd 2.0.8, prebuilt for x86_64 and arm64 Linux
curl --proto '=https' --tlsv1.2 -LsSf \
  https://github.com/rotkonetworks/penumbra/releases/download/v2.0.8/pd-installer.sh | sh
pd --version
# CometBFT 0.37.18 (the binary prints 0.37.16; that is a known upstream bug)
curl -LO https://github.com/cometbft/cometbft/releases/download/v0.37.18/cometbft_0.37.18_linux_amd64.tar.gz
tar -xzf cometbft_0.37.18_linux_amd64.tar.gz cometbft
sudo install -m 0755 cometbft /usr/local/bin/cometbft

Do not run anything older than 0.37.18. It carries the fix for the critical January 2026 CometBFT advisory.

3

Join the network

1 minute

This generates the node directory under ~/.penumbra/network_data/node0, including your node key and consensus key, and fetches genesis and peers from an existing node.

pd network join \
  --moniker "your-validator-name" \
  --external-address "YOUR.PUBLIC.IP:26656" \
  https://penumbra.rotko.net

Then create the two systemd units. The file limit line is not optional.

# /etc/systemd/system/penumbra.service
[Unit]
Description=Penumbra daemon
After=network-online.target
[Service]
User=penumbra
ExecStart=/usr/local/bin/pd start --home /home/penumbra/.penumbra/network_data/node0/pd
Restart=on-failure
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target

# /etc/systemd/system/cometbft.service
[Unit]
Description=CometBFT
After=penumbra.service
Requires=penumbra.service
[Service]
User=penumbra
ExecStart=/usr/local/bin/cometbft start --home /home/penumbra/.penumbra/network_data/node0/cometbft
Restart=on-failure
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target

Do not start them yet. Restore the snapshot first, or you will replay the chain from the last upgrade height, which takes days.

4

Restore the pruned snapshot

~1 hour, mostly download

The snapshot is a pd state store already pruned to a single version plus CometBFT's block history since the last upgrade. It contains no keys and no node identity. Your own priv_validator_state.json must survive the restore, so it is moved aside and back.

Preferred: torrent. The torrent is seeded continuously by Rotko and by everyone who restored before you, and it carries our HTTPS host as a web seed, so it never has fewer sources than the plain download. Any client works; the commands use aria2c because it is non-interactive.

cd ~/.penumbra/network_data/node0
curl -LO https://snapshot.rotko.net/penumbra-1/latest/manifest.json
curl -LO "https://snapshot.rotko.net/penumbra-1/latest/$(jq -r .torrent manifest.json)"
aria2c --seed-time=0 --dir=. "$(jq -r .torrent manifest.json)"
# the torrent's piece hashes verify the download; the sha256 below is a second, independent check
sha256sum -c <(jq -r '"\(.sha256)  \(.file)"' manifest.json)

Or plain HTTPS, resumable with curl -C -:

curl -C - -LO "https://snapshot.rotko.net/penumbra-1/latest/$(jq -r .file manifest.json)"
sha256sum -c <(jq -r '"\(.sha256)  \(.file)"' manifest.json)

Then swap it in, keeping your own signing state:

mv cometbft/data/priv_validator_state.json /tmp/pvs.json
rm -rf pd/rocksdb cometbft/data
lz4 -dc "$(jq -r .file manifest.json)" | tar -xf -
mkdir -p cometbft/data && mv /tmp/pvs.json cometbft/data/

Leave your torrent client seeding afterwards if you can. That is how the next validator gets it quickly.

Verify against the chain, not against us

The manifest carries the snapshot height H and the pd root hash. That root hash is the app_hash in the header of block H+1, which any RPC will give you:

H=$(jq -r .height manifest.json)
curl -s "https://penumbra.rotko.net/block?height=$((H+1))" | jq -r .result.block.header.app_hash
jq -r .root_hash manifest.json
# the two lines must be identical (case-insensitive)
5

Run and verify

minutes to catch up
sudo chown -R penumbra:penumbra ~penumbra/.penumbra
sudo systemctl enable --now penumbra
sudo systemctl enable --now cometbft
journalctl -fu cometbft

The first CometBFT lines should read ABCI Handshake App Info height=H hash=<root hash> followed by Completed ABCI Handshake. Blocks then arrive from peers at a few hundred per minute until you reach the tip.

curl -s localhost:26657/status | jq '.result.sync_info | {latest_block_height, catching_up}'

You are synced when catching_up is false. Nothing you do in the next step matters until then.

6

Become a validator

2 days to activation

The wallet that owns the validator can live on a different machine from the node, and for a production validator it should. The identity key derived from that wallet is permanent; you cannot rotate it later. Choose custody accordingly.

# wallet for the validator identity (soft-kms is fine for testing; use threshold custody in production)
pcli init soft-kms generate
pcli view address 0
# fund this address with at least 100 UM plus a little for fees
pcli view balance
# build the definition from the node's consensus key
pcli validator definition template \
  --tendermint-validator-keyfile ~/.penumbra/network_data/node0/cometbft/config/priv_validator_key.json \
  --file validator.toml

Edit validator.toml: set enabled = true, fill name, website and description, and decide your funding streams. If pcli runs elsewhere, paste the value from the node's priv_validator_key.json into [consensus_key]. Then upload, find your identity, and self-delegate.

pcli validator definition upload --file validator.toml
pcli validator identity
pcli tx delegate 100penumbra --to penumbravalid1...
pcli query validator list --show-inactive

Your validator appears as inactive immediately and becomes active at the next epoch boundary if it ranks inside the top 100 by voting power. Every later change to the definition is the same upload with sequence_number incremented.

The one unrecoverable mistake

Two nodes with the same priv_validator_key.json will eventually double-sign. That is a 10% slash of all delegations and a permanent tombstone. Back the key up offline, never run it twice, and never restore a snapshot without keeping your own priv_validator_state.json.

7

Day two

Pruning your own node later

A node restored from this snapshot starts pruned. If you joined the older way, pd migrate prune in pd 2.0.8 takes the state store from about 350 GB to 30 GB in an hour. Both services are stopped for the duration. Coordinate with the other validators so that never more than a third of voting power is offline, and if you alone hold more than a third, do not prune. The full procedure is in the 2.0.8 release notes.

Upgrades

Consensus upgrades are announced on the repository. Point releases in the same series, such as 2.0.6 to 2.0.8, are drop-in binary swaps with no coordination. A release with a new APP_VERSION halts the chain at a governance-set height and every node runs pd migrate; that migration deletes CometBFT block history by design, which is why the archive is kept separately.

Monitoring

Watch two numbers: your node's latest_block_height against a public RPC, and the missed-blocks counter for your identity in pcli query validator uptime. Alert well before 9,500.

Help

Issues and questions go to github.com/rotkonetworks/penumbra. The public RPC is https://penumbra.rotko.net, which stays unpruned as the network's archive and relayer endpoint.

Snapshots

Taken weekly from Rotko's archive node with a ZFS freeze, pruned on a clone, verified by starting a node on the result and syncing it live before publishing. Every snapshot is published as a torrent with the HTTPS file as a web seed, and Rotko seeds it permanently; seeding health is on status.rotko.net. Restore with the commands in step 4.

DateHeightGet itSizepd root hashsha256
2026-09-06 12563194 magnet · .torrent · https
infohash 8d5b6ea2aea97926a93650f72798a68789a0a101
83.9 GiB d89a1cc83eccc2d52d778e934357a700ecfb728fc26191c2221bd81890085544 3cd41f8a402d0c0ca44eab88aebe854a86ea76f4feba71e4125334c7ee7eaebe

Each snapshot lives at https://snapshot.rotko.net/penumbra-1/<height>/ with its manifest.json and .sha256 beside it, and penumbra-1/latest/ always points at the newest one, so the restore commands above never need editing. snapshots.rotko.net is an alias of the same host.