CertGrid CertGrid
Hands-on Lab·Podman

Podman Private Registry Setup

A registry container on podman03, and the first push fails with `http: server gave HTTP response to HTTPS client`. Podman assumes TLS and will not quietly downgrade - every client has to be told, individually.

Registries and Remote Hosts Guide 40 of 47 Intermediate

Written against the versions above. Podman follows the distribution here rather than a vendor repository, so the version you get is the one Ubuntu shipped. The commands are stable across 5.x.

The 3 hosts these commands ran on: podman01, podman02, podman03.
Server NameIP AddressOSRolesCPURAMHDD
PODMAN01192.168.0.21Ubuntu 26.04 LTSPrimary Container Host2 Core4 GB50 GB
PODMAN02192.168.0.22Ubuntu 26.04 LTSRootless and Remote Client Host2 Core4 GB50 GB
PODMAN03192.168.0.23Ubuntu 26.04 LTSRegistry and Image Transfer Host2 Core4 GB50 GB

Before you start

  1. The registry is a container

    docker.io/library/registry:3 on podman03, with a named volume for its storage - without one, every image you push disappears when the container is recreated, which is the classic way to lose a registry.

    /v2/_catalog answers {"repositories":[]}. That endpoint is the whole registry API's front door and the fastest way to check a registry is alive and empty, from any machine that can reach it.

    bash Example session
    podman run -d --name registry -p 5000:5000 -v regdata:/var/lib/registry docker.io/library/registry:3Trying to pull docker.io/library/registry:3...Getting image source signaturesCopying blob sha256:8f4e1177a675ecd1759cde12c405d158ff166a809659e4006966a2b1a7c87123Copying blob sha256:e6f31ffc071e5560b82a8685fba8214954e5721e3e49269d00958316edbe89feCopying blob sha256:269b60c1a347a7b2501df94ec2391af4c325bb9ef5a0cf5a4e6dd2e87198eb21Copying blob sha256:47465c9fe4b1438b5f9aed1cf7182de26087f6eda66eb23dce944b763673bbedCopying blob sha256:f90c3e90567750f85e63c5ba884e9ef81f4839ab404a8f3dfd306e661018c6a0Copying config sha256:1bc3f1a4432290d5ac3341603f15bf99c5b2feb4a9e3195e530aadc66fbcc65eWriting manifest to image destination89a8df5fa5f05ce2ecac66bb315ec25871cbf482e1edc6fba23c09c82f9959d3podman ps --format 'table {{.Names}} {{.Ports}} {{.Status}}'NAMES       PORTS                   STATUSregistry    0.0.0.0:5000->5000/tcp  Up Less than a secondcurl -s http://localhost:5000/v2/_catalog{"repositories":[]}

    Expected resultA running registry publishing 5000, and an empty catalogue.

    Success conditionA registry is serving on podman03 with persistent storage.

  2. The push that refuses

    From podman01, push to it. It fails:

    Error: trying to reuse blob sha256:6f09edfb3f6d... at destination: pinging
    container registry 192.168.0.23:5000: Get "https://192.168.0.23:5000/v2/":
    http: server gave HTTP response to HTTPS client

    Read it carefully: Podman requested https and got http. It assumes TLS for every registry and does not fall back, which is the correct default and the reason this is a two-step guide rather than one.

    The fix is configuration, not a flag. A file in the same directory that decided short names in guide 12 and the auto-update registry in guide 42:

    [[registry]]
    location = "192.168.0.23:5000"
    insecure = true

    podman info then lists it among the registries it knows, with true for insecure - so you can confirm the file took effect without pushing again.

    A --tls-verify=false flag exists and works for a manual push, but it does not help anything that runs without your flags: auto-update, a Quadlet unit, a Kubernetes-style manifest. Configure it once.

    bash Example session
    podman push localhost/multi:1 192.168.0.23:5000/multi:1Getting image source signaturesCopying blob sha256:7da0375eb2ecd60c8f630fc42f4cc36c6d2b3923f404212810c167019169b092Copying blob sha256:6f09edfb3f6d7173733adc8eec8ea00626550dc6fc2dcf07d40e13f5c1e907c4Error: trying to reuse blob sha256:6f09edfb3f6d7173733adc8eec8ea00626550dc6fc2dcf07d40e13f5c1e907c4 at destination: pinging container registry 192.168.0.23:5000: Get "https://192.168.0.23:5000/v2/": http: server gave HTTP response to HTTPS client[exit 125]sudo -n tee /etc/containers/registries.conf.d/97-lab-registry.conf <<'CONF'[[registry]]location = "192.168.0.23:5000"insecure = trueCONF[[registry]]location = "192.168.0.23:5000"insecure = truepodman info --format 'insecure={{.Registries}}'insecure=map[192.168.0.23:5000:{192.168.0.23:5000 {192.168.0.23:5000 true } [] false false} localhost:5000:{localhost:5000 {localhost:5000 true } [] false false}]

    Expected resultThe HTTPS-to-HTTP error, then the config written and reflected in podman info.

    Success conditionThe client is configured to talk to this registry over plain HTTP.

  3. Push from one host, pull on another

    The push now completes, and the registry's own API confirms what it holds:

    $ curl -s http://localhost:5000/v2/_catalog
    {"repositories":["multi"]}
    $ curl -s http://localhost:5000/v2/multi/tags/list
    {"name":"multi","tags":["1"]}

    Then podman02 - which received this image directly in guide 73 - gets the same insecure configuration, deletes its local copy, and pulls from the registry instead. It runs.

    Note that the configuration had to be written on podman02 as well. There is no central place to declare a registry insecure; every client is configured individually, which is the strongest practical argument for giving even an internal registry a real certificate. Ten hosts means ten files, and the one you forget fails at the worst moment.

    Compare the two ways of moving this image. image scp was one command and no infrastructure, point to point. The registry took setup and a config file on every client, and in exchange any number of hosts can pull the same image by name, the tags are a record of what exists, and guide 42 can watch it for new versions. The crossover is around the second host you have to do it twice for.

    bash Example session
    podman push localhost/multi:1 192.168.0.23:5000/multi:1Getting image source signaturesCopying blob sha256:7da0375eb2ecd60c8f630fc42f4cc36c6d2b3923f404212810c167019169b092Copying blob sha256:6f09edfb3f6d7173733adc8eec8ea00626550dc6fc2dcf07d40e13f5c1e907c4Error: trying to reuse blob sha256:6f09edfb3f6d7173733adc8eec8ea00626550dc6fc2dcf07d40e13f5c1e907c4 at destination: pinging container registry 192.168.0.23:5000: Get "https://192.168.0.23:5000/v2/": http: server gave HTTP response to HTTPS client[exit 125]curl -s http://localhost:5000/v2/_catalog{"repositories":[]}curl -s http://localhost:5000/v2/multi/tags/list{"name":"multi","tags":["1"]}podman rmi -f localhost/multi:1Untagged: localhost/multi:1Deleted: 9f00b68fe5e6bb84503d42c233b5a42d492c28bca1305725319db6fa702e5f04podman pull 192.168.0.23:5000/multi:1Trying to pull 192.168.0.23:5000/multi:1...Getting image source signaturesCopying blob sha256:a3502f80cd9a0fe774f7a1ceeb2ac817d4d35bde7ccd040cd066c53dfc61f8acCopying blob sha256:03d83da6a4bd7f9b778b541c93fd2c1a603ed29e95b345610dd6ceb826692ab2Copying config sha256:9f00b68fe5e6bb84503d42c233b5a42d492c28bca1305725319db6fa702e5f04Writing manifest to image destination9f00b68fe5e6bb84503d42c233b5a42d492c28bca1305725319db6fa702e5f04podman run --rm 192.168.0.23:5000/multi:1compiled in a stage that was thrown away

    Expected resultA successful push, the image in the catalogue and tag list, and a different host pulling and running it.

    Success conditionOne image is in a registry and running on a host that pulled it by name.

Troubleshooting

Official sources