Setting up a Docker Registry backed with Cloudflare R2

You can run a private Docker registry using Cloudflare R2 as your storage backend, in this tutorial we're going to configure it in a Kubernetes cluster. Creating bucket and access keys First, we need to create a R2 bucket where we'll store the Docker images. We also need an API access key and secret key to access the bucket. Once we have created the bucket and have both keys, we create a secret. vi registry-secrets.yaml apiVersion: v1 kind: Secret metadata: name: r2-secret type: Opaque data: ACCESS_KEY_ID: ACCESS_SECRET_KEY: And we apply the configuration. kubectl apply -f registry-secrets.yaml Deploying the Registry Once we've applied the secrets, we create the Registry deployment. vi registry-deployment.yaml Here is an example of the deployment, you should change some values to make it work in your cluster. apiVersion: apps/v1 kind: Deployment metadata: name: r2-registry spec: replicas: 1 selector: matchLabels: app: r2-registry template: metadata: labels: app: r2-registry spec: restartPolicy: Always containers: - name: registry image: registry:2 env: - name: REGISTRY_HEALTH_STORAGEDRIVER_ENABLED value: "false" - name: REGISTRY_STORAGE_DELETE_ENABLED value: "true" - name: REGISTRY_STORAGE value: s3 - name: REGISTRY_STORAGE_S3_REGION value: auto - name: REGISTRY_STORAGE_S3_REGIONENDPOINT value: https://.r2.cloudflarestorage.com - name: REGISTRY_STORAGE_S3_ENCRYPT value: "false" - name: REGISTRY_STORAGE_S3_SECURE value: "true" - name: REGISTRY_STORAGE_S3_CHUNKSIZE value: "104857600" - name: REGISTRY_STORAGE_S3_BUCKET value: - name: REGISTRY_STORAGE_S3_ACCESSKEY valueFrom: secretKeyRef: name: r2-secret key: ACCESS_KEY_ID - name: REGISTRY_STORAGE_S3_SECRETKEY valueFrom: secretKeyRef: name: r2-secret key: ACCESS_SECRET_KEY Once we've modified the deployment file, we apply it. kubectl apply -f registry-deployment.yaml Create service and ingress To access the registry, we might want to create a service and an ingress resource. The registry exposes at port 5000, so we create a service to that port. vi registry-service.yaml apiVersion: v1 kind: Service metadata: name: r2-registry spec: type: ClusterIP ports: - port: 5000 targetPort: 5000 protocol: TCP selector: app: r2-registry And we apply it. kubectl apply -f registry-service.yaml Next, we create an ingress resource so we can access the registry using our own domain or subdomain. vi registry-ingress.yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: r2-registry annotations: nginx.ingress.kubernetes.io/proxy-body-size: "0" spec: rules: - host: registry.binarycomet.net http: paths: - path: / pathType: Prefix backend: service: name: r2-registry port: number: 5000 And we apply it. kubectl apply -f registry-ingress.yaml Next steps Now we have a working Docker registry that can be accessed via our own domain or subdomain, but it's not production-ready yet. You should add authentication so only the users authorized can access the registry. Also, it might be interesting to create an SSL certificate, so we don't have to register it as an insecure-registries everywhere we want to use it.

May 7, 2025 - 10:32
 0
Setting up a Docker Registry backed with Cloudflare R2

You can run a private Docker registry using Cloudflare R2 as your storage backend, in this tutorial we're going to configure it in a Kubernetes cluster.

Creating bucket and access keys

First, we need to create a R2 bucket where we'll store the Docker images. We also need an API access key and secret key to access the bucket.

Once we have created the bucket and have both keys, we create a secret.

vi registry-secrets.yaml
apiVersion: v1
kind: Secret
metadata:
  name: r2-secret
type: Opaque
data:
  ACCESS_KEY_ID: 
  ACCESS_SECRET_KEY: 

And we apply the configuration.

kubectl apply -f registry-secrets.yaml

Deploying the Registry

Once we've applied the secrets, we create the Registry deployment.

vi registry-deployment.yaml

Here is an example of the deployment, you should change some values to make it work in your cluster.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: r2-registry
spec:
  replicas: 1
  selector:
    matchLabels:
      app: r2-registry
  template:
    metadata:
      labels:
        app: r2-registry
    spec:
      restartPolicy: Always
      containers:
      - name: registry
        image: registry:2
        env:
        - name: REGISTRY_HEALTH_STORAGEDRIVER_ENABLED
          value: "false"
        - name: REGISTRY_STORAGE_DELETE_ENABLED
          value: "true"
        - name: REGISTRY_STORAGE
          value: s3
        - name: REGISTRY_STORAGE_S3_REGION
          value: auto
        - name: REGISTRY_STORAGE_S3_REGIONENDPOINT
          value: https://.r2.cloudflarestorage.com
        - name: REGISTRY_STORAGE_S3_ENCRYPT
          value: "false"
        - name: REGISTRY_STORAGE_S3_SECURE
          value: "true"
        - name: REGISTRY_STORAGE_S3_CHUNKSIZE
          value: "104857600"
        - name: REGISTRY_STORAGE_S3_BUCKET
          value: 
        - name: REGISTRY_STORAGE_S3_ACCESSKEY
          valueFrom:
            secretKeyRef:
              name: r2-secret
              key: ACCESS_KEY_ID
        - name: REGISTRY_STORAGE_S3_SECRETKEY
          valueFrom:
            secretKeyRef:
              name: r2-secret
              key: ACCESS_SECRET_KEY

Once we've modified the deployment file, we apply it.

kubectl apply -f registry-deployment.yaml

Create service and ingress

To access the registry, we might want to create a service and an ingress resource.

The registry exposes at port 5000, so we create a service to that port.

vi registry-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: r2-registry
spec:
  type: ClusterIP
  ports:
  - port: 5000
    targetPort: 5000
    protocol: TCP
  selector:
    app: r2-registry

And we apply it.

kubectl apply -f registry-service.yaml

Next, we create an ingress resource so we can access the registry using our own domain or subdomain.

vi registry-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: r2-registry
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: "0"
spec:
  rules:
  - host: registry.binarycomet.net
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: r2-registry
            port:
              number: 5000

And we apply it.

kubectl apply -f registry-ingress.yaml

Next steps

Now we have a working Docker registry that can be accessed via our own domain or subdomain, but it's not production-ready yet.

You should add authentication so only the users authorized can access the registry.

Also, it might be interesting to create an SSL certificate, so we don't have to register it as an insecure-registries everywhere we want to use it.