
Kar Chun Tan
Creator
Metadata
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.
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/registryRun the registry
Navigate to the directory containing the docker-compose.yml file and run:
docker compose up -dUse 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:v1Setup 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.
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:
- registryOnce 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 -dYou can now access the UI at http://localhost:5001.