Fix Podman `x509: certificate signed by unknown authority`

Tested on Red Hat Enterprise Linux 10.2 (Coughlan)
Package podman-5.8.2-5.el10_2.x86_64
Applies to Any Linux host with Podman pulling from a TLS registry — private registry, mirror, or corporate proxy endpoint
Privilege [sudo](/sudo-command-in-linux/) for system-wide /etc/containers/certs.d/; rootless users can add registry-specific trust under ~/.config/containers/certs.d/ and may also use system paths when no conflicting user directory exists
Scope TLS trust failures — x509: certificate signed by unknown authority, hostname SAN mismatch, http: server gave HTTP response to HTTPS client, certs.d CA layout, .crt versus .cert, debug pulls, --tls-verify=false as a diagnostic only, corporate MITM CA trust, rootful versus rootless cert paths, and mirror endpoint identification. Does not cover private registry deployment, full PKI design, login mechanics, or image signature policy.
Related guides Log in to a container registry
Pull images with podman pull
Push images to a registry

Podman reached the registry host, but TLS verification failed before any image layers downloaded. Match the error string you see to the layer that broke:

  • Untrusted CAx509: certificate signed by unknown authority
  • Hostname mismatch — certificate valid for a different name than you connected to
  • Protocol mismatch — plain HTTP registry answering an HTTPS client

This guide maps each case to the configuration change that belongs on a production host.

IMPORTANT
This article fixes TLS trust and protocol mismatches for registry pulls and logins. It does not walk through standing up a private registry — see Run a private container registry for the HTTPS lab that produces the certificates referenced here.

x509: certificate signed by unknown authority

Pull from a TLS registry whose CA is not in Podman's trust store. On this lab host the HTTPS registry listens on localhost:5443 with a self-signed lab CA not yet installed:

bash
podman pull localhost:5443/ubi-minimal:latest

Sample output:

output
Trying to pull localhost:5443/ubi-minimal:latest...
Error: unable to copy from source docker://localhost:5443/ubi-minimal:latest: initializing source docker://localhost:5443/ubi-minimal:latest: pinging container registry localhost:5443: Get "https://localhost:5443/v2/": tls: failed to verify certificate: x509: certificate signed by unknown authority

The same wording appears on podman login before credentials are checked:

bash
podman login --username testuser --password-stdin localhost:5443

Sample output when the CA is still missing:

output
Error: authenticating creds for "localhost:5443": pinging container registry localhost:5443: Get "https://localhost:5443/v2/": tls: failed to verify certificate: x509: certificate signed by unknown authority

Podman never got past the registry /v2/ ping — the failure is certificate trust, not authentication or a missing image tag.


First identify which registry actually failed

Mirrors can make the registry endpoint differ from the hostname in your original image reference. Authentication can also introduce a separate token-service hostname, so first identify whether TLS failed while contacting the registry/mirror or while obtaining an authentication token. Raise log level and pull again:

bash
podman --log-level=debug pull localhost:5443/ubi-minimal:latest 2>&1 | grep -iE 'Trying to access|GET https|Ping https|x509|certs.d'

Sample output:

output
Trying to pull localhost:5443/ubi-minimal:latest...
time="..." level=debug msg="Trying to access \"localhost:5443/ubi-minimal:latest\""
time="..." level=debug msg="Looking for TLS certificates and private keys in /etc/docker/certs.d/localhost:5443"
time="..." level=debug msg="GET https://localhost:5443/v2/"
time="..." level=debug msg="Ping https://localhost:5443/v2/ err Get \"https://localhost:5443/v2/\": tls: failed to verify certificate: x509: certificate signed by unknown authority (...)"

For a registry or mirror /v2/ request, the registry endpoint determines the certs.d/HOST:PORT/ directory. If the failure instead occurs while Podman is contacting a separate authentication/token service, inspect that certificate chain separately; do not assume that creating certs.d for the token-service hostname is the correct fix. If registries.conf remaps docker.io to an internal mirror, trust the mirror's certificate, not only the public name you typed.


Podman's registry CA directory

Podman loads per-registry TLS material from certs.d. The directory name must match the registry endpoint exactly, including a non-default port.

Search order Path pattern
User-specific (rootless, checked first) $HOME/.config/containers/certs.d/HOST:PORT/
System /etc/containers/certs.d/HOST:PORT/
Legacy system /etc/docker/certs.d/HOST:PORT/

Rootless Podman checks the user-specific directory first. If it exists for that registry, it takes precedence over the system directory rather than merging with it. Rootful Podman uses the system locations.

For registry.example.test:5000 (the hostname used in the private registry guide):

text
/etc/containers/certs.d/registry.example.test:5000/ca.crt

Place one or more PEM-encoded CA certificates as *.crt files in that directory. Podman also checks legacy /etc/docker/certs.d/HOST:PORT/ paths in debug output — prefer the containers path for new configuration.


Install the registry CA

Obtain the CA certificate that signed the registry server certificate — from your registry operator, your PKI team, or the CA.crt file you generated in the private-registry lab. Create the directory and copy the PEM file:

bash
sudo mkdir -p /etc/containers/certs.d/localhost:5443

Copy the PEM into the directory name that matches your registry endpoint:

bash
sudo cp registry-ca.crt /etc/containers/certs.d/localhost:5443/ca.crt

Retry the pull:

bash
podman pull localhost:5443/ubi-minimal:latest

Sample output after trust is wired:

output
Trying to pull localhost:5443/ubi-minimal:latest...
Getting image source signatures
Copying blob sha256:b02c58b1810d6d9c787b6e23d8ac6f4f454a1969867e2c1d9e78cbe380cfab0a
Copying config sha256:591c6dfb4400e422f10f911ff8d95aead6e1d80975bb11cfabd1ba918133f8c5
Writing manifest to image destination
591c6dfb4400e422f10f911ff8d95aead6e1d80975bb11cfabd1ba918133f8c5

Confirm Podman loaded your CA from the expected path:

bash
podman --log-level=debug pull localhost:5443/ubi-minimal:latest 2>&1 | grep 'crt:'

Sample output:

output
crt: /etc/containers/certs.d/localhost:5443/ca.crt

podman login against the same endpoint should reach Login Succeeded! once TLS verification passes — credential errors after that point are a separate topic.


.crt versus .cert versus .key

Podman uses filename suffixes to distinguish trust material from client authentication:

Suffix Purpose
*.crt Trusted CA certificate (what you install for registry trust)
*.cert Client certificate for mutual TLS
*.key Private key matching a client certificate

Name the registry CA ca.crt, not ca.cert — the .cert suffix signals client certificate material, not a trusted issuer.


Certificate hostname mismatch

A sibling error means the chain is trusted but the name does not match. Pull against a registry that presents a certificate for a different hostname:

bash
podman pull localhost:5444/ubi-minimal:latest

Sample output:

output
Error: ... tls: failed to verify certificate: x509: certificate is valid for wronghost.example.com, not localhost

This is not an unknown-CA problem. The server presented a certificate Podman could verify against a known issuer, but the DNS name or IP you connected to is absent from the certificate's Subject Alternative Name (SAN) list.

Inspect what the registry actually presents:

bash
openssl s_client -connect localhost:5444 -servername localhost </dev/null 2>&1 | openssl x509 -noout -subject -ext subjectAltName

Sample output on a misconfigured lab registry:

output
subject=CN=wronghost.example.com
X509v3 Subject Alternative Name:
    DNS:wronghost.example.com

The fix is to reissue the server certificate with the correct DNS: and IP: SAN entries for the hostname clients use — or connect using a name already in the SAN. Do not disable TLS verification to work around a hostname mismatch in production.


http: server gave HTTP response to HTTPS client

A different failure mode — protocol mismatch, not untrusted CA. Pull from a registry that speaks plain HTTP while Podman expects HTTPS:

bash
podman pull localhost:5001/ubi-minimal:latest

Sample output:

output
Trying to pull localhost:5001/ubi-minimal:latest...
Error: unable to copy from source docker://localhost:5001/ubi-minimal:latest: initializing source docker://localhost:5001/ubi-minimal:latest: pinging container registry localhost:5001: Get "https://localhost:5001/v2/": http: server gave HTTP response to HTTPS client

Podman tried https://; the registry answered with HTTP. Installing a CA does not fix this.


Fix an intentional HTTP lab registry

For a controlled lab registry that will stay on HTTP, declare it explicitly in registries.conf:

toml
[[registry]]
location = "localhost:5001"
insecure = true

Place the stanza in a drop-in such as /etc/containers/registries.conf.d/10-lab-http.conf. After that, podman pull localhost:5001/... uses HTTP without TLS.

One-time diagnostic bypass (not a production fix):

bash
podman pull --tls-verify=false localhost:5443/ubi-minimal:latest

That pull can succeed even when no CA is installed — which confirms the network path works and TLS verification was the failing layer. For a real private registry, enable HTTPS on the server, install the trusted CA in certs.d, and leave verification on.


Why --tls-verify=false is not the final fix

--tls-verify=false skips certificate chain validation for that command. It does not add trust — it removes the check that failed. Anyone on the network path could present a different certificate and Podman would accept it.

Use the flag briefly to separate connectivity from trust:

  1. Pull with --tls-verify=false — if it succeeds, TLS verification was the blocker.
  2. Install ca.crt under certs.d/HOST:PORT/.
  3. Repeat the pull without the flag.

Keep verification enabled in scripts, CI, Quadlet units, and production systemd services.


Corporate TLS inspection and MITM proxies

Corporate networks sometimes terminate TLS on a proxy and re-encrypt with an enterprise CA. Typical symptoms:

  • Browsers trust the site because they use a browser-specific or enterprise-managed trust store
  • podman pull fails with x509: certificate signed by unknown authority
  • openssl s_client shows an issuer naming your organization's CA, not a public CA

That pattern does not mean Podman ignores OS trust in general. Podman uses normal system certificate trust plus custom registry CAs. If the corporate root is properly installed in the Linux trust store Podman uses, it should normally be trusted there as well. The failure usually means the CA is missing from that Linux trust store, or the registry-specific certs.d configuration for the affected endpoint does not contain the required CA.

Fix with either approach:

  1. Install the enterprise CA into the host OS trust store using your distribution's supported mechanism.
  2. Install the CA specifically for the affected registry under certs.d/HOST:PORT/.

Install registry-specific trust for the hostname shown in the TLS request or debug output. If registries.conf rewrites the pull to a mirror, that may be the mirror hostname. Do not export and trust a one-off certificate from a single browser session; use the approved CA bundle from your security team.


Rootless versus rootful CA trust

The same image reference can succeed for one invocation and fail for another when trust files differ.

Rootless Podman checks $HOME/.config/containers/certs.d/HOST:PORT/ before system locations such as /etc/containers/certs.d/ and legacy /etc/docker/certs.d/. If a user-specific directory for that registry exists, it takes precedence over the system directory rather than merging with it. Rootful Podman uses the system locations.

A CA installed only under your home directory fixes rootless pulls but not sudo podman. Install under /etc/containers/certs.d/ when every user on the host needs the same registry trust. If rootful pulls work but rootless pulls fail despite a system CA, inspect whether an incomplete user-specific certs.d/HOST:PORT/ directory is shadowing the system configuration.


Registry mirrors

When registries.conf redirects pulls — for example from docker.io to mirror.example.com — TLS errors may reference the mirror hostname. Debug output shows the physical GET https:// target. Install the CA for that mirror endpoint, not only for the logical name in your image reference.


Diagnostic decision tree

text
x509: certificate signed by unknown authority
    → correct HOST:PORT in image reference?
    → certs.d directory name matches HOST:PORT exactly?
    → CA file named *.crt (e.g. ca.crt)?
    → server sends full chain (or you trust the issuing CA)?
    → corporate MITM — need organization CA?

x509: certificate is valid for X, not Y
    → SAN/hostname mismatch — reissue cert or use name in SAN
    → not fixed by installing CA alone

http: server gave HTTP response to HTTPS client
    → registry is plain HTTP — use TLS on server or insecure lab stanza
    → not a CA trust problem

Troubleshooting

Symptom Likely cause Fix
x509: certificate signed by unknown authority CA not in certs.d for that HOST:PORT Copy issuer CA to /etc/containers/certs.d/HOST:PORT/ca.crt
Pull works with --tls-verify=false only Missing CA trust Install CA; remove permanent use of the flag
certificate is valid for X, not Y SAN does not include connected hostname Reissue server cert with correct SAN; use matching hostname
HTTP response to HTTPS client Registry serves HTTP on that port Enable TLS on registry or insecure = true for lab HTTP only
Rootless works, sudo podman fails CA only in ~/.config/containers/certs.d Copy CA to /etc/containers/certs.d/
Rootful works, rootless fails User certs.d/HOST:PORT/ shadows incomplete system trust Inspect $HOME/.config/containers/certs.d/HOST:PORT/; fix or remove the incomplete user-specific directory
Error persists after CA install Wrong HOST:PORT directory name Match directory to debug GET https:// host:port exactly
Mirror in use Trust needed for mirror endpoint Debug pull; install CA for mirror from registries.conf

References


Summary

When Podman reports x509: certificate signed by unknown authority, the registry TLS handshake failed because Podman does not trust the certificate issuer. Install the signing CA as ca.crt under certs.d/HOST:PORT/ where HOST:PORT matches the endpoint from podman --log-level=debug — including non-default ports and mirror hostnames.

Hostname errors (certificate is valid for X, not Y) need a certificate reissue or a corrected client hostname, not merely a new CA file. http: server gave HTTP response to HTTPS client means Podman used HTTPS against a plain HTTP registry — fix the protocol or mark a lab registry insecure, not the CA store.

Use --tls-verify=false only to confirm that connectivity works and TLS was the failing layer, then install proper trust and pull again with verification enabled. Rootless Podman checks user-specific certs.d directories before system paths; an incomplete user directory can shadow system trust. For the full private-registry TLS lab that generates the CA, continue with Run a private container registry.


Frequently Asked Questions

1. Where does Podman look for a private registry CA certificate?

Podman reads per-registry trust from certs.d directories named HOST:PORT to match the registry endpoint, including a non-default port. Rootless Podman checks $HOME/.config/containers/certs.d/HOST:PORT/ first, then system locations such as /etc/containers/certs.d/ and legacy /etc/docker/certs.d/. The first matching per-registry directory wins. Place PEM CAs as *.crt files such as ca.crt inside that directory.

2. What is the difference between x509 unknown authority and certificate is valid for X not Y?

Unknown authority means Podman does not trust the certificate issuer — install the registry CA or your organization CA in certs.d. Valid for X not Y means the certificate is trusted but the DNS name or IP you connected to is not listed in the certificate SAN — reissue the cert or use the hostname in the SAN.

3. Why does podman pull fail with http server gave HTTP response to HTTPS client?

Podman opened an HTTPS connection but the registry answered with plain HTTP. The registry is not serving TLS on that port, or you need an insecure stanza in registries.conf for a controlled HTTP lab only. This is not fixed by installing a CA.

4. Is podman pull --tls-verify=false safe for production?

No. It disables TLS certificate verification for that pull and proves only that the network path works. Use it briefly to isolate a TLS problem, then install the correct CA in certs.d and pull again with verification enabled.

5. Why does podman pull work for my user but sudo podman pull fails with x509?

Rootless Podman checks $HOME/.config/containers/certs.d/HOST:PORT/ before system locations. If a user-specific directory for that registry exists, it takes precedence over the system directory rather than merging with it. Rootful Podman uses the system locations. A CA only under your home directory fixes rootless pulls but not sudo podman; install under /etc/containers/certs.d/ when every user on the host needs the same registry trust.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)