Skip to Content

Self-Hosted Docker Registry

Learn how to set up a self-hosted Docker registry for private image storage.

karchunt

Kar Chun Tan

Creator

Metadata

Sun Jan 11 2026

2 min read

343 words

Self-Hosted Docker Registry

Self-hosting a Docker registry can provide several benefits, such as:

  • Security & Privacy: Keep proprietary code safe within your infrastructure. Essential for compliance and integrating custom vulnerability scanning.
  • Performance: Drastically faster pulls on local networks, speeding up CI/CD pipelines and reducing reliance on external bandwidth.
  • Cost Savings: Avoid per-user fees common in SaaS solutions and manage your own storage backends (S3, local disk).
  • Reliability: No dependency on public registry uptime or rate limits. Perfect for air-gapped environments.
  • Customization: Full control over garbage collection, retention policies, and authentication integrations (LDAP, AD).

Setup Steps

Create a docker-compose.yml file

We’re going to use the official Docker Registry  image to create a self-hosted registry.

docker-compose.yml
name: Registry services: registry: image: registry:3 container_name: registry ports: - "5000:5000" restart: unless-stopped environment: REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /var/lib/registry volumes: - ./registry-data:/var/lib/registry

Run the registry

Navigate to the directory containing the docker-compose.yml file and run:

docker compose up -d

Use the registry

You can now tag, push, and pull images from your registry.

docker tag <image_name> <registry_url>/<image_name> docker push <registry_url>/<image_name> docker pull <registry_url>/<image_name>

Here is an example:

kc@kchomelab:~/Desktop/karchunt.com$ docker tag hello-world:latest localhost:5000/hello-world:v1 kc@kchomelab:~/Desktop/karchunt.com$ docker push localhost:5000/hello-world:v1 The push refers to repository [localhost:5000/hello-world] 17eec7bbc9d7: Layer already exists v1: digest: sha256:2771e37a12b7bcb2902456ecf3f29bf9ee11ec348e66e8eb322d9780ad7fc2df size: 1035 i Info → Not all multiplatform-content is present and only the available single-platform image was pushed sha256:d4aaab6242e0cace87e2ec17a2ed3d779d18fbfd03042ea58f2995626396a274 -> sha256:2771e37a12b7bcb2902456ecf3f29bf9ee11ec348e66e8eb322d9780ad7fc2df kc@kchomelab:~/Desktop/karchunt.com$ docker pull localhost:5000/hello-world:v1 v1: Pulling from hello-world Digest: sha256:2771e37a12b7bcb2902456ecf3f29bf9ee11ec348e66e8eb322d9780ad7fc2df Status: Downloaded newer image for localhost:5000/hello-world:v1 localhost:5000/hello-world:v1

Setup UI (Optional)

By default, the official Docker Registry image does not come with a UI. Therefore, we’re going to use the joxit/docker-registry-ui  image to setup a UI for our registry.

docker-compose.yml
name: Registry services: registry: image: registry:3 container_name: registry ports: - "5000:5000" restart: unless-stopped environment: REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /var/lib/registry REGISTRY_HTTP_HEADERS_Access-Control-Allow-Origin: '[http://registry-ui.example.com]' REGISTRY_HTTP_HEADERS_Access-Control-Allow-Methods: '[HEAD,GET,OPTIONS,DELETE]' REGISTRY_HTTP_HEADERS_Access-Control-Allow-Credentials: '[true]' REGISTRY_HTTP_HEADERS_Access-Control-Allow-Headers: '[Authorization,Accept,Cache-Control]' REGISTRY_HTTP_HEADERS_Access-Control-Expose-Headers: '[Docker-Content-Digest]' REGISTRY_STORAGE_DELETE_ENABLED: 'true' volumes: - ./registry-data:/var/lib/registry registry-ui: image: joxit/docker-registry-ui:latest container_name: registry-ui ports: - "5001:80" restart: unless-stopped environment: - SINGLE_REGISTRY=true - REGISTRY_TITLE=Docker Registry UI - DELETE_IMAGES=true - SHOW_CONTENT_DIGEST=true - NGINX_PROXY_PASS_URL=http://registry:5000 - SHOW_CATALOG_NB_TAGS=true - CATALOG_MIN_BRANCHES=1 - CATALOG_MAX_BRANCHES=1 - TAGLIST_PAGE_SIZE=100 - REGISTRY_SECURED=false - CATALOG_ELEMENTS_LIMIT=1000 depends_on: - registry

Once you have added the registry-ui service to your docker-compose.yml file, you can run the following command to start the registry and the UI:

docker compose up -d

You can now access the UI at http://localhost:5001.

Last updated on