Skip to main content

Usage Guide

Practical examples for common deployment scenarios. For the full list of configuration values, see configuration.md.

Pushing and pulling images

Port-forward (or expose via networking — see below) and use the Docker CLI. With the default ClusterIP service:

kubectl port-forward svc/my-registry-docker-registry 5000:5000 &

# tag and push
docker tag my-image:latest localhost:5000/my-image:latest
docker push localhost:5000/my-image:latest

# pull
docker pull localhost:5000/my-image:latest

# list repositories / tags via the v2 API
curl http://localhost:5000/v2/_catalog
curl http://localhost:5000/v2/my-image/tags/list

If htpasswd auth is enabled, run docker login localhost:5000 first.


Enabling htpasswd authentication

Generate an htpasswd entry and pass it via secrets.htpasswd. The -B flag (bcrypt) is required — the registry rejects other hash formats:

docker run --rm --entrypoint htpasswd httpd:2 -Bbn user 'password' > ./htpasswd
helm install my-registry docker-registry/docker-registry \
--set-file secrets.htpasswd=./htpasswd

The chart defaults to networking.type: gateway. Set httproute.parentRefs to point at your Gateway and the HTTPRoute is created automatically. Requires the Gateway API CRDs in the cluster.

# values.yaml
networking:
type: gateway # already the default — explicit here for clarity
httproute:
parentRefs:
- name: my-gateway
namespace: gateway-system
sectionName: https
hostnames:
- registry.example.com

No HTTPRoute is created until parentRefs is populated, so a plain helm install is safe on clusters without Gateway API CRDs.


Exposing via a classic Ingress

If your cluster does not have Gateway API CRDs, use networking.type: ingress:

networking:
type: ingress
ingress:
className: nginx
hosts:
- registry.example.com
tls:
- secretName: registry-tls
hosts:
- registry.example.com

Using an existing Secret

To manage registry credentials outside the chart (e.g. with external-secrets or sealed-secrets), set existingSecret. The chart skips creating its own Secret and references yours instead. The Secret must contain the keys required by your configuration (haSharedSecret, htpasswd, the relevant azure*/s3*/ swift* storage keys, and proxyUsername/proxyPassword when proxying):

helm install my-registry docker-registry/docker-registry \
--set existingSecret=my-registry-credentials

StatefulSet mode

Set useStatefulSet: true to deploy a StatefulSet with volumeClaimTemplates instead of a Deployment + standalone PVC. Useful when each replica needs its own volume:

helm install my-registry docker-registry/docker-registry \
--set useStatefulSet=true \
--set persistence.enabled=true \
--set persistence.storageClass=gp2

Arbitrary registry configuration via configData

configData is rendered verbatim into the registry's config.yml, so any Distribution config key can be set without a dedicated value. For example, to disable storage redirects (useful when S3 is not directly reachable by clients):

configData:
storage:
redirect:
disable: true

The same approach covers options like storage.maintenance.uploadpurging, storage.delete.enabled, and custom http.headers.


S3 with an IAM instance profile / IRSA

When the node or pod identity already grants S3 access, omit secrets.s3 entirely — the chart emits no static-credential env vars and the registry falls back to the AWS credential chain:

storage: s3
s3:
region: us-east-1
bucket: my-registry-bucket
# no secrets.s3 block -> uses the instance profile / IRSA role

Serving HTTPS (TLS)

Two ways to terminate TLS at the registry:

Existing Secret — reference a kubernetes.io/tls Secret you manage (cert-manager, etc.):

tlsSecretName: registry-tls

Inline cert/key — let the chart create the Secret from PEM material (tlsSecretName takes precedence if both are set):

tls:
crt: |
-----BEGIN CERTIFICATE-----
...
key: |
-----BEGIN PRIVATE KEY-----
...

Either way the registry mounts the cert at /etc/ssl/docker, serves HTTPS, and the probes switch to the HTTPS scheme.


Redis blob descriptor cache

For multi-replica HA, back the blob descriptor cache with Redis. Put the connection settings in configData (rendered into config.yml) and the password under redis so it stays out of the ConfigMap (injected as REGISTRY_REDIS_PASSWORD):

configData:
storage:
cache:
blobdescriptor: redis
redis:
addr: redis-master:6379
db: 0
dialtimeout: 10ms
readtimeout: 10ms
writetimeout: 10ms
redis:
password: my-redis-password
# or reference an external Secret with a redisPassword key:
# secretRef: my-redis-secret

Token authentication / RBAC

Delegate authorization to an external token server by passing the registry's auth.token config through configData, and mount the issuer's root cert bundle with extraVolumes/extraVolumeMounts:

configData:
auth:
token:
realm: "https://auth.example.com/token"
service: "container_registry"
issuer: "auth-server"
rootcertbundle: "/etc/docker/registry/auth.crt"
extraVolumes:
- name: auth-cert
secret:
secretName: registry-auth-cert
extraVolumeMounts:
- name: auth-cert
mountPath: /etc/docker/registry/auth.crt
subPath: auth.crt
readOnly: true

Trusting a private CA

To make the registry (e.g. as a proxy/mirror) trust an upstream served by a private CA, mount the CA bundle into the system trust store via extraVolumes/ extraVolumeMounts:

extraVolumes:
- name: private-ca
configMap:
name: my-private-ca # contains ca.crt
extraVolumeMounts:
- name: private-ca
mountPath: /etc/ssl/certs/my-private-ca.crt
subPath: ca.crt
readOnly: true

Running as a pull-through cache / mirror

Configure the registry to proxy and cache another registry (e.g. Docker Hub):

proxy:
enabled: true
remoteurl: https://registry-1.docker.io
username: my-dockerhub-user
password: my-dockerhub-token

See the Distribution mirror documentation for client-side configuration.