docker/cli · error
private key is encrypted - support for encrypted private…
Error message
private key is encrypted - support for encrypted private keys has been removed, see https://docs.docker.com/go/deprecated/
What it means
Returned by Endpoint.tlsConfig when x509.IsEncryptedPEMBlock reports the PEM private key is passphrase-protected (DEK-Info present). Docker removed support for encrypted private keys because they cannot be used unattended (no way to supply a passphrase to the daemon/client automatically), so an encrypted key is explicitly rejected.
Solutions
- Generate or convert an unencrypted PEM key: 'openssl rsa -in encrypted.key -out plain.key'.
- Recreate the docker context using the unencrypted key file.
- If a passphrase is mandatory by policy, store the context with an unencrypted key protected by filesystem permissions instead.
Example fix
# before openssl genrsa -aes256 -out key.pem 4096 # after openssl genrsa -out key.pem 4096 chmod 600 key.pem
Defensive patterns
Strategy: validation
Validate before calling
block, _ := pem.Decode(keyBytes)
if block != nil && x509.IsEncryptedPEMBlock(block) { //nolint:staticcheck
return errors.New("encrypted keys are unsupported; decrypt the key first")
} Type guard
func isUnencryptedPEMKey(b []byte) bool {
block, _ := pem.Decode(b)
return block != nil && !x509.IsEncryptedPEMBlock(block) //nolint:staticcheck
} Prevention
- Generate keys without a passphrase.
- Convert with 'openssl rsa -in enc.key -out plain.key'.
- Protect unencrypted keys with filesystem permissions (chmod 600).
When it happens
Trigger: Creating a docker context with a TLS key that was generated with a passphrase (e.g. 'openssl genrsa -aes256'). The key has PEM headers like '-----BEGIN ENCRYPTED PRIVATE KEY-----' or contains DEK-Info.
Common situations: Reusing an SSH or TLS key generated with a passphrase for Docker context TLS. A security policy mandates encrypted keys, conflicting with Docker's deprecation.
Related errors
- no valid private key found
- default context cannot be edited
- failed to retrieve context tls info: ca.pem seems invalid
- archive format is invalid
- unable to resolve docker endpoint
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/f32272afe737fd77.
Report an issue: GitHub.
Appendix: source
Thrown at cli/context/docker/load.go:67
}
var tlsOpts []func(*tls.Config)
if ep.TLSData != nil && ep.TLSData.CA != nil {
certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(ep.TLSData.CA) {
return nil, errors.New("failed to retrieve context tls info: ca.pem seems invalid")
}
tlsOpts = append(tlsOpts, func(cfg *tls.Config) {
cfg.RootCAs = certPool
})
}
if ep.TLSData != nil && ep.TLSData.Key != nil && ep.TLSData.Cert != nil {
keyBytes := ep.TLSData.Key
pemBlock, _ := pem.Decode(keyBytes)
if pemBlock == nil {
return nil, errors.New("no valid private key found")
}
if x509.IsEncryptedPEMBlock(pemBlock) { //nolint:staticcheck // SA1019: x509.IsEncryptedPEMBlock is deprecated, and insecure by design
return nil, errors.New("private key is encrypted - support for encrypted private keys has been removed, see https://docs.docker.com/go/deprecated/")
}
x509cert, err := tls.X509KeyPair(ep.TLSData.Cert, keyBytes)
if err != nil {
return nil, fmt.Errorf("failed to retrieve context tls info: %w", err)
}
tlsOpts = append(tlsOpts, func(cfg *tls.Config) {
cfg.Certificates = []tls.Certificate{x509cert}
})
}
if ep.SkipTLSVerify {
tlsOpts = append(tlsOpts, func(cfg *tls.Config) {
cfg.InsecureSkipVerify = true
})
}
return tlsconfig.ClientDefault(tlsOpts...), nil
}
View on GitHub (pinned to 4f84911bfe)