Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 10

Secure Container Registry Management

Recommended resources

Hello! Welcome to your next lesson in the "Advanced Image and Runtime Management" module.

In our last session, we mastered the art of tagging images with semantic versioning and pushing them to public registries like Docker Hub and GitHub Container Registry. This is perfect for open-source projects and public applications. However, in a corporate or secure environment, you need a private, controlled space for your proprietary images.

Today, we'll tackle exactly that. Your learning outcome is to authenticate with, use, and run a private container registry for secure image distribution. As a DevOps engineer with an interest in cybersecurity, you'll find this skill fundamental for building secure software supply chains and maintaining control over your organization's intellectual property. We will set up a local, secure registry, a common task for establishing development and testing environments.

1. Why Use a Private Registry?

While public registries are convenient, they aren't always suitable. Companies need private registries for several key reasons:

  • Security: To store proprietary or sensitive application images that should not be publicly accessible.
  • Control: To manage access control, deciding who can push (write) and pull (read) images.
  • Performance: To have a registry on the local network, reducing latency and reliance on internet bandwidth for CI/CD pipelines and deployments.
  • Cost & Rate Limits: To avoid potential costs and rate-limiting imposed by public registries on high-volume usage.

Real-Time Custom Private Docker Registry With SSL | DevOps | Docker

To start, let's watch the introduction of this video from DevOps Shack. It clearly explains the business and technical motivations for setting up a custom private registry instead of relying on public or even cloud-provider options.

Watch the first minute and 19 seconds (00:00 - 01:19). Focus on the comparison between public Docker Hub, cloud-managed registries (like AWS ECR), and a self-hosted custom registry.


2. The Anatomy of a Secure Private Registry

A secure private registry has two essential components:

  1. Authentication: A mechanism to verify the identity of users trying to log in, push, or pull images. We'll use basic HTTP authentication with a username and password file generated by htpasswd.
  2. Encryption (TLS/SSL): A protocol to encrypt the communication between the Docker client and the registry. This prevents image data and, more importantly, your login credentials from being sent over the network in plaintext.

The official Docker registry:2 image provides the core functionality. We will configure it to use our own authentication credentials and SSL certificates.

Private Container Registry with Authentication Flow
This diagram shows the basic flow. Clients (like your Docker daemon or a UI) must go through an authentication step to push or pull images from the container registry. This ensures only authorized users can access your private images.

3. Hands-On: Building Your Secure Registry with Docker Compose

While you can run a registry with a long docker run command, a more maintainable and professional approach—especially for a DevOps role—is to define it as a service in a docker-compose.yml file. This makes the configuration declarative and easy to manage.

We will set up a registry that is accessible on your local machine at registry.local:5000.

Step 1: Create the Project Structure and Certificates

First, we need to create a directory structure and generate a self-signed SSL certificate. This is common for local development environments. For a real production server, you would use a trusted authority like Let's Encrypt, as shown in the DevOps Shack video.

Open your terminal and run the following commands:

# Create the main project directory
mkdir my-secure-registry
cd my-secure-registry

# Create subdirectories for data, auth, and certs
mkdir -p data auth certs

# Generate a self-signed SSL certificate and private key
openssl req \
  -newkey rsa:4096 -nodes -sha256 -keyout certs/domain.key \
  -x509 -days 365 -out certs/domain.crt \
  -subj "/CN=registry.local"

This openssl command creates a certificate (domain.crt) and a private key (domain.key) valid for 365 days for the domain name registry.local.

Step 2: Create Authentication Credentials

Next, we'll create a user and password for our registry using htpasswd. You may need to install this utility first (sudo apt install apache2-utils on Debian/Ubuntu or sudo yum install httpd-tools on CentOS/RHEL).

# Create a user named 'admin' (you will be prompted to enter a password)
htpasswd -Bc auth/htpasswd admin

This command creates a file named htpasswd in the auth directory containing the encrypted credentials for the user admin.

Step 3: Create the Docker Compose File

Now, let's define our registry service. Create a file named docker-compose.yml in the my-secure-registry directory with the following content:

version: '3.8'

services:
  registry:
    image: registry:2
    container_name: docker-registry
    restart: always
    ports:
      - "5000:5000"
    environment:
      REGISTRY_AUTH: htpasswd
      REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
      REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
      REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt
      REGISTRY_HTTP_TLS_KEY: /certs/domain.key
    volumes:
      - ./data:/var/lib/registry
      - ./auth:/auth
      - ./certs:/certs

Let's break down the environment variables:

  • REGISTRY_AUTH: Specifies the authentication scheme (htpasswd).
  • REGISTRY_AUTH_HTPASSWD_PATH: Tells the registry where to find the password file inside the container.
  • REGISTRY_HTTP_TLS_CERTIFICATE & REGISTRY_HTTP_TLS_KEY: Point to the SSL certificate and key files.

The code for these steps is consolidated in the article below.

How to Set Up a Private Docker Registry on Ubuntu

This guide from OneUptime provides a clear, text-based walkthrough of setting up a production-style private registry. We have just followed its core steps for setting up certificates, authentication, and the Docker Compose file.

Review the sections 'Production Setup' -> 'Create Directory Structure', 'Generate SSL Certificates' (specifically the 'For self-signed certificates' part), 'Configure Authentication', and 'Create Docker Compose File'. This will reinforce what we've just done.

Step 4: Configure Your Host Machine

Since we are using a self-signed certificate, we need to tell our system (and the Docker daemon) to trust it. We also need to map our custom domain registry.local to our local machine.

  1. Edit your hosts file to resolve registry.local to 127.0.0.1.

    • On Linux/macOS: sudo nano /etc/hosts
    • On Windows: C:\Windows\System32\drivers\etc\hosts (as Administrator)
    • Add this line and save: 127.0.0.1 registry.local
  2. Configure the Docker daemon to trust your certificate. This is the most critical step.

    # Create a directory for your registry's certificate
    sudo mkdir -p /etc/docker/certs.d/registry.local:5000
    
    # Copy your public certificate to that directory
    sudo cp certs/domain.crt /etc/docker/certs.d/registry.local:5000/ca.crt
    
    # Restart the Docker daemon to apply the changes
    sudo systemctl restart docker
    

    This process is also detailed in the OneUptime article under the "Configure Docker Clients" section.


4. Run and Use Your Secure Registry

Everything is now in place! Let's start the registry and push an image to it.

  1. Start the registry:

    docker compose up -d
    
  2. Log in to your private registry:

    docker login registry.local:5000
    

    Enter the username (admin) and the password you created earlier. You should see Login Succeeded.

  3. Tag an image and push it:
    Let's use a simple image like nginx.

    # Pull a public image to work with
    docker pull nginx:latest
    
    # Tag it for your private registry
    docker tag nginx:latest registry.local:5000/my-nginx:1.0
    
    # Push it to your private registry
    docker push registry.local:5000/my-nginx:1.0
    
  4. Verify the push:
    You can verify the image exists in your registry via the API.

    curl --cacert certs/domain.crt -u admin:<your_password> https://registry.local:5000/v2/_catalog
    

    This should return {"repositories":["my-nginx"]}.

Test your understanding!

You have a local image named webapp:3.5-beta. What are the two docker commands you need to run to push this image to your newly created private registry at registry.local:5000? Assume you are already logged in.

Show answer
  1. Tag the image with the private registry's URL:
    docker tag webapp:3.5-beta registry.local:5000/webapp:3.5-beta
    
  2. Push the newly tagged image:
    docker push registry.local:5000/webapp:3.5-beta
    

5. Beyond the Basics: Enterprise-Grade Registries

The registry:2 image is excellent for many use cases, but for large-scale enterprise needs, you might use a more feature-rich solution like Harbor. Harbor is an open-source registry that includes features your cybersecurity interest will appreciate, such as:

  • Role-Based Access Control (RBAC)
  • Vulnerability scanning for images
  • Image signing
  • A rich web UI for administration

Harbor is often deployed on Kubernetes, which directly connects to your existing experience.

Secure Private Container Registry Architecture
This diagram shows a more complex, enterprise-ready architecture using NGINX as a reverse proxy, Harbor as the registry, and Keycloak for advanced identity and access management (IAM).

Harbor Private Container Registry for Docker and Kubernetes

To see what an enterprise-grade solution looks like, watch this brief overview of Harbor. It will give you a sense of the additional capabilities available and how it fits into a Kubernetes ecosystem.

Watch from 00:46 to 01:52. This section introduces Harbor and explains why you would choose it for a CI/CD and Kubernetes environment, highlighting features like vulnerability scanning and policy control.


Conclusion

Congratulations! You've successfully set up, secured, and used a private Docker registry. This is a crucial piece of infrastructure for any professional DevOps environment, enabling secure and efficient distribution of container images.

Key Takeaways:

  • Private registries are essential for security, control, and performance.
  • A secure registry requires both authentication (e.g., htpasswd) and encryption (TLS/SSL).
  • Using Docker Compose provides a clean, declarative way to manage your registry configuration.
  • Docker clients must be configured to trust the registry's SSL certificate, especially if it's self-signed.
  • The workflow (login, tag, push) is the same as for public registries; only the registry URL in the tag is different.
  • For advanced needs, enterprise solutions like Harbor offer features like vulnerability scanning and RBAC.

In our next lesson, we will dive deeper into the images you've just pushed. You will learn to inspect image layers and container configurations using docker inspect, allowing you to analyze and debug your containerized applications with precision.