Enterprise Docker Registry Security: Private Registry Implementation with High Availability and Compliance for Kubernetes
Private Docker registries serve as critical infrastructure components in enterprise environments, managing container images that power mission-critical applications across distributed Kubernetes clusters. Securing these registries requires sophisticated approaches to authentication, authorization, vulnerability management, and compliance monitoring. This comprehensive implementation guide demonstrates enterprise-grade Docker registry deployment patterns with advanced security hardening, high availability architecture, and production-ready operational practices.
Executive Summary
Enterprise container registries must balance accessibility for development workflows with stringent security requirements for production deployments. Modern organizations require registry solutions that provide granular access controls, comprehensive vulnerability scanning, compliance reporting, and seamless integration with CI/CD pipelines. This implementation guide covers advanced Docker registry architecture patterns, security frameworks, high availability configurations, and operational excellence practices for mission-critical container infrastructure.
Understanding Enterprise Registry Architecture
Security-First Registry Design
Enterprise Docker registries must address multiple security domains:
- Image Security: Vulnerability scanning, malware detection, and content trust
- Access Control: Role-based authentication and fine-grained authorization
- Network Security: Transport encryption and network segmentation
- Compliance: Audit trails, policy enforcement, and regulatory requirements
- Operational Security: Backup encryption, disaster recovery, and incident response
Architecture Patterns
Hierarchical Registry Model:
Enterprise Registry Federation
├── Production Registry (Multi-Region)
│ ├── Verified Base Images
│ ├── Security-Scanned Applications
│ └── Signed Release Artifacts
├── Development Registry
│ ├── Development Images
│ ├── Feature Branch Builds
│ └── Experimental Containers
└── Staging Registry
├── Pre-Production Images
├── Integration Test Artifacts
└── Performance Test Images
Enterprise Docker Registry Deployment
High Availability Registry Configuration
Deploy Docker Registry with comprehensive enterprise features:
# docker-registry-values.yaml
image:
repository: registry
tag: 2.8.3
pullPolicy: IfNotPresent
replicaCount: 3
# Security configuration
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
# Resource management
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: 2000m
memory: 4Gi
# High availability
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- docker-registry
topologyKey: kubernetes.io/hostname
# Storage configuration
persistence:
enabled: true
storageClass: fast-ssd
size: 1Ti
accessMode: ReadWriteMany
# Redis for caching (optional)
redis:
enabled: true
host: redis-cluster.redis.svc.cluster.local
port: 6379
auth:
enabled: true
existingSecret: redis-auth
existingSecretPasswordKey: password
# Registry configuration
configData:
version: 0.1
# Logging configuration
log:
level: info
fields:
service: registry
environment: production
accesslog:
disabled: false
# Storage configuration with S3 backend
storage:
s3:
region: us-west-2
bucket: company-docker-registry
encrypt: true
storageclass: STANDARD_IA
rootdirectory: /registry
chunksize: 52428800 # 50MB chunks
cache:
blobdescriptor: redis
redirect:
disable: true
delete:
enabled: true
# HTTP configuration
http:
addr: :5000
headers:
X-Content-Type-Options: [nosniff]
X-Frame-Options: [DENY]
X-XSS-Protection: ["1; mode=block"]
Strict-Transport-Security: ["max-age=31536000; includeSubDomains"]
tls:
certificate: /etc/ssl/certs/registry.crt
key: /etc/ssl/private/registry.key
minimumtls: "1.2"
ciphersuites:
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
# Authentication configuration
auth:
htpasswd:
realm: "Docker Registry"
path: /etc/docker/registry/htpasswd
token:
realm: https://auth.registry.company.com/auth
service: Docker Registry
issuer: "Registry Auth Service"
rootcertbundle: /etc/ssl/certs/auth-ca.crt
# Health check configuration
health:
storagedriver:
enabled: true
interval: 10s
threshold: 3
# Proxy configuration for upstream registries
proxy:
remoteurl: https://registry-1.docker.io
username: dockerhub-user
password: dockerhub-password
ttl: 168h
# Notification webhooks
notifications:
endpoints:
- name: harbor-webhook
url: https://harbor.company.com/service/notifications
headers:
Authorization: ["Bearer webhook-token"]
events:
- name: push
action: push
- name: pull
action: pull
- name: delete
action: delete
# Service configuration
service:
type: ClusterIP
port: 5000
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: nlb
service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp
# Ingress with TLS termination
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: "production-ca-issuer"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "2048m"
nginx.ingress.kubernetes.io/client-max-body-size: "2048m"
hosts:
- host: registry.company.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: registry-tls
hosts:
- registry.company.com
# Monitoring
metrics:
enabled: true
port: 5001
serviceMonitor:
enabled: true
namespace: monitoring
Security-Hardened Deployment
Deploy with comprehensive security controls:
# Create namespace with security labels
kubectl create namespace docker-registry
kubectl label namespace docker-registry \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/audit=restricted \
pod-security.kubernetes.io/warn=restricted
# Create authentication secrets
kubectl create secret generic registry-auth \
--from-file=htpasswd=registry-htpasswd \
--namespace=docker-registry
# Create TLS certificates
kubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: registry-tls
namespace: docker-registry
spec:
secretName: registry-tls
issuerRef:
name: production-ca-issuer
kind: ClusterIssuer
commonName: registry.company.com
dnsNames:
- registry.company.com
- registry-internal.company.com
duration: 8760h
renewBefore: 720h
keyAlgorithm: RSA
keySize: 4096
EOF
# Deploy registry using Helm
helm repo add twuni https://helm.twun.io
helm repo update
helm install docker-registry twuni/docker-registry \
--namespace docker-registry \
--values docker-registry-values.yaml \
--wait \
--timeout 600s
Advanced Authentication and Authorization
OIDC Integration with Enterprise SSO
Configure enterprise authentication with fine-grained authorization:
# Registry authentication service
apiVersion: apps/v1
kind: Deployment
metadata:
name: registry-auth-service
namespace: docker-registry
spec:
replicas: 3
selector:
matchLabels:
app: registry-auth-service
template:
metadata:
labels:
app: registry-auth-service
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
containers:
- name: auth-service
image: cesanta/docker_auth:1.9.0
args:
- /config/auth_config.yml
ports:
- containerPort: 5001
env:
- name: OIDC_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: oidc-credentials
key: client-secret
volumeMounts:
- name: config
mountPath: /config
- name: certificates
mountPath: /certificates
readOnly: true
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
volumes:
- name: config
configMap:
name: auth-config
- name: certificates
secret:
secretName: auth-service-tls
---
# Authentication configuration
apiVersion: v1
kind: ConfigMap
metadata:
name: auth-config
namespace: docker-registry
data:
auth_config.yml: |
server:
addr: ":5001"
certificate: "/certificates/tls.crt"
key: "/certificates/tls.key"
token:
issuer: "Docker Registry Auth"
expiration: 900
certificate: "/certificates/tls.crt"
key: "/certificates/tls.key"
# OIDC authentication
authn:
oidc:
issuer: "https://sso.company.com"
client_id: "docker-registry"
client_secret: "${OIDC_CLIENT_SECRET}"
redirect_url: "https://auth.registry.company.com/auth/callback"
scopes: ["openid", "profile", "email", "groups"]
# Authorization policies
authz:
- match: {account: "admin"}
actions: ["*"]
comment: "Admin full access"
- match: {account: "/.+/", name: "${account}/*"}
actions: ["pull", "push"]
comment: "User namespace access"
- match: {account: "/.+/", type: "repository", name: "library/*"}
actions: ["pull"]
comment: "Public library read access"
- match: {account: "/.+/", type: "repository", name: "production/*"}
actions: ["pull"]
conditions:
- groups: ["production-users", "developers"]
comment: "Production images pull access"
- match: {account: "/.+/", type: "repository", name: "development/*"}
actions: ["pull", "push", "delete"]
conditions:
- groups: ["developers"]
comment: "Development environment full access"
# Service account access for CI/CD
- match: {account: "service-ci"}
actions: ["pull", "push"]
conditions:
- name: "ci/*"
comment: "CI service account"
- match: {account: "service-deployment"}
actions: ["pull"]
conditions:
- name: "production/*"
comment: "Deployment service account"
Role-Based Access Control (RBAC)
Implement comprehensive RBAC for registry access:
# Registry RBAC service account
apiVersion: v1
kind: ServiceAccount
metadata:
name: registry-rbac
namespace: docker-registry
automountServiceAccountToken: false
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: registry-rbac
rules:
# Registry user management
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
resourceNames: ["registry-users", "registry-policies"]
# Secret management for authentication
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch"]
resourceNames: ["registry-auth", "oidc-credentials"]
# Certificate management
- apiGroups: ["cert-manager.io"]
resources: ["certificates"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: registry-rbac
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: registry-rbac
subjects:
- kind: ServiceAccount
name: registry-rbac
namespace: docker-registry
---
# User management API
apiVersion: apps/v1
kind: Deployment
metadata:
name: registry-user-management
namespace: docker-registry
spec:
replicas: 2
selector:
matchLabels:
app: registry-user-management
template:
metadata:
labels:
app: registry-user-management
spec:
serviceAccountName: registry-rbac
containers:
- name: user-management-api
image: registry-user-management:v1.2.0
ports:
- containerPort: 8080
env:
- name: REGISTRY_URL
value: "https://registry.company.com"
- name: AUTH_SERVICE_URL
value: "https://auth.registry.company.com"
- name: KUBERNETES_NAMESPACE
value: "docker-registry"
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
Image Security and Vulnerability Management
Integrated Vulnerability Scanning
Deploy comprehensive vulnerability scanning pipeline:
# Trivy vulnerability scanner
apiVersion: apps/v1
kind: Deployment
metadata:
name: trivy-scanner
namespace: docker-registry
spec:
replicas: 2
selector:
matchLabels:
app: trivy-scanner
template:
metadata:
labels:
app: trivy-scanner
spec:
containers:
- name: trivy-scanner
image: aquasec/trivy:0.48.3
command: ["trivy"]
args:
- "server"
- "--listen"
- "0.0.0.0:4954"
- "--cache-dir"
- "/tmp/trivy"
ports:
- containerPort: 4954
env:
- name: TRIVY_AUTH_URL
value: "https://registry.company.com"
- name: TRIVY_USERNAME
valueFrom:
secretKeyRef:
name: trivy-credentials
key: username
- name: TRIVY_PASSWORD
valueFrom:
secretKeyRef:
name: trivy-credentials
key: password
volumeMounts:
- name: cache
mountPath: /tmp/trivy
- name: tmp
mountPath: /tmp
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: 2000m
memory: 4Gi
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
volumes:
- name: cache
emptyDir:
sizeLimit: 10Gi
- name: tmp
emptyDir:
sizeLimit: 1Gi
---
# Admission controller for image scanning
apiVersion: apps/v1
kind: Deployment
metadata:
name: image-scan-admission-controller
namespace: docker-registry
spec:
replicas: 3
selector:
matchLabels:
app: image-scan-admission-controller
template:
metadata:
labels:
app: image-scan-admission-controller
spec:
containers:
- name: admission-controller
image: image-scan-admission-controller:v1.5.0
ports:
- containerPort: 8443
env:
- name: TRIVY_SERVER_URL
value: "http://trivy-scanner:4954"
- name: REGISTRY_URL
value: "https://registry.company.com"
- name: POLICY_CONFIG
value: "/config/policy.yaml"
volumeMounts:
- name: certs
mountPath: /certs
readOnly: true
- name: policy-config
mountPath: /config
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 1000m
memory: 1Gi
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
volumes:
- name: certs
secret:
secretName: admission-controller-certs
- name: policy-config
configMap:
name: scan-policy-config
---
# Scanning policy configuration
apiVersion: v1
kind: ConfigMap
metadata:
name: scan-policy-config
namespace: docker-registry
data:
policy.yaml: |
# Vulnerability severity thresholds
severity_thresholds:
production:
max_critical: 0
max_high: 2
max_medium: 10
development:
max_critical: 5
max_high: 20
max_medium: 50
# Image signing requirements
signature_requirements:
production: true
staging: true
development: false
# Base image allowlist
approved_base_images:
- "registry.company.com/base/alpine:*"
- "registry.company.com/base/ubuntu:20.04"
- "registry.company.com/base/distroless/*"
# Prohibited packages
prohibited_packages:
- name: "curl"
reason: "Use wget instead for smaller attack surface"
- name: "telnet"
reason: "Insecure protocol"
- name: "ftp"
reason: "Insecure protocol"
# License compliance
prohibited_licenses:
- "GPL-3.0"
- "AGPL-3.0"
- "LGPL-3.0"
# Malware scanning
malware_scanning:
enabled: true
max_scan_time: 300s
quarantine_on_detection: true
Content Trust and Image Signing
Implement Docker Content Trust with Notary:
# Notary server for image signing
apiVersion: apps/v1
kind: Deployment
metadata:
name: notary-server
namespace: docker-registry
spec:
replicas: 2
selector:
matchLabels:
app: notary-server
template:
metadata:
labels:
app: notary-server
spec:
containers:
- name: notary-server
image: notary:server-0.7.0
ports:
- containerPort: 4443
env:
- name: NOTARY_SERVER_STORAGE_BACKEND
value: "mysql"
- name: NOTARY_SERVER_STORAGE_DB_URL
valueFrom:
secretKeyRef:
name: notary-db-credentials
key: url
- name: NOTARY_SERVER_TRUST_SERVICE_TYPE
value: "local"
- name: NOTARY_SERVER_LOGGING_LEVEL
value: "info"
volumeMounts:
- name: config
mountPath: /etc/notary
- name: certificates
mountPath: /certs
readOnly: true
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 1000m
memory: 1Gi
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
volumes:
- name: config
configMap:
name: notary-server-config
- name: certificates
secret:
secretName: notary-server-certs
---
# Notary signer for key management
apiVersion: apps/v1
kind: Deployment
metadata:
name: notary-signer
namespace: docker-registry
spec:
replicas: 2
selector:
matchLabels:
app: notary-signer
template:
metadata:
labels:
app: notary-signer
spec:
containers:
- name: notary-signer
image: notary:signer-0.7.0
ports:
- containerPort: 7899
env:
- name: NOTARY_SIGNER_STORAGE_BACKEND
value: "mysql"
- name: NOTARY_SIGNER_STORAGE_DB_URL
valueFrom:
secretKeyRef:
name: notary-db-credentials
key: url
- name: NOTARY_SIGNER_LOGGING_LEVEL
value: "info"
volumeMounts:
- name: config
mountPath: /etc/notary
- name: certificates
mountPath: /certs
readOnly: true
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
volumes:
- name: config
configMap:
name: notary-signer-config
- name: certificates
secret:
secretName: notary-signer-certs
Network Security and Compliance
Network Segmentation and Policies
Implement comprehensive network security:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: docker-registry-policy
namespace: docker-registry
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: docker-registry
policyTypes:
- Ingress
- Egress
ingress:
# Allow ingress from ingress controllers
- from:
- namespaceSelector:
matchLabels:
name: ingress-system
ports:
- protocol: TCP
port: 5000
# Allow monitoring
- from:
- namespaceSelector:
matchLabels:
name: monitoring
ports:
- protocol: TCP
port: 5001 # Metrics
# Allow authentication service
- from:
- podSelector:
matchLabels:
app: registry-auth-service
ports:
- protocol: TCP
port: 5000
egress:
# Allow S3 storage backend
- to: []
ports:
- protocol: TCP
port: 443
- protocol: TCP
port: 80
# Allow Redis caching
- to:
- namespaceSelector:
matchLabels:
name: redis
ports:
- protocol: TCP
port: 6379
# Allow DNS
- to: []
ports:
- protocol: TCP
port: 53
- protocol: UDP
port: 53
---
# Vulnerability scanner network policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: trivy-scanner-policy
namespace: docker-registry
spec:
podSelector:
matchLabels:
app: trivy-scanner
policyTypes:
- Ingress
- Egress
ingress:
# Allow connections from admission controller
- from:
- podSelector:
matchLabels:
app: image-scan-admission-controller
ports:
- protocol: TCP
port: 4954
# Allow monitoring
- from:
- namespaceSelector:
matchLabels:
name: monitoring
ports:
- protocol: TCP
port: 4954
egress:
# Allow registry access for scanning
- to:
- podSelector:
matchLabels:
app.kubernetes.io/name: docker-registry
ports:
- protocol: TCP
port: 5000
# Allow vulnerability database updates
- to: []
ports:
- protocol: TCP
port: 443
# Allow DNS
- to: []
ports:
- protocol: TCP
port: 53
- protocol: UDP
port: 53
Compliance and Audit Logging
Implement comprehensive audit and compliance monitoring:
# Audit logging configuration
apiVersion: v1
kind: ConfigMap
metadata:
name: registry-audit-config
namespace: docker-registry
data:
fluent-bit.conf: |
[SERVICE]
Flush 1
Log_Level info
Daemon off
Parsers_File parsers.conf
[INPUT]
Name tail
Path /var/log/registry/*.log
Parser json
Tag registry.*
Refresh_Interval 5
Mem_Buf_Limit 50MB
[INPUT]
Name tail
Path /var/log/auth/*.log
Parser json
Tag auth.*
Refresh_Interval 5
Mem_Buf_Limit 50MB
[FILTER]
Name kubernetes
Match *
Kube_URL https://kubernetes.default.svc:443
Kube_CA_File /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
Kube_Token_File /var/run/secrets/kubernetes.io/serviceaccount/token
Merge_Log On
[FILTER]
Name modify
Match *
Add compliance_domain container_registry
Add security_classification high
Add retention_period 7_years
[OUTPUT]
Name forward
Match *
Host audit-log-collector.compliance.svc.cluster.local
Port 24224
tls on
tls.verify on
tls.ca_file /certs/ca.crt
tls.crt_file /certs/client.crt
tls.key_file /certs/client.key
parsers.conf: |
[PARSER]
Name json
Format json
Time_Key timestamp
Time_Format %Y-%m-%dT%H:%M:%S.%L%z
Time_Keep On
---
# Compliance monitoring deployment
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: registry-audit-logger
namespace: docker-registry
spec:
selector:
matchLabels:
app: registry-audit-logger
template:
metadata:
labels:
app: registry-audit-logger
spec:
containers:
- name: fluent-bit
image: fluent/fluent-bit:2.2.0
volumeMounts:
- name: config
mountPath: /fluent-bit/etc/
- name: registry-logs
mountPath: /var/log/registry
readOnly: true
- name: auth-logs
mountPath: /var/log/auth
readOnly: true
- name: certs
mountPath: /certs
readOnly: true
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
volumes:
- name: config
configMap:
name: registry-audit-config
- name: registry-logs
hostPath:
path: /var/log/containers/docker-registry-*
- name: auth-logs
hostPath:
path: /var/log/containers/registry-auth-*
- name: certs
secret:
secretName: audit-client-certs
tolerations:
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
High Availability and Disaster Recovery
Multi-Region Registry Replication
Implement cross-region registry replication:
# Registry replication controller
apiVersion: apps/v1
kind: Deployment
metadata:
name: registry-replication-controller
namespace: docker-registry
spec:
replicas: 2
selector:
matchLabels:
app: registry-replication-controller
template:
metadata:
labels:
app: registry-replication-controller
spec:
containers:
- name: replication-controller
image: registry-replication:v1.3.0
env:
- name: SOURCE_REGISTRY
value: "https://registry.company.com"
- name: TARGET_REGISTRIES
value: "https://registry-eu.company.com,https://registry-ap.company.com"
- name: REPLICATION_INTERVAL
value: "300s"
- name: CONCURRENT_REPLICATIONS
value: "5"
- name: AWS_REGION
value: "us-west-2"
- name: S3_BUCKET_SOURCE
value: "company-docker-registry-us-west-2"
- name: S3_BUCKET_EU
value: "company-docker-registry-eu-west-1"
- name: S3_BUCKET_AP
value: "company-docker-registry-ap-southeast-1"
volumeMounts:
- name: aws-credentials
mountPath: /root/.aws
readOnly: true
- name: registry-config
mountPath: /config
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 1000m
memory: 1Gi
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
volumes:
- name: aws-credentials
secret:
secretName: aws-replication-credentials
- name: registry-config
configMap:
name: replication-config
---
# Replication configuration
apiVersion: v1
kind: ConfigMap
metadata:
name: replication-config
namespace: docker-registry
data:
replication.yaml: |
# Replication policies
policies:
- name: production-images
source_namespace: "production/*"
targets:
- registry: "https://registry-eu.company.com"
namespace: "production/*"
- registry: "https://registry-ap.company.com"
namespace: "production/*"
sync_mode: "push"
deletion_policy: "never"
- name: base-images
source_namespace: "base/*"
targets:
- registry: "https://registry-eu.company.com"
namespace: "base/*"
- registry: "https://registry-ap.company.com"
namespace: "base/*"
sync_mode: "push"
deletion_policy: "sync"
# Filtering rules
filters:
- type: "tag_pattern"
pattern: "^(latest|v\\d+\\.\\d+\\.\\d+)$"
action: "include"
- type: "age"
max_age: "30d"
action: "include"
- type: "vulnerability_severity"
max_severity: "medium"
action: "exclude"
Backup and Recovery Procedures
Implement comprehensive backup strategies:
#!/bin/bash
# registry-backup.sh
BACKUP_DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/backup/registry-${BACKUP_DATE}"
NAMESPACE="docker-registry"
mkdir -p "$BACKUP_DIR"
echo "Starting Docker Registry backup..."
# Backup registry configuration
kubectl get configmap,secret -n "$NAMESPACE" -o yaml > "$BACKUP_DIR/registry-config.yaml"
# Backup authentication data
kubectl get secret registry-auth -n "$NAMESPACE" -o yaml > "$BACKUP_DIR/registry-auth.yaml"
# Backup TLS certificates
kubectl get certificates,secrets -n "$NAMESPACE" -l cert-manager.io/certificate-name -o yaml > "$BACKUP_DIR/certificates.yaml"
# Backup database (if using external DB)
if [ -n "$POSTGRES_HOST" ]; then
pg_dump -h "$POSTGRES_HOST" -U "$POSTGRES_USER" -d registry > "$BACKUP_DIR/registry-db.sql"
fi
# Backup S3 metadata (registry blobs are already in S3)
aws s3 sync s3://company-docker-registry/docker/registry/v2/ "$BACKUP_DIR/s3-metadata/" \
--exclude "*/blobs/*" \
--include "*/_manifests/*" \
--include "*/current/*" \
--include "*/repositories/*"
# Create backup manifest
cat << EOF > "$BACKUP_DIR/backup-manifest.yaml"
apiVersion: v1
kind: ConfigMap
metadata:
name: backup-manifest
namespace: docker-registry
data:
backup_date: "$BACKUP_DATE"
backup_type: "full"
kubernetes_version: "$(kubectl version --short --client)"
registry_version: "$(kubectl get deployment docker-registry -n docker-registry -o jsonpath='{.spec.template.spec.containers[0].image}')"
s3_bucket: "company-docker-registry"
components: |
- registry-configuration
- authentication-data
- tls-certificates
- database-dump
- s3-metadata
EOF
# Encrypt backup
if command -v gpg &> /dev/null && [ -n "$BACKUP_GPG_KEY" ]; then
echo "Encrypting backup..."
tar czf - "$BACKUP_DIR" | gpg --trust-model always --encrypt --armor \
--recipient "$BACKUP_GPG_KEY" > "$BACKUP_DIR.tar.gz.gpg"
rm -rf "$BACKUP_DIR"
echo "Encrypted backup created: $BACKUP_DIR.tar.gz.gpg"
else
echo "Backup created: $BACKUP_DIR"
fi
# Upload to secure backup storage
if [ -n "$BACKUP_S3_BUCKET" ]; then
aws s3 cp "$BACKUP_DIR.tar.gz.gpg" "s3://$BACKUP_S3_BUCKET/registry-backups/" \
--sse AES256 \
--metadata "backup-date=$BACKUP_DATE,component=docker-registry"
fi
echo "Backup completed successfully"
Monitoring and Observability
Comprehensive Registry Monitoring
Deploy advanced monitoring for registry operations:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: docker-registry
namespace: monitoring
spec:
namespaceSelector:
matchNames:
- docker-registry
selector:
matchLabels:
app.kubernetes.io/name: docker-registry
endpoints:
- port: metrics
interval: 30s
path: /metrics
honorLabels: true
metricRelabelings:
- sourceLabels: [__name__]
regex: 'registry_.*'
targetLabel: __name__
replacement: '${1}'
---
# Custom metrics for registry operations
apiVersion: v1
kind: ConfigMap
metadata:
name: registry-metrics-config
namespace: docker-registry
data:
metrics.yaml: |
# Custom registry metrics
collectors:
- name: image_pull_metrics
query: |
SELECT
namespace,
repository,
tag,
COUNT(*) as pull_count,
MAX(timestamp) as last_pull
FROM registry_events
WHERE action = 'pull'
AND timestamp > NOW() - INTERVAL 24 HOUR
GROUP BY namespace, repository, tag
- name: vulnerability_metrics
query: |
SELECT
namespace,
repository,
tag,
severity,
COUNT(*) as vulnerability_count
FROM vulnerability_scans s
JOIN images i ON s.image_id = i.id
WHERE s.status = 'completed'
GROUP BY namespace, repository, tag, severity
- name: storage_metrics
query: |
SELECT
namespace,
SUM(size_bytes) as total_size_bytes,
COUNT(DISTINCT repository) as repository_count,
COUNT(*) as image_count
FROM images
GROUP BY namespace
Alerting and Incident Response
Configure comprehensive alerting:
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: docker-registry-alerts
namespace: monitoring
spec:
groups:
- name: docker-registry
rules:
- alert: RegistryDown
expr: up{job="docker-registry"} == 0
for: 5m
labels:
severity: critical
component: registry
annotations:
summary: "Docker Registry is down"
description: "Docker Registry has been down for more than 5 minutes"
runbook_url: "https://runbooks.company.com/registry/registry-down"
- alert: RegistryHighLatency
expr: histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket{job="docker-registry"}[5m])) by (le)) > 5
for: 10m
labels:
severity: warning
component: registry
annotations:
summary: "Docker Registry high latency"
description: "Docker Registry 99th percentile latency is {{ $value }}s"
- alert: RegistryStorageAlmostFull
expr: (registry_storage_used_bytes / registry_storage_total_bytes) * 100 > 85
for: 15m
labels:
severity: warning
component: registry
annotations:
summary: "Docker Registry storage almost full"
description: "Docker Registry storage usage is {{ $value }}%"
- alert: HighVulnerabilityImagePushed
expr: increase(registry_vulnerability_scan_total{severity="critical"}[10m]) > 0
for: 0s
labels:
severity: critical
component: security
annotations:
summary: "High vulnerability image pushed to registry"
description: "Image with critical vulnerabilities pushed to {{ $labels.repository }}"
runbook_url: "https://runbooks.company.com/security/high-vulnerability-image"
- alert: UnauthorizedRegistryAccess
expr: increase(registry_http_requests_total{code=~"401|403"}[5m]) > 10
for: 2m
labels:
severity: warning
component: security
annotations:
summary: "Unauthorized registry access attempts"
description: "{{ $value }} unauthorized access attempts in the last 5 minutes"
- alert: RegistryReplicationFailed
expr: increase(registry_replication_failures_total[15m]) > 0
for: 0s
labels:
severity: warning
component: replication
annotations:
summary: "Registry replication failed"
description: "Registry replication to {{ $labels.target_registry }} failed"
Conclusion
Enterprise Docker registry implementation requires sophisticated approaches to security, compliance, and operational excellence. This comprehensive deployment demonstrates advanced patterns that ensure image security, access control, high availability, and regulatory compliance for mission-critical container infrastructure.
Key benefits of this enterprise registry implementation include:
- Advanced Security: Multi-layered security with vulnerability scanning, content trust, and access controls
- Compliance Integration: Comprehensive audit trails and policy enforcement
- High Availability: Multi-region replication and disaster recovery capabilities
- Operational Excellence: Advanced monitoring, alerting, and automated remediation
- Developer Productivity: Seamless integration with CI/CD pipelines and development workflows
- Cost Optimization: Efficient storage management and resource utilization
Regular security assessments, compliance audits, and disaster recovery testing ensure the continued effectiveness of the registry infrastructure. Consider implementing additional security measures such as zero-trust networking, advanced threat detection, and automated incident response based on organizational security requirements.
The patterns demonstrated here provide a solid foundation for implementing production-grade container registry solutions that scale from hundreds to thousands of images while maintaining security, compliance, and operational efficiency across complex enterprise environments.