Need a secure, fast, and self-hosted solution for storing container images? Setting up a private Docker registry is easier than you think.

Why Run Your Own Docker Registry?

Running your own registry allows you to:

  • Avoid Docker Hub rate limits
  • Store proprietary images securely
  • Speed up CI/CD workflows
  • Control image retention and access

Great for air-gapped environments and enterprise deployments.

1. Prerequisites

You’ll need:

  • Docker installed
  • Access to a server or VM
  • Optional: domain name and TLS certs (for production)

2. Start a Local Registry (Quick Start)

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

This launches a registry on localhost:5000.

Push an image:

docker tag alpine localhost:5000/alpine
docker push localhost:5000/alpine

Pull it:

docker pull localhost:5000/alpine

3. Persistent Storage

For production, mount a volume:

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

Use a reverse proxy with HTTPS:

Example with Caddy:

registry.example.com {
    reverse_proxy localhost:5000
    tls you@example.com
}

Run Caddy:

docker run -d \
  -v $PWD/Caddyfile:/etc/caddy/Caddyfile \
  -p 80:80 -p 443:443 \
  --name caddy \
  caddy

Now access via: https://registry.example.com

Update Docker daemon config on clients:

{
  "insecure-registries" : ["registry.example.com"]
}

Tip: Use a self-signed cert only for testing. Always use real HTTPS in production.

5. Authentication (Optional)

Use the registry auth token method or a basic auth proxy.

Basic example:

mkdir auth
docker run --entrypoint htpasswd httpd:2 -Bbn user pass > auth/htpasswd

Mount it:

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" \
  registry:2

6. Clean Up Old Images

Registry doesn’t automatically delete old blobs. Use:

7. Bonus: Use with Docker Compose

Create a directory and generate a user for basic authentication:

mkdir -p ~/docker-registry/auth 
cd ~/docker-registry/auth
htpasswd -Bc registry.passwd myuser

Create docker-compose:

vi ~/docker-registry/docker-compose.yml
version: '3'
services:
  registry:
    image: registry:2
    ports:
      - 5000:5000
    environment:
      REGISTRY_AUTH: htpasswd
      REGISTRY_AUTH_HTPASSWD_REALM: Registry-Realm
      REGISTRY_AUTH_HTPASSWD_PATH: /auth/registry.passwd
      REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
    volumes:
      - registry-data:/data
      - ./auth:/auth
volumes:
  registry-data:

Run with:

cd ~/docker-registry
docker compose up -d

Summary

FeatureLocal RegistryProduction-Ready
Persistent storage❌ No✅ Yes
TLS encryption❌ No✅ Yes (via proxy)
Authentication❌ No✅ Recommended
Cleanup support⚠️ Manual⚠️ Manual

Final Thoughts

Setting up a private Docker registry is quick and provides full control over your container images. It’s an essential step for secure, scalable DevOps pipelines.


Related Reading

Need help with advanced setups or automation? Leave a comment or reach out!