Skip to content
pbb.io/blog

OTP 26 changes SSL defaults and your Postgres connection might break

OTP 26 changes the default SSL verify option. This can break your Ecto Postgres connection if you rely on SSL without supplying CA certificates.

September 29, 2023
2 min read
Erlang Elixir

OTP 26 ships with safer SSL defaults. The default value for the verify option changed from :verify_none to :verify_peer. This means host verification is now enabled by default and requires trusted CA certificates to be supplied via cacerts or cacertsfile.

This is a good change for security, but it can catch you off guard when upgrading.

The problem

If you use SSL to connect to Postgres (mandatory on providers like Digital Ocean), the connection will simply fail after upgrading to OTP 26. You’ll see an error like this:

** (DBConnection.ConnectionError) connection not available and request was dropped
from queue after 15000ms. This means requests are coming in and there are no idle
connections, no ## connection processes are available to be started and/or checked out,
or the pool is otherwise not accepting requests

The underlying issue is that Postgrex passes your ssl_opts to Erlang’s :ssl module, which now requires CA certificates for peer verification.

The fix

You could set verify: :verify_none to restore the old behaviour, but that disables certificate verification entirely. Since OTP 25, :public_key.cacerts_get/0 can fetch the CA certificates from your operating system’s certificate store instead. It works on Linux, macOS and Windows without hardcoding any file paths. Add this to your runtime.exs:

config :myapp, MyApp.Repo,
# ...
ssl_opts: [
verify: :verify_peer,
cacerts: :public_key.cacerts_get()
]

This needs to be in runtime.exs (not config.exs or prod.exs) because :public_key.cacerts_get/0 is a runtime call. It should read the certificates from the machine your app runs on, not from your build machine.

If you’re running in a Docker container, make sure the ca-certificates package is installed in your image.

A note on Azure

This works with Azure Database for PostgreSQL out of the box. Azure uses certificates signed by publicly trusted root CAs (DigiCert Global Root G2, Microsoft RSA Root CA 2017), which are included in standard OS certificate stores.

If you’re connecting through Azure Private Endpoints with custom DNS, hostname verification might fail because the certificate’s common name doesn’t match your private DNS name. In that case you can set server_name_indication explicitly:

ssl_opts: [
verify: :verify_peer,
cacerts: :public_key.cacerts_get(),
server_name_indication: ~c"your-server.postgres.database.azure.com"
]

Back to posts

Erlang Elixir
Phil-Bastian Berndt

Phil-Bastian Berndt
Tech Lead at Naymspace