Deploying Zot for a Private Container Registry and Docker Image Acceleration

zot registrydocker image accelerationprivate container registrydocker compose deploymentzot configuration
Published·Modified·

Before using Zot, I relied on Nexus 3 to build a container image repository. However, this heavyweight tool consumes high memory, is complex to set up, and is difficult to troubleshoot when issues arise. Now, I have completely moved away from Nexus 3 and embraced Zot.

CleanShot 2026-06-16 at 14.44.35@2x.png

Note: This article has a certain level of technical depth. It is recommended for developers or operations personnel. Beginners are not advised to attempt this.

What is Zot?

Zot is a container image repository similar to Docker Hub, but it is lightweight and private, allowing you to deploy it freely on your own servers. It is specifically designed to store and distribute your container images and can also be used to accelerate Docker Hub images, thereby solving the problem of being unable to pull Docker images in China.

Zot features the following characteristics:

  • Private Image Storage: Build a private Docker image repository on your own server for secure internal team sharing.
  • Image Push and Pull: Fully compatible with Docker commands; push and pull images just like using Docker Hub.
  • Security Vulnerability Scanning: Automatically checks for known security risks within images to identify potential issues early.
  • Access Control: Supports user authentication and authorization, giving you control over who can push and who can pull.
  • Garbage Collection: Automatically reclaims unused image layers and redundant data to save storage space.
  • Data Deduplication: Stores identical content only once to avoid redundant disk usage.
  • Cross-Platform Support: Runs on Linux, Mac, Raspberry Pi, and more; suitable for cloud, on-premise, and edge environments.
  • Minimal Deployment: The entire software is a single file; download and run without installing dependencies or additional services.

Deploying Zot with Docker Compose

Below, I will deploy Zot using Docker Compose based on my actual requirements.

1. Generate a Password File

Zot supports various user authentication methods. The simplest is Basic Authentication. First, generate the htpasswd file using the following command:

docker run --rm httpd:alpine htpasswd -Bbn username password > ./htpasswd
  • username: Change to your own username.
  • password: Change to your own password.

2. Write the Configuration File

To be honest, although Zot itself is lightweight, its configuration is quite complex. I spent a lot of time tweaking it with the help of AI to create a configuration that meets my needs.

Let's first discuss my requirements:

  • Accelerate Docker official and GitHub container image repositories to allow normal pulling over domestic networks.
  • Establish my own private container image repository.

If your needs are the same as mine, congratulations, you can copy my configuration directly. Create a new config.json file with the following content:

{
    "storage": {
        "rootDirectory": "/var/lib/registry",
        "commit": true,
        "dedupe": true,
        "gcInterval": "24h",
        "retention": {
            "policies": [
                {
                    "repositories": [
                        "username/**"
                    ],
                    "deleteUntagged": true,
                    "keepTags": [
                        {
                            "patterns": ".*"
                        }
                    ]
                },
                {
                    "repositories": [
                        "**"
                    ],
                    "deleteUntagged": true,
                    "keepTags": [
                        {
                            "pushedWithin": "24h",
                            "pulledWithin": "24h"
                        }
                    ]
                }
            ]
        }
    },
    "http": {
        "address": "0.0.0.0",
        "port": "5000",
        "auth": {
            "htpasswd": {
                "path": "/etc/zot/htpasswd"
            }
        },
        "accessControl": {
            "repositories": {
                "**": {
                    "defaultPolicy": [
                        "read",
                        "create",
                        "update",
                        "delete"
                    ]
                }
            }
        }
    },
    "extensions": {
        "sync": {
            "enable": true,
            "registries": [
                {
                    "urls": [
                        "https://ghcr.io"
                    ],
                    "onDemand": true,
                    "tlsVerify": true,
                    "maxRetries": 3,
                    "retryDelay": "5m",
                    "content": [
                        {
                            "prefix": "**"
                        }
                    ]
                },
                {
                    "urls": [
                        "https://registry-1.docker.io"
                    ],
                    "onDemand": true,
                    "tlsVerify": true,
                    "maxRetries": 3,
                    "retryDelay": "5m"
                }
            ]
        },
        "search": {
            "enable": true,
            "cve": {
                "updateInterval": "2h"
            }
        },
        "ui": {
            "enable": true
        }
    }
}

Note: Change the username in the above configuration to your own username (refer to the first step). For more configuration parameters, refer to the official documentation: https://zotregistry.dev/v2.1.17/admin-guide/admin-configuration/

3. Create the compose.yaml File

Create a compose.yaml file with the following content:

services:
  zot:
    image: ghcr.io/project-zot/zot-linux-amd64:latest
    container_name: reg_xphub
    restart: always
    ports:
      - "5000:5000"
    volumes:
      - ./config.json:/etc/zot/config.json:ro
      - ./htpasswd:/etc/zot/htpasswd:ro
      - ./data:/var/lib/registry

Finally, run docker compose up -d to start the service and visit the WebUI at http://IP:5000 to log in.

CleanShot 2026-06-16 at 14.45.34@2x.png

Note: If you want to use Zot to solve the problem of being unable to pull Docker images in China, you should deploy Zot on a server located overseas.

Usage

Since the Docker client does not support HTTP by default, we can configure an SSL certificate via reverse proxy for http://IP:5000 to enable HTTPS support.

Note: If you use a CDN for configuration, do not set caching on the CDN, as this may cause authentication exceptions.

After successfully configuring the SSL certificate, you can use the Docker command line to log in:

docker login pub.tcp.mk

Note: Change pub.tcp.mk to your own domain name. Do not add https:// at the beginning or any path at the end.

Then, pull the image:

docker pull pub.tcp.mk/helloz/zmark

Conclusion

If you also want to build a lightweight, private container image repository or need to accelerate Docker image pulls in China, give Zot a try. Compared to heavyweight tools like Nexus 3 or Harbor, Zot saves resources and is simple to deploy. Although the configuration file requires some effort, once configured correctly, it will work seamlessly for a long time.