cilium/cilium · critical

failed to load or generate private key: %w

Error message

failed to load or generate private key: %w

What it means

The agent's init step loads the WireGuard private key from privKeyPath or generates a new one; any failure (unreadable file, invalid key data, unwritable directory) is wrapped as "failed to load or generate private key". Without a valid private key the node cannot identify itself on the WireGuard mesh.

Source

Thrown at pkg/wireguard/agent/agent.go:286

				types.NodeEncryptionOptOutLabels+" label selector",
			logfields.Selector, a.config.NodeEncryptionOptOutLabels,
		)
		localNode.Local.OptOutNodeEncryption = true
		localNode.EncryptionKey = 0
	}

	a.optOut = localNode.Local.OptOutNodeEncryption
}

// init creates and configures the local WireGuard tunnel device.
func (a *Agent) init() error {
	a.Lock()
	defer a.Unlock()

	var err error
	a.privKey, err = loadOrGeneratePrivKey(a.privKeyPath)
	if err != nil {
		return fmt.Errorf("failed to load or generate private key: %w", err)
	}

	// Best-effort MTU computation: account for WireGuard overhead including padding.
	// Without this, the kernel defaults to 1500 - 80 = 1420, ignoring alignment padding.
	// Worst case we set 1500 - 95 = 1405; the mtuReconciler will adjust once the MTU table is populated.
	deviceMTU := mtu.EthernetMTU
	if mtuRoute, _, _, found := a.mtuTable.GetWatch(a.db.ReadTxn(), mtu.MTURouteByPrefix(mtu.DefaultPrefixV4)); found {
		deviceMTU = mtuRoute.DeviceMTU
	}
	linkMTU := deviceMTU - mtu.WireguardOverhead

	link := &netlink.Wireguard{
		LinkAttrs: netlink.LinkAttrs{
			Name: types.IfaceName,
			MTU:  linkMTU,
		},
	}

View on GitHub (pinned to ac7b90affa)

Solutions

  1. Fix permissions on the private key file and its directory so the agent user can read/write.
  2. Delete the corrupt key file to let the agent generate a fresh one (peers will re-learn the public key).
  3. Ensure the volume backing privKeyPath is writable and persistent.
  4. Inspect the wrapped error to distinguish read vs. generate failure.

Example fix

// before
chown -R kube-apiserver: /var/lib/cilium/wg  # wrong user
// after
chown -R root:root /var/lib/cilium/wg && chmod 700 /var/lib/cilium/wg
Defensive patterns

Strategy: validation

Validate before calling

// pre-flight: key path must be readable, dir writable
if info, err := os.Stat(privKeyPath); err == nil && info.Mode().Perm()&0400 == 0 {
    return errors.New("private key file not readable")
}
if err := os.WriteFile(filepath.Join(filepath.Dir(privKeyPath), ".probe"), nil, 0600); err != nil {
    return fmt.Errorf("key dir not writable: %w", err)
}

Try / catch

if err := agent.Start(ctx); err != nil {
    if strings.Contains(err.Error(), "failed to load or generate private key") {
        return fmt.Errorf("fix permissions or delete corrupt key file: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: loadOrGeneratePrivKey fails because the key file exists but is unreadable/corrupt, its parent directory is missing or not writable, or its content is not a valid curve25519 key.

Common situations: Container running as non-root without access to the key path; stale/corrupt key from an interrupted write; read-only root filesystem; key truncated after a node crash.

Related errors


AI-assisted analysis of cilium/cilium@ac7b90affa (2026-08-31). Data as JSON: /api/errors/ca3955f1b413422e. Report an issue: GitHub.