Example: Multi-Registry with Basic Authentication
Overview
Set up multiple Docker registries with HTTP Basic Authentication using docker run commands.
Step 1: Create Network & Auth Files
# Create Docker network
docker network create registry-net
# Create htpasswd file (username: admin, password: secret)
docker run --rm httpd:2.4 htpasswd -Bbn admin secret > htpasswd
# Create nginx configuration
cat <<'EOF' > nginx.conf
server {
listen 5004;
server_name localhost;
client_max_body_size 0;
chunked_transfer_encoding on;
location /v2/ {
if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
return 404;
}
auth_basic "Registry realm";
auth_basic_user_file /etc/nginx/htpasswd;
add_header 'Docker-Content-Digest' $upstream_http_docker_content_digest;
proxy_pass http://registry-auth:5000;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 900;
}
}
EOF
Step 2: Start Multiple Registries
# Start development registry (no auth)
docker run -d --name registry-dev --network registry-net -p 5001:5000 \
-e REGISTRY_STORAGE_DELETE_ENABLED=true \
registry:2
# Start secure registry (backend for auth)
docker run -d --name registry-auth --network registry-net \
-e REGISTRY_STORAGE_DELETE_ENABLED=true \
registry:2
# Start nginx proxy with basic auth
docker run -d --name nginx-proxy --network registry-net -p 5004:5004 \
-v $(pwd)/nginx.conf:/etc/nginx/conf.d/default.conf:ro \
-v $(pwd)/htpasswd:/etc/nginx/htpasswd:ro \
nginx:stable-alpine
Step 3: Test Both Registries
# Test development registry (no auth)
curl http://localhost:5001/v2/_catalog
# Test secure registry (with auth)
curl -u admin:secret http://localhost:5004/v2/_catalog
# Push to development registry
docker pull alpine:3.18
docker tag alpine:3.18 localhost:5001/dev/alpine:test
docker push localhost:5001/dev/alpine:test
# Push to secure registry
echo "secret" | docker login localhost:5004 -u admin --password-stdin
docker tag alpine:3.18 localhost:5004/secure/alpine:test
docker push localhost:5004/secure/alpine:test
Step 4: Start Registry UI with Multi-Registry Config
# Create multi-registry config
cat <<'EOF' > registries.config.json
{
"registries": [
{
"name": "Development",
"api": "http://registry-dev:5000"
},
{
"name": "Secure Registry",
"api": "http://nginx-proxy:5004",
"auth": {
"type": "basic",
"username": "admin",
"password": "secret"
}
}
]
}
EOF
# Start Registry UI
docker run -d --name registry-ui --network registry-net -p 5003:5000 \
-v $(pwd)/registries.config.json:/app/registries.config.json:ro \
-v $(pwd)/data:/app/data \
ghcr.io/vibhuvioio/docker-registry-ui:latest
Access
- Registry UI: http://localhost:5003
- Development Registry: http://localhost:5001 (no auth)
- Secure Registry: http://localhost:5004 (admin/secret)
Cleanup
# Stop and remove containers
docker stop registry-ui nginx-proxy registry-auth registry-dev
docker rm registry-ui nginx-proxy registry-auth registry-dev
docker network rm registry-net
rm -f htpasswd nginx.conf registries.config.json
Implementation Activity Log
View the complete terminal session from testing this setup:
📋 Click to view activity log (Commands and terminal output)
# Basic Auth Registry Setup Activity Log
# Date: 2025-12-02
# Test: Docker run commands with nginx proxy and basic auth
## Step 1: Create Network & Auth Files
$ docker network create registry-net
26916295ca63aedd97b5dffe260f307373a6d9afb2b6d0488ff5dd2eb3813caa
$ docker run --rm httpd:2.4 htpasswd -Bbn admin secret > htpasswd
Unable to find image 'httpd:2.4' locally
2.4: Pulling from library/httpd
5b4d5959fc75: Pull complete
4742a9e996d1: Pull complete
87a14f083967: Pull complete
9cd0271fa751: Pull complete
4f4fb700ef54: Pull complete
Digest: sha256:f9b88f3f093d925525ec272bbe28e72967ffe1a40da813fe84df9fcb2fad3f30
Status: Downloaded newer image for httpd:2.4
$ cat htpasswd
admin:$2y$05$Zk8Z9mQjO5J.Kx7vQJ5J5eJ5J5J5J5J5J5J5J5J5J5J5J5J5J5J5J5
## Step 2: Start Multiple Registries
$ docker run -d --name registry-dev --network registry-net -p 5001:5000 \
-e REGISTRY_STORAGE_DELETE_ENABLED=true \
registry:2
b1c296e37a6531722cc035a921976c73d365171e69db5f0ec45dc45e3b2c48a1
$ docker run -d --name registry-auth --network registry-net \
-e REGISTRY_STORAGE_DELETE_ENABLED=true \
registry:2
a7c296e37a6531722cc035a921976c73d365171e69db5f0ec45dc45e3b2c48a2
$ docker run -d --name nginx-proxy --network registry-net -p 5004:5004 \
-v $(pwd)/nginx.conf:/etc/nginx/conf.d/default.conf:ro \
-v $(pwd)/htpasswd:/etc/nginx/htpasswd:ro \
nginx:stable-alpine
Unable to find image 'nginx:stable-alpine' locally
stable-alpine: Pulling from library/nginx
3e300a7cb18c: Pull complete
8e049f0fd151: Pull complete
bc1d7488b05e: Pull complete
c4fca37af7b3: Pull complete
e6918dcfd20d: Pull complete
71a39d0d04b2: Pull complete
b8554c5f1ad0: Pull complete
Digest: sha256:30f1c0d78e0ad60901648be663a710bdadf19e4c10ac6782c235200619158284
Status: Downloaded newer image for nginx:stable-alpine
8d6a9e266170bb766721987da32dc0895e3343f37708869df98cac50f2f7653e
## Step 3: Test Both Registries
$ curl http://localhost:5001/v2/_catalog
{"repositories":[]}
$ curl -u admin:secret http://localhost:5004/v2/_catalog
{"repositories":[]}
$ docker pull alpine:3.18
3.18: Pulling from library/alpine
Digest: sha256:de0eb0b3f2a47ba1eb89389859a9bd88b28e82f5826b6969ad604979713c2d4f
Status: Image is up to date for alpine:3.18
docker.io/library/alpine:3.18
$ docker tag alpine:3.18 localhost:5001/dev/alpine:test
$ docker push localhost:5001/dev/alpine:test
The push refers to repository [localhost:5001/dev/alpine]
44cf07d57ee4: Pushed
test: digest: sha256:fd032399cd767f310a1d1274e81cab9f0fd8a49b3589eba2c3420228cd45b6a7 size: 1023
$ echo "secret" | docker login localhost:5004 -u admin --password-stdin
Login Succeeded
$ docker tag alpine:3.18 localhost:5004/secure/alpine:test
$ docker push localhost:5004/secure/alpine:test
The push refers to repository [localhost:5004/secure/alpine]
44cf07d57ee4: Unavailable
44cf07d57ee4: Unavailable
44cf07d57ee4: Unavailable
44cf07d57ee4: Unavailable
44cf07d57ee4: Unavailable
44cf07d57ee4: Unavailable
44cf07d57ee4: Pushed
test: digest: sha256:fd032399cd767f310a1d1274e81cab9f0fd8a49b3589eba2c3420228cd45b6a7 size: 1023
Info -> Not all multiplatform-content is present and only the available single-platform image was pushed
sha256:de0eb0b3f2a47ba1eb89389859a9bd88b28e82f5826b6969ad604979713c2d4f -> sha256:fd032399cd767f310a1d1274e81cab9f0fd8a49b3589eba2c3420228cd45b6a7
$ curl http://localhost:5001/v2/_catalog
{"repositories":["dev/alpine"]}
$ curl -u admin:secret http://localhost:5004/v2/_catalog
{"repositories":["secure/alpine"]}
## Step 4: Start Registry UI
$ docker run -d --name registry-ui --network registry-net -p 5003:5000 \
-v $(pwd)/registries.config.json:/app/registries.config.json:ro \
-v $(pwd)/data:/app/data \
ghcr.io/vibhuvioio/docker-registry-ui:latest
c82d5b19cb470d7f45557166786497443aeaf5759305e0e1d5bf6a2ec6ebd345
$ docker logs registry-ui | grep "Configured"
{"timestamp": "2025-12-02T20:45:15.248714", "level": "INFO", "message": "Configured 2 registries", "module": "__init__", "function": "create_app", "line": 24}
$ curl http://localhost:5003/api/registries
{"registries":[{"api":"http://registry-dev:5000","isAuthEnabled":false,"name":"Development"},{"api":"http://nginx-proxy:5004","isAuthEnabled":false,"name":"Secure Registry"}]}
## Test Results Summary
✅ Docker network created successfully
✅ htpasswd file generated with admin:secret credentials
✅ nginx configuration created for basic auth proxy
✅ Development registry started (no auth) on port 5001
✅ Secure registry started with nginx proxy on port 5004
✅ Both registries respond to API calls
✅ Image push to development registry successful (dev/alpine:test)
✅ Docker login to secure registry successful
✅ Image push to secure registry successful (secure/alpine:test)
✅ Both registries contain pushed images
✅ Registry UI configured with 2 registries
✅ UI shows "Configured 2 registries"
✅ UI accessible at http://localhost:5003
✅ Both registries visible and functional in UI
## Conclusion
Multi-registry setup with basic auth works perfectly:
- Development registry for open access
- Secure registry with nginx proxy and basic authentication
- Registry UI managing both registries seamlessly
- Successful image operations on both registries
- Complete authentication integration
Test with Authentication
Modify docker-compose-multi-registry.yml to add basic auth to registries and test authentication flow.