The problem: libp2p generates a new peer ID every time you restart your application, making it impossible to maintain a consistent identity on the network.
The solution: Persist the private key and reuse it on subsequent starts.
The libp2p documentation and many online examples are outdated. The API has changed significantly, and the old marshalPrivateKey/unmarshalPrivateKey functions no longer exist. After digging through the source code, I found the correct modern approach.
P.S. I tried using Cursor and Kiro for this exact task, but unfortunately, they weren’t able to provide a working solution. I ended up spending two days reviewing code and questioning whether the task was even feasible. I had even given them access to the source code of the relevant libraries, but despite that, they couldn’t figure it out. It seems they still need a fair amount of human guidance. That said, they were quite helpful when it came to adding comments and logs—as you’ll see in the code attached to this Gist!
Looking at the createLibp2p source code:
The solution is to persist the private key, not the peer ID itself.
The @libp2p/crypto/keys module exports these functions:
- privateKeyToProtobuf(privateKey) - serialize private key
- privateKeyFromProtobuf(bytes) - deserialize private key
- generateKeyPair(type) - create new key pair
- First run: Generates a new Ed25519 private key and saves it as protobuf bytes
- Subsequent runs: Loads the saved key and recreates the same peer ID
- File format: Binary protobuf format (the standard libp2p uses internally)
- ✅ Consistent identity across application restarts
- ✅ Modern API - works with latest libp2p versions
- ✅ Simple - just pass the privateKey to createLibp2p
- ✅ Standard format - uses libp2p's internal protobuf serialization
- ✅ Automatic fallback - creates new key if loading fails
❌ Don't try to persist the peer ID object directly
❌ Don't use the old marshalPrivateKey/unmarshalPrivateKey (they don't exist anymore)
❌ Don't use privateKey.marshal() directly (different format)
✅ Do use privateKeyToProtobuf/privateKeyFromProtobuf
✅ Do pass the privateKey to createLibp2p({ privateKey })
- @libp2p/crypto: ^5.1.1
- @libp2p/peer-id: ^5.0.8
- libp2p: ^2.3.1
- Node.js 18+
Found an issue or improvement? Please share in the comments below!
This guide was created after struggling with outdated documentation and API changes. Hope it saves you time! 🙏
.png)

