TL; DR: Not all Linux distros can run NGINX with PQC support. Read this article to learn which ones do, and how to do it yourself.
At DEF CON 33, Konstantinos Karagiannis argued that usable quantum capabilities could arrive much sooner than many expect. Regardless of the exact date, agencies such as NIST, NSA, and CISA advise beginning PQC migrations now to mitigate the “harvest now, decrypt later” (HNDL) risk.
OpenSSL is where the magic happens
OpenSSL is among the most well-known cryptography libraries and can be found in the most popular Linux distributions. A cryptographic library is like a box of certified security algorithms, handling signatures, encryption, and certificates. Software like NGINX plugs into OpenSSL to handle HTTPS encryption, allowing the NGINX engineers to focus on traffic management.
With the April 2025 release of OpenSSL 3.5, several NIST-approved PQC algorithms are enabled by default. This means that if you want PQC enabled in your open-source NGINX instance, you must ensure that NGINX has been compiled with a ≥3.5 version of OpenSSL.
What if my Linux distribution doesn’t have OpenSSL 3.5?
While OpenSSL 3.0.x used in several current distributions do not support PQC algorithms, you can still gain PQC support by using the OQS provider. See below for details.
Given that OpenSSL 3.5 was released earlier in 2025, the Linux distro landscape only has a few options containing a ≥3.5 version.
| Distribution/Release | OpenSSL Version Shipped | PQC Available? | Notes |
|---|---|---|---|
| Debian 13 “Trixie” | 3.5.x | ✅ | Current stable release |
| Debian 12 “Bookworm” | 3.0.x | ❌* | Current oldstable release – Workaround available |
| Alpine Linux 3.22 | 3.5.x | ✅ | Latest Alpine release |
| Alpine Linux 3.21 | 3.3.x | ❌ | Previous Alpine release |
| Ubuntu 22.04 (LTS) | 3.0.x | ❌* | Old LTS – Workaround available |
| Ubuntu 24.04 (LTS) | 3.0.x | ❌* | Current LTS – Workaround available |
| Alma/Rocky/RHEL 10.1 | 3.5.x | ✅ | Latest RedHat 10.1 family release |
| Alma/Rocky/RHEL 10 | 3.2.x* | ❌ | Initial RedHat 10 family release – Workaround available |
| Suse Linux Enterprise (SLES) 16 | 3.5.x | ✅ | Latest SLES release |
This affects NGINX whether you use the version supplied by the Linux distro, the F5 NGINX official packages, or the official Docker container images because each is built with the OpenSSL version available on that platform.
To confirm the OpenSSL version that NGINX was compiled with, use the nginx –V command:
Debian 12 Bookworm uses OpenSSL 3.0:
$ nginx -V
nginx version: nginx/1.29.3
built by gcc 12.2.0 (Debian 12.2.0-14+deb12u1)
built with OpenSSL 3.0.16 11 Feb 2025 (running with OpenSSL 3.0.17 1 Jul 2025)
TLS SNI support enabled
...
Debian 13 Trixie uses OpenSSL 3.5:
$ nginx -V
nginx version: nginx/1.29.3
built by gcc 14.2.0 (Debian 14.2.0-19)
built with OpenSSL 3.5.1 1 Jul 2025
TLS SNI support enabled
...
NGINX Kubernetes Solutions
F5 NGINX Ingress Controller
This discrepancy in PQC availability equally applies to the current open-source release of F5 NGINX Ingress Controller (5.2.1). The default image is based on Debian 12, which does not have PQC available – although Debian 13 support is expected in the upcoming release. However, choosing the Alpine image (based on Alpine 3.22) will enable PQC support immediately.
If you’ve migrated to open-source F5 NGINX Ingress due to the recent community Ingress NGINX retirement announcement, welcome! Deploying a PQC-ready F5 NGINX Ingress version can be achieved easily via Helm. It simply involves updating the image tag of the Helm values.yaml file.
# F5 NGINX Ingress values.yaml
# Select the Alpine based tag
controller:
image:
repository: nginx/nginx-ingress
tag: 5.2.1-alpine
F5 NGINX Gateway Fabric
The current release of F5 NGINX Gateway Fabric (2.2.1) is based on Alpine 3.22. If running the latest release, then you are already PQC-enabled.
Once you have an OpenSSL ≥3.5 NGINX version, you can validate that PQC-safe ciphers are enabled with a quick OpenSSL CLI test. Note that the success of this test requires a PQC enabled OpenSSL on the client and server.
# Instruct OpenSSL CLI to connect with a hybrid PQC cipher
$ openssl s_client -groups "X25519MLKEM768" -tls1_3 -connect community.f5.com:443 2>/dev/null
Negotiated TLS1.3 group: X25519MLKEM768
What you can do about this now
If you’re already running an Alpine, Debian or RHEL-compatible 10 release, simply update the latest current release and run the OpenSSL test above.
If you’re running Ubuntu 22.04, or 24.04, or Debian 12 you can add PQC support with the instructions below. Ubuntu 26.04 LTS is expected in April 2026, though I would advise testing with the Ubuntu 25.10 interim release today. Likewise, if you’re running Debian 12, I advise updating to Debian 13.
Important Limitations for OpenSSL 3.0.x
Before proceeding, these are the PQC limitations of patching OpenSSL 3.0.x with Liboqs
If you’re using OpenSSL 3.0.x or 3.1.x, be aware that post-quantum signature algorithms cannot be used for TLS 1.3 server authentication. This is a known limitation that was resolved in OpenSSL 3.2+.
What this means in the real world: In a TLS 1.3 handshake, there are two separate cryptographic operations. 1. Key Exchange (KEM) 2. Server Authentication (Signature).
Key exchange works fine with PQC, liboqs and OpenSSL 3.0.x. Server authentication; this is where the server proves it owns the private key used to sign the certificate. Here, PQC is not supported until OpenSSL 3.2+.
In our opinion it’s still worth adding the liboqs provider specifically to protect against “Harvest now, decrypt later” attacks.
Adding PQC support via OQS-Provider
For distributions running older 3.0.x versions of OpenSSL, you can add PQC support to NGINX by installing the liboqs and oqs-provider projects.
(The following instructions have been tested on Debian/Ubuntu variants)
Install the required build tools:
$ sudo apt install -y build-essential git cmake ninja-build libssl-dev
Clone the liboqs project:
$ git clone --branch main https://github.com/open-quantum-safe/liboqs.git
Build and install the liboqs project:
$ cd liboqs && mkdir build && cd build
$ cmake -GNinja -DCMAKE_INSTALL_PREFIX=/usr/local -DOQS_DIST_BUILD=ON ..
$ ninja
$ sudo ninja install
Clone the oqs-provider project:
$ cd ~
$ git clone --branch main <https://github.com/open-quantum-safe/oqs-provider.git>$ cd oqs-provider && mkdir build && cd build
$ cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local -DOPENSSL_ROOT_DIR=/usr/local/ssl ..
$ make -j$(nproc)
$ sudo make install
Create an OpenSSL configuration with the OQS-Provider:
$ sudo tee /etc/ssl/openssl-oqs.cnf > /dev/null <<'EOF'
openssl_conf = openssl_init
[openssl_init]
providers = provider_sect
[provider_sect]
default = default_sect
oqsprovider = oqsprovider_sect
[default_sect]
activate = 1
[oqsprovider_sect]
activate = 1
# For ARM based machines replace x86_64 with aarch64
module = /usr/lib/x86_64-linux-gnu/ossl-modules/oqsprovider.so
EOF
```
Here’s a sample NGINX configuration that explicitly sets a PQC curve:
server {
listen 443 ssl;
server_name localhost;
ssl_certificate /etc/ssl/private/localhost.crt;
ssl_certificate_key /etc/ssl/private/localhost.key;
ssl_protocols TLSv1.3;
ssl_ecdh_curve X25519MLKEM768:X25519:prime256v1; # PQC + classical curves
location / {
root /var/www/html;
index index.html;
}
}
Using the sample configuration, you’ll notice the NGINX config test fail without specifying the OQS-Provider.
This test fails without the OQS OpenSSL configuration:
$ sudo nginx –t
nginx: [emerg] SSL_CTX_set1_curves_list("X25519MLKEM768:X25519:prime256v1") failed
nginx: configuration file /etc/nginx/nginx.conf test failed
This test passes with OpenSSL environment set to the OQS-Provider configuration:
$ sudo OPENSSL_CONF=/etc/ssl/openssl-oqs.cnf nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Setting the OPENSSL_CONF environment variable
By having a distinct openssl-oqs.cnf we can selectively choose which applications we’d like to support PQC. For NGINX it means setting OPENSSL_CONF. Systemd, docker and Kubernetes all have means to set an environment variable. Please refer to the documentation for whichever management tool you use.

