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

400 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.