Use a remote signer
With a remote signer, the Prysm validator client never holds private keys. It sends each signing request to Web3Signer, which holds the keys, checks its slashing-protection database, and returns the signature. Use it when keys must live on a separate, hardened host, in a cloud key vault, or in an HSM.
Before you begin
- A synced beacon node. See the Quickstart.
- Your validator keystores and their slashing-protection history if they signed anywhere before.
- A fee recipient.
Set up Web3Signer
Set up Web3Signer by following its documentation. It needs Java 25 or later (or Docker), a PostgreSQL database for slashing protection, and one key configuration file per keystore. A typical start command:
bin/web3signer \
--key-store-path=/var/lib/web3signer/keys \
--http-listen-host=127.0.0.1 \
--http-listen-port=9000 \
eth2 \
--network=mainnet \
--slashing-protection-db-url="jdbc:postgresql://localhost/web3signer" \
--slashing-protection-db-username=<DB_USER> \
--slashing-protection-db-password=<DB_PASSWORD>
bin\web3signer.bat `
--key-store-path=$HOME\web3signer\keys `
--http-listen-host=127.0.0.1 `
--http-listen-port=9000 `
eth2 `
--network=mainnet `
--slashing-protection-db-url="jdbc:postgresql://localhost/web3signer" `
--slashing-protection-db-username=<DB_USER> `
--slashing-protection-db-password=<DB_PASSWORD>
The network must match Prysm's. This returns 200 when it's up:
curl http://127.0.0.1:9000/upcheck
curl.exe http://127.0.0.1:9000/upcheck
This lists the keys it holds:
curl http://127.0.0.1:9000/api/v1/eth2/publicKeys
curl.exe http://127.0.0.1:9000/api/v1/eth2/publicKeys
Point the validator client at Web3Signer
| Flag | What to set |
|---|---|
--validators-external-signer-url | Web3Signer's URL, for example http://127.0.0.1:9000. With it set, Prysm doesn't load a local wallet. |
--validators-external-signer-public-keys | The public keys this client validates with, comma-separated. Or a URL that returns them, such as http://127.0.0.1:9000/api/v1/eth2/publicKeys. |
--validators-external-signer-key-file | And/or: a file with one public key per line. It must exist; Prysm watches it for changes. Keys added through the Keymanager API are saved here. |
--validators-external-signer-poll-interval | With a public keys URL: how often to fetch it again, for example 5m, so keys added to or removed from the signer are picked up without a restart. Off by default. |
You can combine these sources: the validator client validates with the keys from all of them. A failed or empty answer from the URL keeps the current keys, so emptying the signer doesn't stop validating.
Add them to your validator client command and restart it:
--validators-external-signer-url=http://127.0.0.1:9000
--validators-external-signer-public-keys=<PUBKEY_1>,<PUBKEY_2>
Replace the placeholders with your 0x-prefixed public keys. Keep Web3Signer on the same host or LAN: every duty waits for a signature. The validator client still keeps its own slashing protection database, so you get two layers of protection.
If the keys ever signed anywhere else, import that client's slashing protection history into Web3Signer and into Prysm before anything signs, and make sure the old client can never run the keys again. Refer tp tje Move your slashing protection history page.
Check that it works
At startup, the validator client logs:
No Prysm wallet required for web3signer validation
After a few epochs, it logs this for your keys:
Submitted new attestations
Web3Signer's logs show the signing requests.
All remote signer flags are in the validator reference.