Fixing "UNABLE_TO_VERIFY_LEAF_SIGNATURE" when running Renovate against an internal Azure DevOps server
Recently I ran into a failing Renovate pipeline right after configuring it against our internal Azure DevOps server:
"err": {
"code": "UNABLE_TO_VERIFY_LEAF_SIGNATURE",
"message": "unable to verify the first certificate; if the root CA is installed locally, try running Node.js with --use-system-ca"
}
Error: unable to verify the first certificate; if the root CA is installed locally, try running Node.js with --use-system-ca
at TLSSocket.onConnectSecure (node:internal/tls/wrap:1748:34)
at TLSSocket.emit (node:events:509:28)
at TLSSocket.emit (node:domain:489:12)
at TLSSocket._finishInit (node:internal/tls/wrap:1185:8)
at TLSWrap.ssl.onhandshakedone (node:internal/tls/wrap:966:12)
The root cause: Renovate runs on Node.js, and Node doesn't use the Windows certificate store by default. Our internal Azure DevOps server presents a certificate signed by our internal CA, and Node has no idea that CA exists. So the TLS handshake fails before Renovate ever gets to talk to the server.
Remark: The suggestion in the error message to run Node.js with the --use-system-ca flag, I couldn’t get it working.
The fix: point Node to your internal CA
The trick is the NODE_EXTRA_CA_CERTS environment variable. Set it to a .pem file containing your internal root CA certificate, and Node will trust it on top of its built-in CA bundle.
Remark: this has to be a PEM-encoded certificate, not the binary .cer format Windows exports by default.
Export the internal CA certificate
On the machine hosting your self-hosted agent:
- Open
certlm.msc(Local Computer Certificates). - Go to:
- Trusted Root Certification Authorities → Certificates, or
- Intermediate Certification Authorities → Certificates
- Find your internal CA's certificate.
- Right-click → All Tasks → Export.
- Choose:
- No, do not export the private key
- Base-64 encoded X.509 (.CER)
- Save it as, for example:
vlm-root-ca.cer - Rename it to
vlm-root-ca.pem
Base-64 encoded X.509 is PEM format, so the rename is all you need. No conversion required.
Reference it in the pipeline YAML
Point NODE_EXTRA_CA_CERTS at the file:
trigger: none
pool:
name: default
variables:
NODE_EXTRA_CA_CERTS: 'd:\vlm-root.pem'
steps:
- task: RenovateMe@1
inputs:
renovateOptionsVersion: 'latest'
That's it. Renovate now validates the certificate chain against your internal CA and can talk to your internal Azure DevOps server without complaints.