| Tested on | Debian 12 (bookworm), Rocky Linux 9 |
|---|---|
| Package | Debian: ca-certificates 20250419~deb12u1, openssl 3.0.20-1~deb12u2, curl 7.88.1-10+deb12u15, p11-kit 0.24.1-2Rocky: ca-certificates 2025.2.80_v9.0.305-91.el9, openssl 3.5.5-6.el9_8, curl 7.76.1-40.el9_8.5, p11-kit 0.26.4-1.el9_8 |
| Applies to | Ubuntu, Debian, Linux Mint, Pop!_OS, Raspberry Pi OS, elementary OS, Zorin OS, MX Linux, RHEL, Rocky Linux, AlmaLinux, Oracle Linux, CentOS Stream, Fedora, openSUSE, SUSE Linux Enterprise, Arch Linux, Alpine |
| Privilege | sudo or root |
| Scope | What the OS trust store is, where each distribution family keeps its CA source files, installing and verifying a CA anchor, removing one again, and the applications that keep a private trust store instead. Does not cover running a production CA or issuing server certificates. |
| Related guides | OpenSSL command cheat sheet Use a custom Java truststore instead of cacerts |
You have a CA certificate file. Maybe it came from your security team, maybe from an internal PKI, maybe you generated it yourself. Every tool on the box still refuses HTTPS endpoints that chain to it, and you want that to stop.
The fix is always the same shape: copy the root CA into the directory your distribution reads, run one refresh command, then confirm the anchor landed. Debian and Ubuntu use update-ca-certificates. Rocky Linux, RHEL, AlmaLinux, and Fedora use update-ca-trust extract. The paths differ; the idea does not.
How the Linux system trust store is put together
There is no single file called "the trust store". What you have is a small pipeline with three stages, and understanding the stages is what stops you from editing the wrong file.
- Source directories hold the individual CA certificates. You own one of them; your package manager owns the other.
- A refresh command reads those directories and regenerates everything downstream.
- Generated bundles are what applications open at runtime. They are disposable output, rebuilt from scratch on every refresh.
The mistake that costs people an afternoon is editing a generated bundle. Append your CA to /etc/ssl/certs/ca-certificates.crt on Debian or /etc/pki/tls/certs/ca-bundle.crt on Rocky by hand and the next refresh quietly discards your change. Put the certificate in the source directory instead and let the tool do the concatenating.
Some applications read a CApath directory of hash-named symlinks rather than the big bundle file. That distinction matters when you verify your work on Debian; the RHEL family rebuilds its bundles wholesale through p11-kit.
Trust store paths for every distribution family
Two families dominate, and they use different tools with different rules. Two smaller families are worth knowing because their paths look almost right but are not.
| Family | Drop the CA here | Then run |
|---|---|---|
| Debian, Ubuntu, Mint, Raspberry Pi OS | /usr/local/share/ca-certificates/name.crt |
update-ca-certificates |
| RHEL, Rocky Linux, AlmaLinux, Oracle Linux, CentOS Stream, Fedora, Amazon Linux | /etc/pki/ca-trust/source/anchors/ |
update-ca-trust extract |
| openSUSE, SUSE Linux Enterprise | /etc/pki/trust/anchors/name.crt |
update-ca-certificates |
| Arch, Manjaro, SteamOS | /etc/ca-certificates/trust-source/anchors/name.crt |
trust extract-compat |
| Alpine | /usr/local/share/ca-certificates/name.crt |
update-ca-certificates after apk add ca-certificates |
SUSE reuses the Debian command name with an RPM-style path, which is exactly the combination people guess wrong. Arch and RHEL both run on p11-kit, so trust list works on either.
The file format trap that silently swallows your certificate
Debian is strict in a way that produces no error message, which is why "I ran the command and nothing happened" is the most common complaint about update-ca-certificates. The man page states the rule in one sentence:
Certificates must be in PEM format and have a .crt extension in order to be included
by update-ca-certificates. Furthermore, all certificates with a .crt extension found
below /usr/local/share/ca-certificates are also included and implicitly trusted.Two conditions, both silent when unmet. A DER-encoded file renamed to .crt is skipped. A perfectly valid PEM file named ca.pem, ca.cer, or rootCA.crt.txt is skipped. And the same man page adds a third rule people miss: there should be one certificate per file, not a bundle.
The RHEL family is relaxed about all three. The anchors directory accepts any filename, reads PEM or DER, and handles a file holding several certificates. If you write automation that has to cover both families, code to the Debian rules and RHEL will be happy too:
| Requirement | Debian and Ubuntu | RHEL and Fedora |
|---|---|---|
| Filename extension | must be .crt |
any |
| Encoding | PEM only | PEM or DER |
| Certificates per file | one | several allowed |
| Failure mode when wrong | silent skip | silent skip |
Convert DER to PEM before you copy anything if you are not sure which you have. The file command guesses correctly most of the time, and openssl x509 fails loudly when the encoding is wrong, which is the more reliable test.
The OpenSSL certificate inspection guide
shows how to identify and decode PEM, DER, CER, bundles, and remote
certificate chains before importing an anchor.
Until the root CA is trusted, curl against an internal HTTPS endpoint exits with code 60 and SSL certificate problem: unable to get local issuer certificate, as described in the curl command reference. openssl verify against the leaf reports error 20 at depth 0. Those errors mean the chain is fine and the anchor is missing from your store, which is what the steps below fix.
Debian and Ubuntu
On Debian 12 the pipeline looks like this:
| Stage | Path | Who owns it |
|---|---|---|
| Local source | /usr/local/share/ca-certificates/ |
you, the administrator |
| Package source | /usr/share/ca-certificates/ |
the ca-certificates package, selected through /etc/ca-certificates.conf |
| Generated bundle | /etc/ssl/certs/ca-certificates.crt |
update-ca-certificates |
| Generated hash directory | /etc/ssl/certs/ |
update-ca-certificates |
Install the CA
Copy your PEM root CA into the local source directory. Debian only picks up files whose names end in .crt, so a file named ca.pem or root.pem is ignored with no error. The trap below is worth seeing once because the refresh command exits 0 either way.
First, the wrong extension on purpose:
sudo cp example-root-ca.crt /usr/local/share/ca-certificates/example-root-ca.pemRefresh the store and read the counter:
sudo update-ca-certificatesUpdating certificates in /etc/ssl/certs...
0 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.Zero added, zero removed, exit status 0, no warning about the file it ignored. That counter is your only signal. Remove the misnamed file and copy the same certificate again with a .crt name:
sudo rm /usr/local/share/ca-certificates/example-root-ca.pem
sudo cp example-root-ca.crt /usr/local/share/ca-certificates/example-root-ca.crtRun the refresh command again:
sudo update-ca-certificatesUpdating certificates in /etc/ssl/certs...
rehash: warning: skipping ca-certificates.crt,it does not contain exactly one certificate or CRL
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.One added is the line you were missing a moment ago. The rehash warning about ca-certificates.crt appears on every run and is not a problem.
Retry the HTTPS call that failed before. On Debian 12, curl reads /etc/ssl/certs/ca-certificates.crt as CAfile and /etc/ssl/certs as CApath:
curl -sS https://internal.example.com/ -o /dev/null -w 'HTTP %{http_code} ssl_verify_result=%{ssl_verify_result}\n'HTTP 200 ssl_verify_result=0A verify result of 0 means the full chain validated against the system store.
Verify the anchor landed
"curl works now" is a fine smoke test and a poor answer during an incident. Four checks cover the four ways an application can look for a trust anchor on Debian, and each one can pass or fail independently.
The concatenated bundle is the first place to look, but grepping it for a subject name does not work because PEM bodies are base64. Pipe each certificate through openssl x509 instead:
awk -v cmd='openssl x509 -noout -subject' '/BEGIN CERTIFICATE/{close(cmd)} {print | cmd}' /etc/ssl/certs/ca-certificates.crt | grep -i examplesubject=C = IN, O = Example Corp, CN = Example Corp Root CAThat is the CA sitting inside the bundle that curl, wget, git, and most Go binaries open by default. The bundle on Debian 12 held 150 public roots before the custom CA went in and 151 afterwards:
grep -c 'BEGIN CERTIFICATE' /etc/ssl/certs/ca-certificates.crt151Next comes the symlink farm, and its two-hop structure shows where your file ends up:
ls -l /etc/ssl/certs | grep -i examplelrwxrwxrwx 1 root root 23 Aug 31 05:20 fc2aa687.0 -> example-root-ca.pem
lrwxrwxrwx 1 root root 50 Aug 31 05:20 example-root-ca.pem -> /usr/local/share/ca-certificates/example-root-ca.crtYour .crt source file gained a .pem-named symlink, and that symlink in turn gained a second link named fc2aa687.0. The number is the subject hash OpenSSL uses for CApath lookups:
openssl x509 -in example-root-ca.crt -noout -subject_hashfc2aa687Prove that path works rather than assuming it:
openssl verify -CApath /etc/ssl/certs server.crtserver.crt: OKLast comes trust list, which reads the same store through the PKCS#11 view that NSS-based software uses:
trust list | grep -B2 -A3 -i examplepkcs11:id=%BD%42%0E%02%5B%E9%D4%09%63%9D%A5%69%90%26%5D%EB%A5%D9%01%D3;type=cert
type: certificate
label: Example Corp Root CA
trust: anchor
category: authoritytrust: anchor is the line that matters. A certificate that is merely known to the system, rather than trusted, shows a different trust value.
One certificate per file
The Debian man page asks for one certificate per file and does not explain the consequence. If you are handed a chain file, split it before copying. Concatenating two roots into one file shows the failure mode:
The OpenSSL certificate-chain guide
explains the roles and ordering of root, intermediate, leaf, ca-chain.pem,
and fullchain.pem files.
cat example-root-ca.crt example-intermediate.crt > double-ca.crt
sudo cp double-ca.crt /usr/local/share/ca-certificates/double-ca.crt
sudo update-ca-certificatesUpdating certificates in /etc/ssl/certs...
rehash: warning: skipping ca-certificates.crt, it does not contain exactly one certificate or CRL
rehash: warning: skipping double-ca.pem, it does not contain exactly one certificate or CRL
1 added, 0 removed; done.The certificates do reach the concatenated bundle, so curl and anything else reading CAfile is satisfied. But double-ca.pem was skipped during hashing, so it never gets a hash symlink, and every CApath or SSL_CERT_DIR consumer will fail to find it.
Remove a CA
Delete the file from the source directory. Never delete from /etc/ssl/certs, since those entries are regenerated and your edit will not survive:
sudo rm /usr/local/share/ca-certificates/example-root-ca.crtRefresh and read the counter, which is going to look like nothing happened:
sudo update-ca-certificatesUpdating certificates in /etc/ssl/certs...
0 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.Zero removed, and yet the removal did work where it counts. The bundle is rebuilt from scratch on every run, so the CA is already gone from it:
grep -c 'BEGIN CERTIFICATE' /etc/ssl/certs/ca-certificates.crt150Back to 150, and trust list no longer returns a match either. The counter reports selections from /etc/ca-certificates.conf, not files you deleted from the local directory, which is why it stayed at zero. What did not get cleaned up is the symlink farm:
ls -l /etc/ssl/certs | grep -i examplelrwxrwxrwx 1 root root 23 Aug 31 05:20 fc2aa687.0 -> example-root-ca.pem
lrwxrwxrwx 1 root root 50 Aug 31 05:20 example-root-ca.pem -> /usr/local/share/ca-certificates/example-root-ca.crtBoth links point at a file that no longer exists. That is what --fresh is for:
sudo update-ca-certificates --freshClearing symlinks in /etc/ssl/certs...
done.
Updating certificates in /etc/ssl/certs...
rehash: warning: skipping ca-certificates.crt,it does not contain exactly one certificate or CRL
150 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.Every remaining anchor is reported as added because the directory was emptied first, and the dangling links are gone. There is no --force option on Debian despite what older forum answers suggest; -f is simply the short form of --fresh:
update-ca-certificates --help/usr/sbin/update-ca-certificates: [--verbose] [--fresh]RHEL, Rocky Linux, and Fedora
On Rocky Linux 9 tools open /etc/pki/tls/certs/ca-bundle.crt, a symlink into /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem. The anchors you add live under /etc/pki/ca-trust/source/anchors/.
Install the CA
The anchors directory accepts any filename and any common encoding. Copy the root CA in:
sudo cp example-root-ca.crt /etc/pki/ca-trust/source/anchors/example-root-ca.pemRebuild the extracted bundles. The extract subcommand is the one you want; a bare update-ca-trust does the same thing on current releases:
sudo update-ca-trust extractThe command is silent on success, which trips up anyone used to the Debian counter. p11-kit regenerates several bundles under /etc/pki/ca-trust/extracted/ for OpenSSL, NSS, Java, and firmware consumers:
ls -l /etc/pki/tls/certs/ca-bundle.crtlrwxrwxrwx 1 root root 49 Nov 21 2025 /etc/pki/tls/certs/ca-bundle.crt -> /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pemRetry the HTTPS request that failed before installation:
curl -sS https://internal.example.com/ -o /dev/null -w 'HTTP %{http_code} ssl_verify_result=%{ssl_verify_result}\n'HTTP 200 ssl_verify_result=0Verify the anchor landed
Confirm the anchor is in the OpenSSL bundle p11-kit extracted:
grep -i example /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem# Example Corp Root CAThe public bundle held 146 roots before the custom CA went in and 147 afterwards:
grep -c 'BEGIN CERTIFICATE' /etc/pki/tls/certs/ca-bundle.crt147And that trust list shows it as an anchor, not merely as a known certificate:
trust list | grep -B2 -A3 -i examplepkcs11:id=%7D%93%94%AC%84%CC%95%03%F9%9E%F2%CF%02%51%5A%70%B4%4B%EC%82;type=cert
type: certificate
label: Example Corp Root CA
trust: anchor
category: authorityProve the leaf verifies against the system bundle:
openssl verify -CAfile /etc/pki/tls/certs/ca-bundle.crt server.crtserver.crt: OKRemove or blocklist a CA
Delete the file from the anchors directory and rebuild:
sudo rm /etc/pki/ca-trust/source/anchors/example-root-ca.pem
sudo update-ca-trust extractThe bundle count drops back to 146, and curl fails again with error 60. There is no stale symlink problem on RHEL because the extractor rewrites its output bundles wholesale.
The RHEL family also gives you something Debian does not: a directory for certificates you want actively distrusted. Dropping a CA in /etc/pki/ca-trust/source/blocklist/ and running update-ca-trust extract marks it rejected for every purpose. On RHEL 8 and earlier that directory is named blacklist.
The applications that ignore the system trust store
This is where most "the command did not work" reports come from. The system store was updated correctly and one specific application still refuses the certificate, because that application never looked at the system store in the first place.
Python: certifi decides, not the OS
Anything built on requests verifies against the CA bundle shipped inside the Python environment. Some Debian and Ubuntu packages patch python3-certifi to follow the system bundle, but a virtual environment created with pip install requests does not:
python3 -m venv ~/venv-test && ~/venv-test/bin/pip -q install requestsAsk that environment which bundle it will verify against:
~/venv-test/bin/python -c "import certifi; print('certifi bundle:', certifi.where())"certifi bundle: /home/user/venv-test/lib/python3.11/site-packages/certifi/cacert.pemThat is a Mozilla-derived bundle inside the venv with no knowledge of your CA. With the CA trusted system-wide and curl returning HTTP 200, the same request from that venv fails:
~/venv-test/bin/python -c "import requests; requests.get('https://internal.example.com/', timeout=10)"requests.exceptions.SSLError: HTTPSConnectionPool(host='internal.example.com', port=443):
Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1,
'[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate
in certificate chain (_ssl.c:...)')))Point REQUESTS_CA_BUNDLE at the system bundle and the identical call succeeds:
REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt ~/venv-test/bin/python -c "import requests; print('status', requests.get('https://internal.example.com/', timeout=10).status_code)"status 200Nothing in the trust store changed between those two runs, only which bundle the library was told to read. SSL_CERT_FILE works the same way for code using ssl directly, and installing the truststore package makes Python read the OS store natively, which is the cleaner fix for a long-lived application.
Java: a separate keystore, sometimes kept in sync for you
The JDK verifies against cacerts, a keystore in its own format. Debian ships a jks-keystore hook that syncs the system store into /etc/ssl/certs/java/cacerts when ca-certificates-java is installed. Do not assume that hook exists everywhere. A JDK you unpacked from Temurin or Oracle into /opt, or a JDK inside a container image, has a cacerts that no system tool touches, and you import into it explicitly with keytool -importcert. That workflow is covered in import a certificate into Java cacerts.
Node.js: depends on who built your binary
Node normally verifies against a Mozilla list compiled into the binary, which is why NODE_EXTRA_CA_CERTS exists. Some distribution packages are built against the system OpenSSL default store instead, so the compiled-in list and the store actually used during TLS can disagree. Official binaries from nodejs.org and the node:* container images do not follow the OS store by default. For those, set the environment variable to a PEM file containing the CA, or pass --use-system-ca on Node 22.15 and newer:
export NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crtOn Rocky Linux use /etc/pki/tls/certs/ca-bundle.crt instead. Test with node -p "process.version" and a real request before you commit that export to a Dockerfile, since the difference between packaged and official builds is easy to get wrong.
Firefox and other NSS software
Firefox keeps its trust decisions in an NSS database inside the profile directory, so a system CA is invisible to it by default. Import through Settings, Privacy & Security, View Certificates, Authorities, Import, or make the whole class of NSS applications follow the system store by installing p11-kit-modules and replacing libnssckbi.so with a link to p11-kit-trust.so. Chrome and Chromium on Linux read the NSS user database too, which you edit with certutil -d sql:$HOME/.pki/nssdb.
Docker daemon: per-registry, not the system store
Pulling from a registry with a private CA is the one case where the system store is not enough even on the host. The daemon looks for /etc/docker/certs.d/<registry-host>:<port>/ca.crt, with the port included in the directory name when the registry uses one, and it needs a restart afterwards. Trusting the CA system-wide fixes curl https://registry.internal.lab while docker pull keeps failing, which is a confusing pair of symptoms until you know about that directory.
Trust a CA inside a container image
A container image carries its own /etc/ssl/certs. Installing a CA on the Docker or Podman host changes nothing inside a running container, and mounting the host bundle over the image's one breaks as soon as the base image expects a different path.
Rebuild the trust store during the image build. On a Debian or Ubuntu base the two lines are the same ones you ran on the host:
FROM debian:trixie-slim
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates \
&& rm -rf /var/lib/apt/lists/*
COPY example-root-ca.crt /usr/local/share/ca-certificates/example-root-ca.crt
RUN update-ca-certificatesThe ca-certificates install is not optional padding. Slim and Alpine images frequently ship without the package, which means no update-ca-certificates binary and no public roots either, and the resulting error looks identical to a missing custom CA. Alpine needs apk add ca-certificates for the same reason, and a RHEL UBI base uses microdnf install -y ca-certificates with update-ca-trust extract.
Scratch and distroless images have no package manager at all. There the only option is to copy a complete bundle to the path your runtime expects, usually /etc/ssl/certs/ca-certificates.crt, from a builder stage that had the CA installed. Multi-stage builds that run npm install or pip install need the CA in the builder stage too, because that is where the failing HTTPS request happens.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
update-ca-certificates prints 0 added |
File does not end in .crt, or is DER rather than PEM |
Rename to .crt; convert with openssl x509 -inform DER -in ca.der -out ca.crt |
Certificate is in the bundle but a CApath lookup fails |
Several certificates in one file, so no hash symlink was created | Split into one certificate per file and re-run the refresh |
curl succeeds, Python or Node still fails |
Application uses its own bundle, not the OS store | Set REQUESTS_CA_BUNDLE, SSL_CERT_FILE, or NODE_EXTRA_CA_CERTS |
Removed the file but /etc/ssl/certs still lists it |
Plain refresh does not prune symlinks | Run sudo update-ca-certificates --fresh |
| Verification fails with error 19 rather than 20 | Server is sending the root itself and it is untrusted | Trust the root; error 19 and 20 both mean the anchor is missing |
| Anchor is trusted but a service still rejects the certificate | Process cached its SSL context at startup | Restart the service |
trust list shows the CA but verification fails |
Certificate is a leaf or intermediate, not the root | Install the root CA that signed the chain |
docker pull fails after the host is fixed |
Daemon uses per-registry certificates | Add /etc/docker/certs.d/<host>:<port>/ca.crt and restart Docker |
| Nothing HTTPS works, including public sites | ca-certificates missing, or system clock wrong |
Install ca-certificates; check timedatectl before trusting new roots |
openssl verify says self-signed certificate for your own root |
Root passed as the file to verify, not as the anchor | Verify the leaf, or pass the root with -CAfile |
Summary
Trusting a CA on Linux is two steps that look trivial and one habit that saves you hours. The two steps are copying the certificate into the source directory your distribution reads and running the refresh command: /usr/local/share/ca-certificates/ with update-ca-certificates on Debian and Ubuntu, /etc/pki/ca-trust/source/anchors/ with update-ca-trust extract on RHEL, Rocky Linux, and Fedora. SUSE and Arch have their own paths that look close enough to the others to catch you out. The habit is never editing a generated bundle, because /etc/ssl/certs/ca-certificates.crt and everything under /etc/pki/ca-trust/extracted/ is output that gets overwritten.
The failure that wastes the most time is the silent one. On Debian, update-ca-certificates reads only PEM files ending in .crt, one certificate per file, and reports 0 added with exit status 0 when your file does not qualify. That is why the Debian verification pass checks four things rather than one: the subject inside the concatenated bundle, the hash symlink that CApath consumers depend on, an actual openssl verify run, and trust list showing trust: anchor. On Rocky, grep the extracted bundle, confirm the count rose by one, and run openssl verify -CAfile against the system bundle.
When one application still refuses the certificate after all four checks pass, stop looking at the trust store. Python's requests reads its own certifi bundle, the JDK reads cacerts, Firefox reads an NSS database in the profile, and the Docker daemon wants a per-registry file under /etc/docker/certs.d/. Node.js is the awkward one, because a distribution-packaged binary may honor the system store while the official build from nodejs.org does not. Containers are the same problem in a different shape: rebuild the trust store inside the image, and install ca-certificates first on slim and Alpine bases where the package is absent.
If you are rolling out an internal PKI, decide up front how the CA will reach every host, whether that is a configuration management module, a package your team builds, or a base image layer. The commands here are the easy part; distributing and eventually rotating the anchor is the work.
References
- update-ca-certificates(8) manual — Debian rules for
.crt, PEM, one certificate per file, and--fresh - Ubuntu Server: install a root CA certificate in the trust store — official Ubuntu workflow
- RHEL: using shared system certificates — anchors, blocklist, and
update-ca-trust extracton the RHEL family - Fedora: using shared system certificates — the same trust model with Fedora paths
- p11-kit trust manual — upstream reference for
trust list,trust anchor, andtrust extract-compat - trust(1) on Arch Linux — Arch anchors directory and
extract-compatbehavior - curl TLS certificate verification —
CURL_CA_BUNDLE,SSL_CERT_FILE, andSSL_CERT_DIR - openssl-verify(1) manual — verification error numbers including 19, 20, and 21
- Node.js CLI options —
NODE_EXTRA_CA_CERTSand--use-system-ca - Python truststore documentation — reading the OS trust store from Python instead of certifi
Frequently Asked Questions
Where do I put a CA certificate on Ubuntu or Debian?
Copy the PEM file into /usr/local/share/ca-certificates/ with a .crt extension and run update-ca-certificates. That directory is for local administrator additions; /usr/share/ca-certificates/ belongs to the ca-certificates package and is driven by /etc/ca-certificates.conf.
Why did update-ca-certificates report 0 added when my file is clearly there?
The Debian tool only reads files below /usr/local/share/ca-certificates that end in .crt and contain PEM text. A file named ca.pem, ca.cer, ca.der, or a DER-encoded file renamed to .crt is skipped without any error message, and the counter stays at 0 added.
What is the difference between update-ca-certificates and update-ca-trust?
They are two different implementations of the same idea. update-ca-certificates ships with the Debian and SUSE ca-certificates packages and regenerates /etc/ssl/certs plus the concatenated bundle. update-ca-trust is the p11-kit based tool on RHEL, Fedora, Rocky Linux, AlmaLinux, and Arch, and it regenerates several extracted bundles under /etc/pki/ca-trust/extracted for OpenSSL, NSS, Java, and EDK2.
Does the certificate file extension matter on RHEL and Fedora?
No. The anchors directory under /etc/pki/ca-trust/source/anchors accepts any filename and reads both PEM and DER encoding, and a single file may hold several certificates. Debian is the strict one: one PEM certificate per file, filename ending in .crt.
How do I remove a CA certificate I installed earlier?
Delete the file you added from the source directory and re-run the refresh command. On Debian use update-ca-certificates --fresh so stale symlinks in /etc/ssl/certs are cleared as well, because a plain run leaves dangling links behind. On RHEL delete the file from the anchors directory and run update-ca-trust extract.
Why does curl work after I trust the CA but Python still fails?
The requests library verifies against the certifi bundle shipped inside the Python environment, not the OS trust store. Point REQUESTS_CA_BUNDLE or the verify argument at the system bundle, or install the truststore package so Python reads the OS store directly.
Do I need to restart services after updating the trust store?
Yes for long running processes. OpenSSL reads the CA bundle when a process builds its SSL context, so an already running web server, agent, or application keeps the old trust decisions in memory until you restart it.
Does trusting a CA on the host also fix my containers?
No. Every container image carries its own /etc/ssl/certs tree, so the CA has to be copied into the image and the refresh command run during the build. Slim and Alpine base images often have no ca-certificates package at all, so install it first.

