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
4. Add TLS with Nginx or Caddy (Recommended)
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:
- registry garbage collection
- Cronjob + cleanup script
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
Feature | Local Registry | Production-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!