{"record":{"id":"5b9f1f4670152546","repo":"tailscale/tailscale","slug":"can-not-load-x509-key-pair-for-hostname-q-w","errorCode":null,"errorMessage":"can not load x509 key pair for hostname %q: %w","messagePattern":"can not load x509 key pair for hostname %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/derper/cert.go","lineNumber":131,"sourceCode":"\tnoHostname bool   // whether hostname is an IP address\n}\n\n// NewManualCertManager returns a cert provider which read certificate by given hostname on create.\nfunc NewManualCertManager(certdir, hostname string) (certProvider, error) {\n\tkeyname := unsafeHostnameCharacters.ReplaceAllString(hostname, \"\")\n\tcrtPath := filepath.Join(certdir, keyname+\".crt\")\n\tkeyPath := filepath.Join(certdir, keyname+\".key\")\n\tcert, err := tls.LoadX509KeyPair(crtPath, keyPath)\n\thostnameIP := net.ParseIP(hostname) // or nil if hostname isn't an IP address\n\tif err != nil {\n\t\t// If the hostname is an IP address, automatically create a\n\t\t// self-signed certificate for it.\n\t\tvar certp *tls.Certificate\n\t\tif os.IsNotExist(err) && hostnameIP != nil {\n\t\t\tcertp, err = createSelfSignedIPCert(crtPath, keyPath, hostname)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"can not load x509 key pair for hostname %q: %w\", keyname, err)\n\t\t}\n\t\tcert = *certp\n\t}\n\t// ensure hostname matches with the certificate\n\tx509Cert, err := x509.ParseCertificate(cert.Certificate[0])\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can not load cert: %w\", err)\n\t}\n\tif err := x509Cert.VerifyHostname(hostname); err != nil {\n\t\treturn nil, fmt.Errorf(\"cert invalid for hostname %q: %w\", hostname, err)\n\t}\n\tif hostnameIP != nil {\n\t\t// If the hostname is an IP address, print out information on how to\n\t\t// confgure this in the derpmap.\n\t\tdn := &tailcfg.DERPNode{\n\t\t\tName:     \"custom\",\n\t\t\tRegionID: 900,\n\t\t\tHostName: hostname,","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/tailscale/tailscale/blob/cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042/cmd/derper/cert.go#L113-L149","documentation":"In --certmode=manual, NewManualCertManager builds <certdir>/<keyname>.crt and .key (keyname = --hostname with unsafe characters stripped by regex) and calls tls.LoadX509KeyPair on them. This error wraps that failure after the only recovery path: if the files don't exist AND the hostname parses as an IP, a self-signed cert is generated; anything else (missing files for a DNS name, malformed PEM, key/cert mismatch, unreadable files) surfaces here.","triggerScenarios":"Cert files not named after --hostname (e.g. fullchain.pem instead of derp.example.com.crt); --certdir pointing at the wrong directory; malformed or truncated PEM; private key not matching the certificate; file permissions; IP hostname where self-signing also failed (e.g. unwritable certdir).","commonSituations":"Following Let's Encrypt layouts (cert.pem/privkey.pem) instead of derper's expected naming; copying certs with the wrong case or including '*.' which gets stripped; certdir read-only so the IP self-sign fallback fails.","solutions":["Name the files exactly <hostname>.crt and <hostname>.key inside --certdir (characters like * are stripped from the name)","Verify the pair loads: openssl x509 -in <hostname>.crt -noout && openssl pkey -in <hostname>.key -noout, and check modulus match","Point --certdir at the directory actually containing the files","If the hostname is an IP, ensure --certdir is writable so the self-signed cert can be created","Or switch to --certmode=letsencrypt to avoid file management"],"exampleFix":"# before: derper --certmode=manual --certdir=/letsencrypt/live/derp.example.com\n#         (dir holds fullchain.pem / privkey.pem)\n# after:  cp fullchain.pem /certs/derp.example.com.crt\n#         cp privkey.pem  /certs/derp.example.com.key\n#         derper --certmode=manual --certdir=/certs --hostname=derp.example.com","handlingStrategy":"validation","validationCode":"// Pre-flight a manual cert dir before starting derper:\nfunc checkManualCert(dir, hostname string) error {\n\tkey := regexp.MustCompile(`[^a-zA-Z0-9.-]`).ReplaceAllString(hostname, \"\")\n\tif _, err := tls.LoadX509KeyPair(filepath.Join(dir, key+\".crt\"), filepath.Join(dir, key+\".key\")); err != nil {\n\t\treturn fmt.Errorf(\"manual cert pair invalid: %w\", err)\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Automate deployment of <hostname>.crt/.key with exact naming into --certdir","Verify pairs with openssl (parse + modulus match) in CI","For IP hostnames, ensure certdir is writable to get the auto self-signed cert","Consider letsencrypt mode to remove file management"],"tags":["derper","tls","certificate","manual-cert","configuration"],"backgroundTag":null,"analyzedSha":"cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042","analyzedAt":"2026-08-15T19:58:31.583Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}