An image registry stores and versions container images for distribution to container runtime engines such as Docker, Podman, and others. Images are typically pulled and pushed using the respective commands of the runtime engine. Another benefit is that registries can centralize the distribution of images in an environment. Most runtimes come with default registries to pull and push from, but you might want to have your own personal/private registry where you can control what images are available for your runtimes.
This article outlines the steps needed to implement a private registry as a container and store images in /opt/registry
for internal use. Podman is the container engine and htpasswd
provides authentication.
Install Podman and httpd-tools
Install the podman
package to run the registry. The httpd-tools
package provides the htpasswd
utility for authentication.
# yum install -y podman httpd-tools
Create folders for the registry
The registry will be stored in /opt/registry/
on the host system and the respective directories are mounted in the container running the registry.
# mkdir -p /opt/registry/{auth,certs,data}
- The Auth subdirectory stores the
htpasswd
file used for authentication. - The Certs subdirectory stores certificates used by the registry for
- authentication.
- The Data directory stores the actual images stored in the registry.
Note - Creating the directories on removable storage makes the registry portable for disconnected/restricted networks.
Generate credentials for accessing the registry
Authentication is provided by a simple htpasswd
file and also an SSL key pair.
htpasswd username and password
Use the htpasswd
utility to generate a file containing the credentials for accessing the registry:
# htpasswd -bBc /opt/registry/auth/htpasswd registryuser registryuserpassword
- b provides the password via command.
- B stores the password using Bcrypt encryption.
- c creates the file.
- Username is registryuser.
- Password is registryuserpassword.
A Bcrypt Htpasswd file named htpasswd
will be created in the /opt/registry/auth/
directory.
TLS key pair
The registry is secured with TLS by using a key and certificate signed by a trusted authority (internal or external) or by a simple self-signed certificate. To use a self-signed certificate:
# openssl req -newkey rsa:4096 -nodes -sha256 -keyout /opt/registry/certs/domain.key -x509 -days 365 -out /opt/registry/certs/domain.crt
- req tells OpenSSL to generate and process certificate requests.
- -newkey tells OpenSSL to create a new private key and matching certificate request.
- rsa:4096 tells OpenSSL to generate an RSA key with 4096 bits.
- -nodes tells OpenSSL there is no password requirement for the private key. The private key will not be encrypted.
- -sha256 tells OpenSSL to use the sha256 to sign the request.
- -keyout tells OpenSSL the name and location to store new key.
- -x509 tells OpenSSL to generate a self-signed certificate.
- -days tells OpenSSL the number of days the key pair is valid for.
- -out tells OpenSSL where to store the certificate.
Enter the respective options for your certificate. The CN= value is the hostname of your host. The host's hostname should be resolvable by DNS or the /etc/hosts
file.
Note: If the registry is not secured using TLS, the insecure setting in the /etc/containers/registries.conf
file may have to be configured for the registry.
The certificate will also have to be trusted by your hosts and clients:
# cp /opt/registry/certs/domain.crt /etc/pki/ca-trust/source/anchors/
# update-ca-trust
# trust list | grep -i "<hostname>"
Start the registry
The next step involves running the registry image with an exposed port (5000) and mounted volumes of the respective directories created earlier.
The command to start the registry:
# podman run --name myregistry \
-p 5000:5000 \
-v /opt/registry/data:/var/lib/registry:z \
-v /opt/registry/auth:/auth:z \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
-v /opt/registry/certs:/certs:z \
-e "REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt" \
-e "REGISTRY_HTTP_TLS_KEY=/certs/domain.key" \
-e REGISTRY_COMPATIBILITY_SCHEMA1_ENABLED=true \
-d \
docker.io/library/registry:latest
The details of the options are:
- --name myregistry names the container myregistry.
- -p 5000:5000 exposes port 5000 in the container as port 5000 on the host.
- -v /opt/registry/data:/var/lib/registry:z mounts
/opt/registry/data
on the host as/var/lib/registry
in the container with the correct SELinux context. - -v /opt/registry/auth:/auth:z mounts
/opt/registry/auth
on the host as/auth
in the container with the correct SELinux context. - -v opt/registry/certs:/certs:z mounts
/opt/registry/certs
on the hosts as/certs
in the container with the correct SELinux context. - -e "REGISTRY_AUTH=htpasswd" uses an
bcrypt
encryptedhtpasswd
file for authentication. File location set by container's REGISTRY_AUTH_HTPASSWD_PATH environment variable. - -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" specifies the realm to use for
htpasswd
. - -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd uses the bcrypt-encrypted
/auth/htpasswd
file in the container. - -e "REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt" sets path to certificate file.
- -e "REGISTRY_HTTP_TLS_KEY=/certs/domain.key" sets path to private key.
- -e REGISTRY_COMPATIBILITY_SCHEMA1_ENABLED=true provides backward compatibility for schema1 manifests.
- -d docker.io/library/registry:latest is a registry application that allows for the storage and distribution of images.
Note: If a firewall is running on the hosts, the exposed port (5000) will need to be permitted.
# firewall-cmd --add-port=5000/tcp --zone=internal --permanent
# firewall-cmd --add-port=5000/tcp --zone=public --permanent
# firewall-cmd --reload
Verify access to registry
The curl
command can be used to access the registry:
# curl https://hostname:5000/v2/_catalog
{"repositories":[]}
The certificate can be verified using:
# openssl s_client -connect <servername>:5000 -servername <servername>
Be sure to trust the certificate from earlier or use curl's -k
switch to ignore certificate verification.
Work with the registry
The registry can be accessed and interacted with just like any other registry such as registry.access.redhat.com
, registry.redhat.io
, docker.io
, and/or quay.io
.
Log in to the registry
Use the podman login
command to log into the registry:
# podman login <hostname>:5000
Enter Username:xxxxxxxx
Enter Password:yyyyyyyy
Login Succeeded!
Your credentials will be Base64 encoded into /run/user/0/containers/auth.json
by default.
When working with OpenShift and using an alternate/mirror registry for disconnected/air-gapped environments, the contents of this file can be added to your pull secret file. You can also add your pull secret file as a command-line option to podman login
and your credentials will be added to the file upon login.
# podman login <hostname>:5000 --authfile <pull secret file>
Enter Username:xxxxxxxx
Enter Password:yyyyyyyy
Login Succeeded!
Log out of the registry
Use podman logout
to log out of the registry:
# podman logout <hostname>:5000
Removed login credentials for registry.tkagn.io:5000
Credentials will be removed from /run/user/0/containers/auth.json
or from any file you specify via the --authfile
parameter.
Push/pull images to the registry
To pull images from the registry, prepend the registry location to the repository/image name.
Example: podman pull <Registry Hostname>:<Registry Port>/<Repository>/<Image Name>
To push to the registry, use podman tag
to first tag the image and the registry location, and then push the image.
Example: podman tag <image id|<repo name/image name>> registry:5000/<repo>/<image>
# podman pull registry.access.redhat.com/ubi8/ubi:latest
Trying to pull registry.access.redhat.com/ubi8/ubi...
Getting image source signatures
…
Storing signatures
# podman login <registry hostname>:<port>
Enter Username:xxxxxx
Enter Password:yyyyyyy
Login Suceeded!
# podman tag registry.access.redhat.com/ubi8/ubi:latest <registry
hostname>:<port>/ubi8/ubi:latest
# podman push <registry hostname>:<port>/ubi8/ubi:latest
Getting image source signatures
Copying blob 226bfaae015f done
…
Storing signatures
You can verify the images has been uploaded to the registry via the curl
command:
# curl -u <username>:password https://<registry host>:<port>/v2/_catalog
{"repositories":["ubi8/ubi"]}
With the private registry implemented, you can edit /etc/containers/registries.conf
to include the private registry in the list of supported registries.
Lifecycle
Start registry:
# podman run --name myregistry -p 5000:5000 -v
/opt/registry/data:/var/lib/registry:z -v /opt/registry/auth:/auth:z -e
"REGISTRY_AUTH=htpasswd"
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" -e
REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd -v /opt/registry/certs:/certs:z -e
REGISTRY_COMPATIBILITY_SCHEMA1_ENABLED=true -d docker.io/library/registry:latest
Stop registry:
# podman stop myregistry
Remove registry:
# podman rm myregistry
Delete registry image:
# podman rmi registry:latest
Remove certificate:
# rm /etc/pki/ca-trust/source/anchors/domain.crt
# update-ca-trust
# trust list | grep "<hostname>"
Alternatives
Another option is to use Quay as a private registry, which offers many more features.
Conclusion
With this personal/private registry in place, you can provide a central point of distribution for your in-house images that can be stored and maintained. A private registry can also come in handy in disconnected and air-gapped environments. I hope you find this information useful for your situation.
[ Getting started with containers? Check out this free course. Deploying containerized applications: A technical overview. ]
About the author
Stephen Wilson is a Senior Storage Consultant with Red Hat, Inc. He has over 20 years of experience in information systems management. His professional interests include system administration, cybersecurity, cloud technologies, and virtualization.
Stephen lives in Meridian, MS with his wife Tan and two boys, Stephen and Matthew. Stephen's personal hobbies include weightlifting, running (yes for fun), and basketball. Stephen is active in his community and volunteers his time to try and make things better for everybody
More like this
Browse by channel
Automation
The latest on IT automation for tech, teams, and environments
Artificial intelligence
Updates on the platforms that free customers to run AI workloads anywhere
Open hybrid cloud
Explore how we build a more flexible future with hybrid cloud
Security
The latest on how we reduce risks across environments and technologies
Edge computing
Updates on the platforms that simplify operations at the edge
Infrastructure
The latest on the world’s leading enterprise Linux platform
Applications
Inside our solutions to the toughest application challenges
Original shows
Entertaining stories from the makers and leaders in enterprise tech
Products
- Red Hat Enterprise Linux
- Red Hat OpenShift
- Red Hat Ansible Automation Platform
- Cloud services
- See all products
Tools
- Training and certification
- My account
- Customer support
- Developer resources
- Find a partner
- Red Hat Ecosystem Catalog
- Red Hat value calculator
- Documentation
Try, buy, & sell
Communicate
About Red Hat
We’re the world’s leading provider of enterprise open source solutions—including Linux, cloud, container, and Kubernetes. We deliver hardened solutions that make it easier for enterprises to work across platforms and environments, from the core datacenter to the network edge.
Select a language
Red Hat legal and privacy links
- About Red Hat
- Jobs
- Events
- Locations
- Contact Red Hat
- Red Hat Blog
- Diversity, equity, and inclusion
- Cool Stuff Store
- Red Hat Summit