Skip to content
BotServBotServ
DockerRegistryContainerSelf-HostingTLS

Self-Hosting Your Own Docker Registry

Host Docker Registry locally. Push, pull, authentication, TLS, and best practices for private image storage.

S

schutzgeist

3 min read
Self-Hosting Your Own Docker Registry

Running Your Own Docker Registry

What This Article Covers

  • What a Docker Registry is and when you need one.
  • How to run the official Registry.
  • Pushing and pulling images.
  • Authentication, TLS, and security.
  • Backup and maintenance tips.

Introduction: Running Your Own Docker Registry

A Docker Registry is a storage location for Docker images. By default, images come from Docker Hub, but sometimes you want to serve your own images locally or across an internal network. Your own Registry is useful for CI/CD pipelines, isolated environments, data privacy, or when Docker Hub is unavailable.

This article walks through running the official Docker Registry locally or in a homelab environment.

Key Concepts

  • Registry: Storage location for Docker images.
  • Repository: Collection of images under a single name.
  • Tag: Version identifier for an image.
  • Push: Upload an image to the Registry.
  • Pull: Download an image from the Registry.
  • TLS: Encrypted connection.
  • Basic Auth: Username and password access.
  • Garbage Collection: Cleanup of unreferenced layers.

When You Need Your Own Registry

  • Distribute custom images across an internal network.
  • Speed up CI/CD pipelines.
  • Avoid Docker Hub rate limits.
  • Keep data out of public registries.
  • Supply air-gapped environments.
  • Share build cache between hosts.

Starting the Registry

The official Registry runs as a simple container:

docker run -d -p 5000:5000 --name registry registry:2

Test it:

curl http://localhost:5000/v2/_catalog

Tagging and Pushing Images

Rename an existing image and upload it:

docker tag mein-image:latest localhost:5000/mein-image:latest
docker push localhost:5000/mein-image:latest

Pulling Images

docker pull localhost:5000/mein-image:latest

Persistent Storage with Volumes

docker run -d -p 5000:5000 --name registry \
  -v registry-data:/var/lib/registry \
  registry:2

Enabling TLS

For production or remote access, secure the Registry with TLS. You can use a certificate from a CA or a self-signed certificate.

Self-Signed Certificate

mkdir -p certs
openssl req -newkey rsa:4096 -nodes -sha256 -keyout certs/registry.key -x509 -days 365 -out certs/registry.crt

Run the Registry with the certificate:

docker run -d -p 5000:5000 --name registry \
  -v $(pwd)/certs:/certs \
  -v registry-data:/var/lib/registry \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/registry.crt \
  -e REGISTRY_HTTP_TLS_KEY=/certs/registry.key \
  registry:2

On the client side, accept the certificate:

mkdir -p /etc/docker/certs.d/localhost:5000
cp registry.crt /etc/docker/certs.d/localhost:5000/ca.crt

Authentication

Simple Basic Auth using htpasswd:

mkdir auth
docker run --rm --entrypoint htpasswd httpd:2 -Bbn admin meinpasswort > auth/htpasswd

Run the Registry with authentication:

docker run -d -p 5000:5000 --name registry \
  -v $(pwd)/auth:/auth \
  -e REGISTRY_AUTH=htpasswd \
  -e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \
  -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
  -v registry-data:/var/lib/registry \
  registry:2

Log in:

docker login localhost:5000

Registry with Compose

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

volumes:
  registry-data:

Garbage Collection

Unreferenced layers can consume storage space. Run a cleanup:

docker exec registry bin/registry garbage-collect /etc/docker/registry/config.yml

Backup

Registry data lives in /var/lib/registry. A simple backup approach:

docker run --rm -v registry-data:/data -v $(pwd):/backup alpine tar -czf /backup/registry-backup.tar.gz -C /data .

Security

  • Always use TLS for network access.
  • Enable authentication.
  • Never expose the Registry to the public internet.
  • Restrict access via VPN or Tailscale.
  • Apply backups and updates regularly.
  • Enable logging.

Common Pitfalls

  • No TLS: Newer Docker versions block insecure registries.
  • Insecure Registry: Possible but not recommended.
  • Wrong tag names: Image names must include the Registry host.
  • Authentication not configured: Push fails without login.
  • Storage fills up: Missing garbage collection.
  • Certificate not trusted: Client needs the CA certificate.

Further Reading and Resources

FAQ: Docker Registry

Do I need my own Registry? If you distribute many custom images internally, yes. For occasional images, Docker Hub is fine.

Can I make the Registry public? Only with TLS, auth, and rate limiting. Better to keep it internal.

Is the Registry scalable? A single container works for small setups. For larger scenarios, look at Distribution or Harbor.

How do I delete old images? Via API or garbage collection. Individual tags can be removed through the Registry API.

What is Harbor? An advanced open-source Registry project with a UI, authentication, and security scanning.

Sources and Further Reading

Running your own Docker Registry lets you serve images locally or across an internal network. It starts quickly as a container but should be secured with TLS and authentication for production or remote use. Keep tag names clean, run regular backups, enable garbage collection, and restrict network access. A well-managed Registry saves bandwidth, speeds up deployments, and keeps your images under your control.

Back to Blog
Share:

Related Posts