Install and Trust a CA Certificate in the Linux System Trust Store

Deepak Prasad
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-2
Rocky: 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
Short on time?Beta Get the gist of this 20-minute read in seconds, then ask follow-up questions

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.

IMPORTANT
This guide is about the operating system trust store: the directories your distribution reads, the command that rebuilds the bundles, and how to prove the anchor landed. Generating the CA itself is a separate job covered in create a self-signed CA certificate with OpenSSL. If your certificate errors come from a corporate SSL inspection proxy rather than your own CA, the diagnosis path in fix the curl SSL certificate problem behind a corporate proxy is the closer match.

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:

text
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:

bash
sudo cp example-root-ca.crt /usr/local/share/ca-certificates/example-root-ca.pem

Refresh the store and read the counter:

bash
sudo update-ca-certificates
output
Updating 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:

bash
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.crt

Run the refresh command again:

bash
sudo update-ca-certificates
output
Updating 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:

bash
curl -sS https://internal.example.com/ -o /dev/null -w 'HTTP %{http_code}  ssl_verify_result=%{ssl_verify_result}\n'
output
HTTP 200  ssl_verify_result=0

A 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:

bash
awk -v cmd='openssl x509 -noout -subject' '/BEGIN CERTIFICATE/{close(cmd)} {print | cmd}' /etc/ssl/certs/ca-certificates.crt | grep -i example
output
subject=C = IN, O = Example Corp, CN = Example Corp Root CA

That 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:

bash
grep -c 'BEGIN CERTIFICATE' /etc/ssl/certs/ca-certificates.crt
output
151

Next comes the symlink farm, and its two-hop structure shows where your file ends up:

bash
ls -l /etc/ssl/certs | grep -i example
output
lrwxrwxrwx 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.crt

Your .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:

bash
openssl x509 -in example-root-ca.crt -noout -subject_hash
output
fc2aa687

Prove that path works rather than assuming it:

bash
openssl verify -CApath /etc/ssl/certs server.crt
output
server.crt: OK

Last comes trust list, which reads the same store through the PKCS#11 view that NSS-based software uses:

bash
trust list | grep -B2 -A3 -i example
output
pkcs11: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: authority

trust: 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.

bash
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-certificates
output
Updating 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:

bash
sudo rm /usr/local/share/ca-certificates/example-root-ca.crt

Refresh and read the counter, which is going to look like nothing happened:

bash
sudo update-ca-certificates
output
Updating 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:

bash
grep -c 'BEGIN CERTIFICATE' /etc/ssl/certs/ca-certificates.crt
output
150

Back 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:

bash
ls -l /etc/ssl/certs | grep -i example
output
lrwxrwxrwx 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.crt

Both links point at a file that no longer exists. That is what --fresh is for:

bash
sudo update-ca-certificates --fresh
output
Clearing 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:

bash
update-ca-certificates --help
output
/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:

bash
sudo cp example-root-ca.crt /etc/pki/ca-trust/source/anchors/example-root-ca.pem

Rebuild the extracted bundles. The extract subcommand is the one you want; a bare update-ca-trust does the same thing on current releases:

bash
sudo update-ca-trust extract

The 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:

bash
ls -l /etc/pki/tls/certs/ca-bundle.crt
output
lrwxrwxrwx 1 root root 49 Nov 21  2025 /etc/pki/tls/certs/ca-bundle.crt -> /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem

Retry the HTTPS request that failed before installation:

bash
curl -sS https://internal.example.com/ -o /dev/null -w 'HTTP %{http_code}  ssl_verify_result=%{ssl_verify_result}\n'
output
HTTP 200  ssl_verify_result=0

Verify the anchor landed

Confirm the anchor is in the OpenSSL bundle p11-kit extracted:

bash
grep -i example /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
output
# Example Corp Root CA

The public bundle held 146 roots before the custom CA went in and 147 afterwards:

bash
grep -c 'BEGIN CERTIFICATE' /etc/pki/tls/certs/ca-bundle.crt
output
147

And that trust list shows it as an anchor, not merely as a known certificate:

bash
trust list | grep -B2 -A3 -i example
output
pkcs11: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: authority

Prove the leaf verifies against the system bundle:

bash
openssl verify -CAfile /etc/pki/tls/certs/ca-bundle.crt server.crt
output
server.crt: OK

Remove or blocklist a CA

Delete the file from the anchors directory and rebuild:

bash
sudo rm /etc/pki/ca-trust/source/anchors/example-root-ca.pem
sudo update-ca-trust extract

The 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.

WARNING
Long running processes read the CA bundle when they build their SSL context, not on every connection. A web server, message broker, or monitoring agent that was already running keeps its old trust decisions until you restart it, so a removal is not effective across a host until those services are restarted.

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:

bash
python3 -m venv ~/venv-test && ~/venv-test/bin/pip -q install requests

Ask that environment which bundle it will verify against:

bash
~/venv-test/bin/python -c "import certifi; print('certifi bundle:', certifi.where())"
output
certifi bundle: /home/user/venv-test/lib/python3.11/site-packages/certifi/cacert.pem

That 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:

bash
~/venv-test/bin/python -c "import requests; requests.get('https://internal.example.com/', timeout=10)"
output
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:

bash
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)"
output
status 200

Nothing 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:

bash
export NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt

On 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:

dockerfile
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-certificates

The 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


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.

Finished reading?Beta Get the key takeaways in seconds, then ask follow-up questions
Omer Cakmak

Linux Administrator

Highly skilled at managing Debian, Ubuntu, CentOS, Oracle Linux, and Red Hat servers. Proficient in bash scripting, Ansible, and AWX central server management, he handles server operations on OpenStack, KVM, Proxmox, and VMware.

  • Debian
  • Ubuntu
  • Linux
  • Red Hat Enterprise Linux
  • Shell Script
  • System Administration