InletDownload

SQL Server error

The certificate chain was issued by an authority that is not trusted (SQL Server)

Your client encrypted the connection and checked the server’s certificate, and nobody it trusts signed it: usually SQL Server’s own self-signed certificate. ODBC Driver 18 and Microsoft.Data.SqlClient 4.0 encrypt by default, so it often appears after an upgrade. Give the server a certificate the client can verify, or trust its CA; TrustServerCertificate only skips the check.

Microsoft ODBC Driver 18 for SQL Server : SSL Provider: [error:0A000086:SSL routines::certificate verify failed:self-signed certificate].

Tested on SQL Server 2022 (16.0.4295.3), 2019 and 2025; sqlcmd 18.6 with ODBC Driver 18.6 on Linux · Updated 9 October 2026

What it means

When a client encrypts its connection to SQL Server, it checks the server’s TLS certificate: that it was signed by a certificate authority (CA) the client trusts, hasn’t expired, and carries the name you connected to. This error means the first check failed: the certificate chain doesn’t end at a CA your machine trusts.

Most of the time the certificate is SQL Server’s own. A server that hasn’t been given a certificate makes a self-signed one when the service starts, named SSL_Self_Signed_Fallback, and uses it to encrypt anyway. Nothing trusts it, because nothing signed it but itself. All three of our test servers (2019, 2022 and 2025) presented one, and its validity began the moment the service last started, so it changes with every restart.

The words depend on the client:

ClientMessage
Microsoft.Data.SqlClient, System.Data.SqlClient, SSMSA connection was successfully established with the server, but then an error occurred during the login process. (provider: SSL Provider, error: 0 - The certificate chain was issued by an authority that is not trusted.)
ODBC Driver 18 on Windows[Microsoft][ODBC Driver 18 for SQL Server]SSL Provider: The certificate chain was issued by an authority that is not trusted.
ODBC Driver 18 on Linux (OpenSSL), e.g. sqlcmd in a containerSSL Provider: [error:0A000086:SSL routines::certificate verify failed:self-signed certificate], or …:unable to get local issuer certificate
InletThe server’s certificate couldn’t be verified: self-signed certificate

OpenSSL’s reason tells you which case you’re in. self-signed certificate is the server’s own certificate, signed by nobody. unable to get local issuer certificate is a certificate signed by a CA your machine doesn’t have, typically a company’s private CA. If the CA is trusted but you connected by a name the certificate doesn’t list, OpenSSL says subject name does not match host name; Windows clients say The target principal name is incorrect.

Why it started after an upgrade

Older drivers didn’t encrypt unless asked. The new ones do, and verify the certificate when they do:

  • ODBC Driver 18 defaults to Encrypt=yes (driver 17 and earlier: no).
  • Microsoft.Data.SqlClient 4.0 changed Encrypt to default to true.
  • OLE DB Driver 19 defaults to Mandatory.

So the same server, with the same self-signed certificate, worked before the upgrade and fails after it. And if the server has Force Encryption on, ODBC Driver 18 checks the certificate even with Encrypt=no.

Common causes

  1. The server uses its self-signed certificate, and a newer driver now encrypts and checks it.
  2. The certificate comes from a private CA that this machine (or this container, or this .NET runtime on Linux) doesn’t trust.
  3. The server sends only its own certificate, not the intermediate CA that links it to a trusted root.
  4. You connect by a different name than the certificate holds: an IP address, a DNS alias, a Docker service name, host.docker.internal.

How to fix it

Give the server a certificate clients can verify (the real fix)

Get a certificate from a CA your clients trust (a public CA, or your company’s), with the names clients use in its Subject Alternative Names, and configure SQL Server to use it:

  • Windows: SQL Server Configuration Manager › SQL Server Network Configuration › Protocols for <instance> › Properties › Certificate. Restart the service.

  • Linux and Docker: point mssql-conf at the certificate and key, then restart. Our TLS test server uses this /var/opt/mssql/mssql.conf:

    [network]
    tlscert = /var/opt/mssql/inlet-server.pem
    tlskey = /var/opt/mssql/inlet-server.key
    tlsprotocols = 1.2
    forceencryption = 1
    

Include the intermediate certificates in what the server presents, so clients can build the chain. On managed services you don’t install the certificate yourself: connect with the server’s full DNS name, and on Amazon RDS trust the certificate bundle AWS publishes for its RDS certificate authorities (see AWS RDS for SQL Server).

Or trust the CA on the client

If the server’s certificate comes from your own CA, add that CA (not the server’s certificate) to the client’s trust store:

  • macOS: add it to the System keychain and mark it trusted: sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ca.pem

  • Debian or Ubuntu, including most containers:

    cp ca.pem /usr/local/share/ca-certificates/company-ca.crt
    update-ca-certificates
    
  • Windows: import it into Trusted Root Certification Authorities for the computer.

Fix the host name

Connect with a name the certificate lists. If you must use another one (an alias, an address inside Docker), tell the driver which name to expect instead: HostNameInCertificate=<name> in ODBC Driver 18 (-F <name> in sqlcmd) and in Microsoft.Data.SqlClient 5.0 and later.

TrustServerCertificate: a stopgap, not a fix

TrustServerCertificate=yes (ODBC), TrustServerCertificate=true (SqlClient) or -C (sqlcmd) keeps encryption but skips the check, so anyone who can intercept the connection can pretend to be your server. It’s reasonable for a throwaway local container; for anything else, use one of the fixes above. Encrypt=optional (or no) turns encryption of the data off and so avoids the check too, unless the server forces encryption. With Encrypt=strict (TDS 8.0), the driver always verifies and ignores TrustServerCertificate.

Reproduce it

sqlcmd 18.6 (ODBC Driver 18.6, which uses OpenSSL on Linux), run inside the SQL Server 2022 container, without -C:

sqlcmd -S localhost -U inlet -P <password> -Q "select 1"
Sqlcmd: Error: Microsoft ODBC Driver 18 for SQL Server : SSL Provider: [error:0A000086:SSL routines::certificate verify failed:self-signed certificate].
Sqlcmd: Error: Microsoft ODBC Driver 18 for SQL Server : Client unable to establish connection. For solutions related to encryption errors, see https://go.microsoft.com/fwlink/?linkid=2226722.

With -N o (Encrypt=optional) the same command connected. The certificate that server presented (read with a short script that does the TLS handshake inside TDS pre-login packets, as SQL Server clients do, then openssl x509):

subject=CN=SSL_Self_Signed_Fallback
issuer=CN=SSL_Self_Signed_Fallback
notBefore=Oct  9 13:46:52 2026 GMT
notAfter=Oct  9 13:46:52 2056 GMT

The second test server forces encryption with a certificate for localhost signed by a private test CA. From its own container:

Sqlcmd: Error: Microsoft ODBC Driver 18 for SQL Server : SSL Provider: [error:0A000086:SSL routines::certificate verify failed:unable to get local issuer certificate].
Sqlcmd: Error: Microsoft ODBC Driver 18 for SQL Server : Client unable to establish connection. For solutions related to encryption errors, see https://go.microsoft.com/fwlink/?linkid=2226722.

-N o didn’t help there: the server forces encryption, so the driver still checked, with the same error. Then, from a separate temporary container: after adding the test CA with update-ca-certificates and connecting as host.docker.internal,14332, the CA was trusted but the name wasn’t in the certificate:

Sqlcmd: Error: Microsoft ODBC Driver 18 for SQL Server : SSL Provider: [error:0A000086:SSL routines::certificate verify failed:subject name does not match host name].

Adding -F localhost (HostNameInCertificate) connected, encrypted and verified (encrypt_option was TRUE in sys.dm_exec_connections). SQL Server 2019 and 2025 presented the same kind of self-signed certificate as 2022.

In Inlet

Inlet’s SQL Server connections have an encryption setting with these choices: off (the login is still encrypted when the server can), encrypt without checking the certificate, and verify the certificate (its CA and the host name); strict encryption (TDS 8.0) is an option too. When verification fails, Inlet says The server’s certificate couldn’t be verified: followed by OpenSSL’s reason, such as self-signed certificate or unable to get local issuer certificate, and links to this page. For a local Docker container, encrypting without checking is the practical choice; for servers you don’t control, keep verification on.

Related

Sources