Skip to content

Apply a fix safely

pqdoctor fix prints the diff; --write writes it. --apply goes all the way, and undoes itself when it should:

Terminal window
sudo pqdoctor fix --config /etc/nginx/sites-enabled/default --apply --host www.example.org
  1. Shows the diff, the commands it will run and the undo, then asks you to type yes (--yes for scripts).
  2. Keeps the original as PATH.bak — an older backup is never overwritten.
  3. Writes the file (atomically: a temporary file, then a rename).
  4. Runs the stack’s config test (nginx -t, apachectl configtest, haproxy -c, …). If it fails, the original goes back and nothing is reloaded.
  5. Runs the reload (systemctl reload nginx, …).
  6. Re-checks --host from outside — every address — until it negotiates X25519MLKEM768, up to four tries 1.5 s apart (graceful reloads take a moment).
  7. If the reload failed, the site stopped answering, or it still isn’t post-quantum: puts the original back, re-tests, reloads, and checks the site answers again.

--test-cmd and --reload-cmd replace the stack’s commands (repeat them for several).

Outcome Means
applied Post-quantum now; the original is in the backup
partial This server is PQ, but other addresses behind the name aren’t — they’re different servers; apply the fix there too. The change is kept
test-failed The config test refused it; the original is back, nothing was reloaded
rolled-back Something after the write failed; the original is back, tested, reloaded and answering
rollback-failed Even putting it back failed. pqdoctor says so in red and prints the commands to restore by hand — it never claims success

This is why --apply is safe to try: if the TLS library has no ML-KEM, the server’s config test rejects the new group list —

✘ test sudo nginx -t: nginx: [emerg] SSL_CTX_set1_curves_list("X25519MLKEM768:X25519:prime256v1") failed
✔ restore original back in /etc/nginx/sites-enabled/default
Not applied: the config test rejected the change. The original is back; nothing was reloaded.

— and now you know it’s the library: a sidecar makes the server post-quantum without the upgrade.

The same code runs in an apply lab against real TLS servers and real config files: OpenSSL 3.6 with a pinned list (applied, post-quantum), OpenSSL 3.0 (refused by the config test, restored), a config that doesn’t control the TLS terminator (rolled back), a reload that fails on the new config (rolled back) and a reload that always fails (rollback-failed, file still restored, manual steps printed). In every case where the fix wasn’t kept, the file ends byte-identical to the original.