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:
| Client | Message |
|---|---|
| Microsoft.Data.SqlClient, System.Data.SqlClient, SSMS | A 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 container | SSL Provider: [error:0A000086:SSL routines::certificate verify failed:self-signed certificate], or …:unable to get local issuer certificate |
| Inlet | The 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
Encryptto default totrue. - 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
- The server uses its self-signed certificate, and a newer driver now encrypts and checks it.
- The certificate comes from a private CA that this machine (or this container, or this .NET runtime on Linux) doesn’t trust.
- The server sends only its own certificate, not the intermediate CA that links it to a trusted root.
- 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-confat 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
- A network-related or instance-specific error occurred while establishing a connection to SQL Server
- Login failed for user (Microsoft SQL Server, Error: 18456)
- Connect to SQL Server in Docker from your Mac
- Connect to Azure SQL Database from your Mac
- Connect to Amazon RDS for SQL Server from your Mac
- SQL Server connection strings: ADO.NET, JDBC, ODBC and sqlserver:// URLs
- server does not support SSL, but SSL was required
Sources
- learn.microsoft.com/en-us/troubleshoot/sql/database-engine/connect/certificate-chain-not-trusted
- learn.microsoft.com/en-us/sql/connect/ado-net/sqlclient-troubleshooting-guide
- learn.microsoft.com/en-us/sql/connect/ado-net/introduction-microsoft-data-sqlclient-namespace
- learn.microsoft.com/en-us/sql/connect/ado-net/encryption-and-certificate-validation
- learn.microsoft.com/en-us/sql/connect/odbc/dsn-connection-string-attribute
- learn.microsoft.com/en-us/sql/database-engine/configure-windows/configure-sql-server-encryption
- learn.microsoft.com/en-us/sql/linux/sql-server-linux-encrypted-connections
- docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL.html