diff --git a/.drone/drone.yml b/.drone/drone.yml
deleted file mode 100644
index b7a9989..0000000
--- a/.drone/drone.yml
+++ /dev/null
@@ -1,139 +0,0 @@
-kind: pipeline
-type: exec
-name: linux-amd64
-
-platform:
-  arch: amd64
-  os: linux
-
-steps:
-- name: test
-  commands:
-  - mkdir -p $HOMEPATH/.docker; echo $IMAGE_PULL_SECRETS > $HOMEPATH/.docker/config.json
-  - make docker-build test
-  environment:
-    VERSION: ${DRONE_TAG:-latest}-linux-amd64
-    DEV_REGISTRY:
-      from_secret: dev_registry
-    DRYCC_REGISTRY:
-      from_secret: drycc_registry
-    IMAGE_PULL_SECRETS:
-      from_secret: container_pull_secrets
-  when:
-    event:
-    - push
-    - tag
-    - pull_request
-
-- name: publish
-  commands:
-  - echo $CONTAINER_PASSWORD | docker login $DRYCC_REGISTRY --username $CONTAINER_USERNAME --password-stdin > /dev/null 2>&1
-  - make docker-build docker-immutable-push
-  environment:
-    VERSION: ${DRONE_TAG:-latest}-linux-amd64
-    DEV_REGISTRY:
-      from_secret: dev_registry
-    DRYCC_REGISTRY:
-      from_secret: drycc_registry
-    CONTAINER_USERNAME:
-      from_secret: container_username
-    CONTAINER_PASSWORD:
-      from_secret: container_password
-  when:
-    event:
-    - push
-    - tag
-
----
-kind: pipeline
-type: exec
-name: linux-arm64
-
-platform:
-  arch: arm64
-  os: linux
-
-steps:
-- name: publish
-  commands:
-  - echo $CONTAINER_PASSWORD | docker login $DRYCC_REGISTRY --username $CONTAINER_USERNAME --password-stdin > /dev/null 2>&1
-  - make docker-build docker-immutable-push
-  environment:
-    VERSION: ${DRONE_TAG:-latest}-linux-arm64
-    DEV_REGISTRY:
-      from_secret: dev_registry
-    DRYCC_REGISTRY:
-      from_secret: drycc_registry
-    CONTAINER_USERNAME:
-      from_secret: container_username
-    CONTAINER_PASSWORD:
-      from_secret: container_password
-  when:
-    event:
-    - push
-    - tag
-
----
-kind: pipeline
-type: docker
-name: manifest
-image_pull_secrets:
-- container_pull_secrets
-
-steps:
-- name: generate manifest
-  image: registry.drycc.cc/drycc/python-dev
-  pull: always
-  commands:
-  - sed -i "s/registry.drycc.cc/$${DRYCC_REGISTRY}/g" .drone/manifest.tmpl
-  environment:
-    DRYCC_REGISTRY:
-      from_secret: drycc_registry
-
-- name: publish
-  image: plugins/manifest
-  settings:
-    spec: .drone/manifest.tmpl
-    username:
-      from_secret: container_username
-    password:
-      from_secret: container_password
-  environment:
-    DEV_REGISTRY:
-      from_secret: dev_registry
-    DRYCC_REGISTRY:
-      from_secret: drycc_registry
-
-trigger:
-  event:
-  - push
-  - tag
-
-depends_on:
-- linux-amd64
-- linux-arm64
-
----
-kind: pipeline
-type: exec
-name: chart
-
-steps:
-- name: generate chart
-  commands:
-  - IMAGE_TAG=$([ ! -z $DRONE_TAG ] && echo ${DRONE_TAG:1} || echo \"canary\")
-  - sed -i "s/imageTag:\ \"canary\"/imageTag:\ $IMAGE_TAG/g" charts/registry/values.yaml
-  - helm package -u charts/registry --version $([ -z $DRONE_TAG ] && echo 1.0.0 || echo ${DRONE_TAG#v})
-  - echo $CONTAINER_PASSWORD | helm registry login $DRYCC_REGISTRY -u $CONTAINER_USERNAME --password-stdin
-  - helm push registry-$([ -z $DRONE_TAG ] && echo 1.0.0 || echo ${DRONE_TAG#v}).tgz oci://$DRYCC_REGISTRY/$([ -z $DRONE_TAG ] && echo charts-testing || echo charts)
-  environment:
-    DRYCC_REGISTRY:
-      from_secret: drycc_registry
-    CONTAINER_USERNAME:
-      from_secret: container_username
-    CONTAINER_PASSWORD:
-      from_secret: container_password
-  when:
-    event:
-    - push
-    - tag
diff --git a/.drone/manifest.tmpl b/.drone/manifest.tmpl
deleted file mode 100644
index 04806b3..0000000
--- a/.drone/manifest.tmpl
+++ /dev/null
@@ -1,18 +0,0 @@
-image: registry.drycc.cc/drycc/registry:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}canary{{/if}}
-{{#if build.tags}}
-tags:
-{{#each build.tags}}
-  - {{this}}
-{{/each}}
-{{/if}}
-manifests:
-  -
-    image: registry.drycc.cc/drycc/registry:{{#if build.tag}}{{build.tag}}-{{else}}latest-{{/if}}linux-amd64
-    platform:
-      architecture: amd64
-      os: linux
-  -
-    image: registry.drycc.cc/drycc/registry:{{#if build.tag}}{{build.tag}}-{{else}}latest-{{/if}}linux-arm64
-    platform:
-      architecture: arm64
-      os: linux
diff --git a/.gitignore b/.gitignore
index 1b73338..e5361b7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,3 @@
-rootfs/opt/
 contrib/ci/tmp/
 .vscode/
+rootfs/opt/registry/sbin
\ No newline at end of file
diff --git a/.woodpecker/build-linux.yml b/.woodpecker/build-linux.yml
new file mode 100644
index 0000000..ef4ecfd
--- /dev/null
+++ b/.woodpecker/build-linux.yml
@@ -0,0 +1,35 @@
+matrix:
+  platform:
+    - linux/amd64
+    - linux/arm64
+
+labels:
+  type: exec
+  platform: ${platform}
+
+steps:
+- name: publish-linux
+  image: bash
+  commands:
+  - export VERSION=$([ -z $CI_COMMIT_TAG ] && echo latest || echo $CI_COMMIT_TAG)-$(sed 's#/#-#g' <<< $CI_SYSTEM_PLATFORM)
+  - echo $CONTAINER_PASSWORD | podman login $DRYCC_REGISTRY --username $CONTAINER_USERNAME --password-stdin > /dev/null 2>&1
+  - make podman-build podman-immutable-push
+  environment:
+    CODENAME:
+      from_secret: codename
+    DEV_REGISTRY:
+      from_secret: dev_registry
+    DRYCC_REGISTRY:
+      from_secret: drycc_registry
+    CONTAINER_USERNAME:
+      from_secret: container_username
+    CONTAINER_PASSWORD:
+      from_secret: container_password
+  when:
+    event:
+    - push
+    - tag
+    - cron
+
+depends_on:
+- test-linux
\ No newline at end of file
diff --git a/.woodpecker/chart.yaml b/.woodpecker/chart.yaml
new file mode 100644
index 0000000..cfcd3ec
--- /dev/null
+++ b/.woodpecker/chart.yaml
@@ -0,0 +1,33 @@
+labels:
+  type: exec
+  platform: linux/amd64
+
+steps:
+- name: generate-chart
+  image: bash
+  commands:
+  - export VERSION=$(sed 's#v##' <<< $CI_COMMIT_TAG)
+  - export IMAGE_TAG=$([ ! -z $CI_COMMIT_TAG ] && echo \"$VERSION\" || echo \"canary\")
+  - export APP_VERSION=$([ -z $CI_COMMIT_TAG ] && echo $CI_COMMIT_SHA || echo $VERSION)
+  - export CHART_VERSION=$([ -z $CI_COMMIT_TAG ] && echo 1.0.0 || echo $VERSION)
+  - sed -i "s/imageTag:\ \"canary\"/imageTag:\ $IMAGE_TAG/g" charts/$${CI_REPO_NAME}/values.yaml
+  - helm package -u charts/$${CI_REPO_NAME} --version $CHART_VERSION --app-version $APP_VERSION
+  - echo $CONTAINER_PASSWORD | helm registry login $DRYCC_REGISTRY -u $CONTAINER_USERNAME --password-stdin
+  - helm push $${CI_REPO_NAME}-$CHART_VERSION.tgz oci://$DRYCC_REGISTRY/$([ -z $CI_COMMIT_TAG ] && echo charts-testing || echo charts)
+  environment:
+    DEV_REGISTRY:
+      from_secret: dev_registry
+    DRYCC_REGISTRY:
+      from_secret: drycc_registry
+    CONTAINER_USERNAME:
+      from_secret: container_username
+    CONTAINER_PASSWORD:
+      from_secret: container_password
+  when:
+    event:
+    - push
+    - tag
+    - cron
+
+depends_on:
+- manifest
diff --git a/.woodpecker/manifest.tmpl b/.woodpecker/manifest.tmpl
new file mode 100644
index 0000000..8739535
--- /dev/null
+++ b/.woodpecker/manifest.tmpl
@@ -0,0 +1,18 @@
+image: registry.drycc.cc/drycc/{{project}}:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}canary{{/if}}
+{{#if build.tags}}
+tags:
+{{#each build.tags}}
+  - {{this}}
+{{/each}}
+{{/if}}
+manifests:
+  -
+    image: registry.drycc.cc/drycc/{{project}}:{{#if build.tag}}{{build.tag}}-{{else}}latest-{{/if}}linux-amd64
+    platform:
+      architecture: amd64
+      os: linux
+  -
+    image: registry.drycc.cc/drycc/{{project}}:{{#if build.tag}}{{build.tag}}-{{else}}latest-{{/if}}linux-arm64
+    platform:
+      architecture: arm64
+      os: linux
diff --git a/.woodpecker/manifest.yml b/.woodpecker/manifest.yml
new file mode 100644
index 0000000..19fc70c
--- /dev/null
+++ b/.woodpecker/manifest.yml
@@ -0,0 +1,43 @@
+labels:
+  type: exec
+  platform: linux/amd64
+
+steps:
+- name: generate-manifest
+  image: bash
+  commands:
+  - sed -i "s/{{project}}/$${CI_REPO_NAME}/g" .woodpecker/manifest.tmpl
+  - sed -i "s/registry.drycc.cc/$${DRYCC_REGISTRY}/g" .woodpecker/manifest.tmpl
+  environment:
+    DRYCC_REGISTRY:
+      from_secret: drycc_registry
+  when:
+    event:
+    - tag
+    - push
+    - cron
+
+- name: publish-manifest
+  image: bash
+  commands:
+  - podman run --rm
+    -e PLUGIN_SPEC=.woodpecker/manifest.tmpl
+    -e PLUGIN_USERNAME=$CONTAINER_USERNAME
+    -e PLUGIN_PASSWORD=$CONTAINER_PASSWORD
+    -e DRONE_TAG=$CI_COMMIT_TAG
+    -v $(pwd):$(pwd)
+    -w $(pwd)
+    docker.io/plugins/manifest
+  environment:
+    CONTAINER_USERNAME:
+      from_secret: container_username
+    CONTAINER_PASSWORD:
+      from_secret: container_password
+  when:
+    event:
+    - tag
+    - push
+    - cron
+
+depends_on:
+- build-linux
diff --git a/.woodpecker/test-linux.yml b/.woodpecker/test-linux.yml
new file mode 100644
index 0000000..194832f
--- /dev/null
+++ b/.woodpecker/test-linux.yml
@@ -0,0 +1,24 @@
+matrix:
+  platform:
+  - linux/amd64
+  - linux/arm64
+
+labels:
+  type: exec
+  platform: ${platform}
+
+steps:
+- name: test-linux
+  image: bash
+  commands:
+  - make test
+  environment:
+    CODENAME:
+      from_secret: codename
+    DEV_REGISTRY:
+      from_secret: dev_registry
+  when:
+    event:
+    - push
+    - tag
+    - cron
diff --git a/Dockerfile b/Dockerfile
index f7f96d2..e9e37c3 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,31 +1,31 @@
+ARG CODENAME
+
 FROM registry.drycc.cc/drycc/go-dev:latest AS build
 ARG LDFLAGS
 ADD . /workspace
 RUN export GO111MODULE=on \
   && cd /workspace \
-  && CGO_ENABLED=0 init-stack go build -ldflags "${LDFLAGS}" -o /usr/local/bin/registry main.go \
-  && upx -9 --brute /usr/local/bin/registry
+  && CGO_ENABLED=0 init-stack go build -ldflags "${LDFLAGS}" -o /bin/start-registry main.go \
+  && upx -9 --brute /bin/start-registry
 
 
-FROM registry.drycc.cc/drycc/base:bullseye
+FROM registry.drycc.cc/drycc/base:${CODENAME}
 
-ENV DRYCC_UID=1001 \
+ARG DRYCC_UID=1001 \
   DRYCC_GID=1001 \
   DRYCC_HOME_DIR=/var/lib/registry \
-  JQ_VERSION="1.6" \
-  MC_VERSION="2022.08.28.20.08.11" \
-  REGISTRY_VERSION="2.8.0"
-
-COPY rootfs/bin/ /bin/
-COPY --from=build /usr/local/bin/registry /opt/registry/bin/registry
+  JQ_VERSION="1.7.1" \
+  NGINX_VERSION="1.29.1" \
+  RCLONE_VERSION="1.71.1" \
+  REGISTRY_VERSION="3.0.0"
 
 RUN groupadd drycc --gid ${DRYCC_GID} \
   && useradd drycc -u ${DRYCC_UID} -g ${DRYCC_GID} -s /bin/bash -m -d ${DRYCC_HOME_DIR} \
   && install-packages apache2-utils \
   && install-stack jq $JQ_VERSION \
-  && install-stack mc $MC_VERSION \
+  && install-stack nginx ${NGINX_VERSION} \
+  && install-stack rclone $RCLONE_VERSION \
   && install-stack registry $REGISTRY_VERSION \
-  && chmod +x /bin/init_registry \
   && rm -rf \
       /usr/share/doc \
       /usr/share/man \
@@ -40,12 +40,16 @@ RUN groupadd drycc --gid ${DRYCC_GID} \
       /usr/lib/`echo $(uname -m)`-linux-gnu/gconv/IBM* \
       /usr/lib/`echo $(uname -m)`-linux-gnu/gconv/EBC* \
   && mkdir -p /usr/share/man/man{1..8} \
-  && chown -R ${DRYCC_UID}:${DRYCC_GID} ${DRYCC_HOME_DIR}
+  && chown -R ${DRYCC_UID}:${DRYCC_GID} /opt/drycc
 
+COPY --from=build /bin/start-registry /bin/start-registry
+COPY --chown=${DRYCC_UID}:${DRYCC_GID} rootfs/bin/ /bin/
+COPY --chown=${DRYCC_UID}:${DRYCC_GID} rootfs/opt/drycc/nginx /opt/drycc/nginx
 COPY --chown=${DRYCC_UID}:${DRYCC_GID} rootfs/config-example.yml /opt/drycc/registry/etc/config.yml
-ENV DRYCC_REGISTRY_CONFIG /opt/drycc/registry/etc/config.yml
+
+ENV OTEL_TRACES_EXPORTER=none \
+  DRYCC_REGISTRY_CONFIG=/opt/drycc/registry/etc/config.yml
 
 USER ${DRYCC_UID}
 VOLUME ["${DRYCC_HOME_DIR}"]
-CMD ["/opt/registry/bin/registry"]
 EXPOSE 5000
diff --git a/Makefile b/Makefile
index d75fcb0..7fce73e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 # Short name: Short name, following [a-zA-Z_], used all over the place.
 # Some uses for short name:
-# - Docker image name
+# - Container image name
 # - Kubernetes service, deployment, pod names
 SHORT_NAME := registry
 DRYCC_REGISTRY ?= ${DEV_REGISTRY}
@@ -17,7 +17,7 @@ SHELL_SCRIPTS = $(wildcard rootfs/bin/* _scripts/*.sh contrib/ci/*.sh)
 # and other build options
 DEV_ENV_IMAGE := ${DEV_REGISTRY}/drycc/go-dev
 DEV_ENV_WORK_DIR := /opt/drycc/go/src/${REPO_PATH}
-DEV_ENV_PREFIX := docker run --rm -v ${CURDIR}:${DEV_ENV_WORK_DIR} -w ${DEV_ENV_WORK_DIR}
+DEV_ENV_PREFIX := podman run --rm -v ${CURDIR}:${DEV_ENV_WORK_DIR} -w ${DEV_ENV_WORK_DIR}
 DEV_ENV_CMD := ${DEV_ENV_PREFIX} ${DEV_ENV_IMAGE}
 LDFLAGS := "-s -w -X main.version=${VERSION}"
 BINDIR := ./rootfs/opt/registry/sbin
@@ -29,31 +29,28 @@ endif
 all:
 	@echo "Use a Makefile to control top-level building of the project."
 
-build: check-docker
+build: check-podman
 	mkdir -p ${BINDIR}
 	$(MAKE) build-binary
 
 # For cases where we're building from local
 # We also alter the RC file to set the image name.
-docker-build: check-docker
-	docker build ${DOCKER_BUILD_FLAGS} -t ${IMAGE} --build-arg LDFLAGS=${LDFLAGS} .
-	docker tag ${IMAGE} ${MUTABLE_IMAGE}
-
-docker-buildx: check-docker
-	docker buildx build --platform ${PLATFORM} -t ${IMAGE} --build-arg LDFLAGS=${LDFLAGS} . --push
+podman-build: check-podman
+	podman build --build-arg CODENAME=${CODENAME} -t ${IMAGE} --build-arg LDFLAGS=${LDFLAGS} .
+	podman tag ${IMAGE} ${MUTABLE_IMAGE}
 
 build-binary:
 	${DEV_ENV_CMD} go build -ldflags ${LDFLAGS} -o $(BINDIR)/${SHORT_NAME} main.go
 	$(call check-static-binary,$(BINDIR)/${SHORT_NAME})
 	${DEV_ENV_CMD} upx -9 --brute $(BINDIR)/${SHORT_NAME}
 
-test: check-docker test-style
+test: check-podman podman-build test-style
 	contrib/ci/test.sh ${IMAGE}
 
 test-style:
 	${DEV_ENV_CMD} shellcheck $(SHELL_SCRIPTS)
 
-deploy: check-kubectl docker-build docker-push
+deploy: check-kubectl podman-build podman-push
 	kubectl --namespace=drycc patch deployment drycc-$(SHORT_NAME) --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"$(IMAGE)"}]'
 
-.PHONY: all build build-binary docker-build test test-style deploy
+.PHONY: all build build-binary podman-build test test-style deploy
diff --git a/README.md b/README.md
index d3c5a7a..ae7eea3 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # Drycc Registry v2
 
-[![Build Status](https://drone.drycc.cc/api/badges/drycc/registry/status.svg)](https://drone.drycc.cc/drycc/registry)
+[![Build Status](https://woodpecker.drycc.cc/api/badges/drycc/registry/status.svg)](https://woodpecker.drycc.cc/drycc/registry)
 [![Go Report Card](https://goreportcard.com/badge/github.com/drycc/registry)](https://goreportcard.com/report/github.com/drycc/registry)
 
 
@@ -12,11 +12,19 @@ We welcome your input! If you have feedback, please submit an [issue][issues]. I
 
 # About
 
-The registry is a [Docker registry](https://docs.docker.com/registry/) component for use in Kubernetes. While it's intended for use inside of the Drycc open source [PaaS](https://en.wikipedia.org/wiki/Platform_as_a_service), it's flexible enough to be used as a standalone pod on any Kubernetes cluster.
+Registry consists of two components, namely the proxy component and the registry component.
 
-If you decide to use this component standalone, you can host your own Docker registry in your own Kubernetes cluster.
+## Proxy
 
-The Docker image that this repository builds is based on [the official Docker v2 registry image](https://github.com/docker/distribution).
+The proxy component is a proxy deployed on every Kubernetes worker node, proxying all requests to the Drycc Workflow [registry][registry]. This allows the worker nodes daemons to communicate to the registry over localhost, bypassing the need for adding the `--insecure-registry` flag to the daemons.
+
+## Registry
+
+The registry component is a [Container registry](https://github.com/distribution/distribution) component for use in Kubernetes. While it's intended for use inside of the Drycc open source [PaaS](https://en.wikipedia.org/wiki/Platform_as_a_service), it's flexible enough to be used as a standalone pod on any Kubernetes cluster.
+
+If you decide to use this component standalone, you can host your own Container registry in your own Kubernetes cluster.
+
+The Container image that this repository builds is based on [the official Container v2 registry image](https://github.com/distribution/distribution).
 
 # Development
 
@@ -49,7 +57,7 @@ $ export IMAGE_PREFIX=youruser/             # if using Quay or Dockerhub
 To build and push the image run:
 
 ```console
-$ make docker-build docker-push
+$ make podman-build podman-push
 ```
 
 To deploy the image via patching the registry deployment run:
diff --git a/charts/registry/Chart.yaml b/charts/registry/Chart.yaml
index 733f9f4..cd8abff 100644
--- a/charts/registry/Chart.yaml
+++ b/charts/registry/Chart.yaml
@@ -1,11 +1,12 @@
 name: registry
 home: https://github.com/drycc/registry
 apiVersion: v2
+appVersion: 1.0.0
 dependencies:
   - name: common
     repository: oci://registry.drycc.cc/charts
-    version: ~1.1.1
-description: Docker registry for Drycc Workflow.
+    version: ~1.1.2
+description: Container registry for Drycc Workflow.
 maintainers:
   - name: Drycc Team
     email: engineering@drycc.com
diff --git a/charts/registry/templates/_helper.tpl b/charts/registry/templates/_helper.tpl
index df13657..cd87b72 100644
--- a/charts/registry/templates/_helper.tpl
+++ b/charts/registry/templates/_helper.tpl
@@ -24,26 +24,37 @@ env:
     secretKeyRef:
       name: registry-secret
       key: password
-- name: "DRYCC_STORAGE_LOOKUP"
+{{- if (.Values.storageEndpoint) }}
+- name: "DRYCC_STORAGE_BUCKET"
   valueFrom:
     secretKeyRef:
-      name: storage-creds
-      key: lookup
-- name: "DRYCC_STORAGE_HEALTH"
+      name: registry-secret
+      key: storage-bucket
+- name: "DRYCC_STORAGE_ENDPOINT"
   valueFrom:
     secretKeyRef:
-      name: storage-creds
-      key: health
-- name: "DRYCC_STORAGE_BUCKET"
+      name: registry-secret
+      key: storage-endpoint
+- name: "DRYCC_STORAGE_ACCESSKEY"
   valueFrom:
     secretKeyRef:
-      name: storage-creds
-      key: registry-bucket
-- name: "DRYCC_STORAGE_ENDPOINT"
+      name: registry-secret
+      key: storage-accesskey
+- name: "DRYCC_STORAGE_SECRETKEY"
   valueFrom:
     secretKeyRef:
-      name: storage-creds
-      key: endpoint
+      name: registry-secret
+      key: storage-secretkey
+- name: "DRYCC_STORAGE_PATH_STYLE"
+  valueFrom:
+    secretKeyRef:
+      name: registry-secret
+      key: storage-path-style
+{{- else if .Values.storage.enabled  }}
+- name: "DRYCC_STORAGE_BUCKET"
+  value: "registry"
+- name: "DRYCC_STORAGE_ENDPOINT"
+  value: http://drycc-storage:9000
 - name: "DRYCC_STORAGE_ACCESSKEY"
   valueFrom:
     secretKeyRef:
@@ -54,24 +65,7 @@ env:
     secretKeyRef:
       name: storage-creds
       key: secretkey
+- name: "DRYCC_STORAGE_PATH_STYLE"
+  value: "true"
 {{- end }}
-
-{{/* Generate registry deployment limits */}}
-{{- define "registry.limits" -}}
-{{- if or (.Values.limitsCpu) (.Values.limitsMemory)}}
-resources:
-  limits:
-    {{- if (.Values.limitsCpu) }}
-    cpu: {{.Values.limitsCpu}}
-    {{- end }}
-    {{- if (.Values.limitsMemory) }}
-    memory: {{.Values.limitsMemory}}
-    {{- end }}
-    {{- if (.Values.limitsHugepages2Mi) }}
-    hugepages-2Mi: {{.Values.limitsHugepages2Mi}}
-    {{- end }}
-    {{- if (.Values.limitsHugepages1Gi) }}
-    hugepages-1Gi: {{.Values.limitsHugepages1Gi}}
-    {{- end }}
 {{- end }}
-{{- end }}
\ No newline at end of file
diff --git a/charts/registry/templates/registry-cronjob-daily.yaml b/charts/registry/templates/registry-cronjob-daily.yaml
index 30f9d3f..442841b 100644
--- a/charts/registry/templates/registry-cronjob-daily.yaml
+++ b/charts/registry/templates/registry-cronjob-daily.yaml
@@ -1,4 +1,3 @@
-{{- if eq .Values.global.registryLocation "on-cluster" }}
 apiVersion: batch/v1
 kind: CronJob
 metadata:
@@ -23,22 +22,25 @@ spec:
             image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/python-dev:latest
             imagePullPolicy: {{.Values.imagePullPolicy}}
             args:
-              - netcat
-              - -v
-              - -u
-              - $(DRYCC_STORAGE_ENDPOINT)
-            {{- include "builder.envs" . | indent 12 }}
+            - netcat
+            - -v
+            - -u
+            - $(DRYCC_STORAGE_ENDPOINT)
+            {{- include "registry.envs" . | indent 12 }}
           containers:
           - image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/registry:{{.Values.imageTag}}
             imagePullPolicy: {{.Values.imagePullPolicy}}
             name: drycc-registry-garbage-collect
+            {{- if .Values.diagnosticMode.enabled }}
+            command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 14 }}
+            args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 14 }}
+            {{- else }}
             args:
-            - /bin/bash
-            - -c
-            - /opt/registry/bin/registry
-            - garbage-collect
-            - $(DRYCC_REGISTRY_CONFIG)
-            - --dry-run
-            - --delete-untagged
-            {{- include "builder.envs" . | indent 12 }}
-{{- end }}
+            - /usr/bin/env
+            - bash
+            - -ec
+            - |
+              # run garbage collect
+              start-registry garbage-collect ${DRYCC_REGISTRY_CONFIG} --dry-run --delete-untagged
+            {{- end }}
+            {{- include "registry.envs" . | indent 12 }}
diff --git a/charts/registry/templates/registry-deployment.yaml b/charts/registry/templates/registry-deployment.yaml
index e1bdcae..dc362e3 100644
--- a/charts/registry/templates/registry-deployment.yaml
+++ b/charts/registry/templates/registry-deployment.yaml
@@ -1,4 +1,3 @@
-{{- if eq .Values.global.registryLocation "on-cluster" }}
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -19,12 +18,12 @@ spec:
       app: drycc-registry
   template:
     metadata:
-      labels:
+      labels: {{- include "common.labels.standard" . | nindent 8 }}
         app: drycc-registry
     spec:
       affinity:
-        podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.podAffinityPreset.type "key" .Values.podAffinityPreset.key "values" .Values.podAffinityPreset.values ) | nindent 10 }}
-        podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.podAntiAffinityPreset.type "key" .Values.podAntiAffinityPreset.key "values" .Values.podAntiAffinityPreset.values ) | nindent 10 }}
+        podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.podAffinityPreset.type "component" "" "extraMatchLabels" .Values.podAffinityPreset.extraMatchLabels "topologyKey" "" "context" $) | nindent 10 }}
+        podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.podAntiAffinityPreset.type "component" "" "extraMatchLabels" .Values.podAntiAffinityPreset.extraMatchLabels "topologyKey" "" "context" $) | nindent 10 }}
         nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.nodeAffinityPreset.type "key" .Values.nodeAffinityPreset.key "values" .Values.nodeAffinityPreset.values ) | nindent 10 }}
       serviceAccount: drycc-registry
       initContainers:
@@ -32,17 +31,28 @@ spec:
         image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/python-dev:latest
         imagePullPolicy: {{.Values.imagePullPolicy}}
         args:
-          - netcat
-          - -v
-          - -g
-          - $(DRYCC_STORAGE_HEALTH)
+        - netcat
+        - -v
+        - -u
+        - $(DRYCC_STORAGE_ENDPOINT)
         {{- include "registry.envs" . | indent 8 }}
       containers:
       - name: drycc-registry
         image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/registry:{{.Values.imageTag}}
         imagePullPolicy: {{.Values.imagePullPolicy}}
-        {{- include "registry.limits" . | indent 8 }}
+        {{- if .Values.diagnosticMode.enabled }}
+        command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 10 }}
+        args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 10 }}
+        {{- else }}
+        args:
+        - start-registry
+        {{- end }}
+        {{- with index .Values "resources" }}
+        resources:
+          {{- toYaml . | nindent 10 }}
+        {{- end }}
         {{- include "registry.envs" . | indent 8 }}
+        {{- if not .Values.diagnosticMode.enabled }}
         startupProbe:
           tcpSocket:
             port: 5000
@@ -69,16 +79,17 @@ spec:
           timeoutSeconds: 1
           successThreshold: 1
           failureThreshold: 5
+        {{- end }}
         ports:
-          - containerPort: 5000
+        - containerPort: 5000
+          name: http
         volumeMounts:
-          - name: registry-storage
-            mountPath: /var/lib/registry
+        - name: registry-storage
+          mountPath: /var/lib/registry
       securityContext:
         fsGroup: 1001
         runAsGroup: 1001
         runAsUser: 1001
       volumes:
-        - name: registry-storage
-          emptyDir: {}
-{{- end }}
+      - name: registry-storage
+        emptyDir: {}
diff --git a/charts/registry/templates/registry-proxy-daemonset.yaml b/charts/registry/templates/registry-proxy-daemonset.yaml
new file mode 100644
index 0000000..c83a364
--- /dev/null
+++ b/charts/registry/templates/registry-proxy-daemonset.yaml
@@ -0,0 +1,98 @@
+apiVersion: apps/v1
+kind: DaemonSet
+metadata:
+  name: drycc-registry-proxy
+  labels:
+    heritage: drycc
+  annotations:
+    component.drycc.cc/version: {{ .Values.imageTag }}
+spec:
+  updateStrategy:
+    type: RollingUpdate
+  selector:
+    matchLabels:
+      app: drycc-registry-proxy
+      heritage: drycc
+  template:
+    metadata:
+      name: drycc-registry-proxy
+      labels:
+        heritage: drycc
+        app: drycc-registry-proxy
+    spec:
+      securityContext:
+        fsGroup: 1001
+        runAsGroup: 1001
+        runAsUser: 1001
+      initContainers:
+      - name: drycc-registry-init
+        image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/python-dev:latest
+        imagePullPolicy: {{.Values.imagePullPolicy}}
+        args:
+        - netcat
+        - -v
+        - -a
+        - $(DRYCC_REGISTRY_HOST)
+        env:
+        - name: "DRYCC_REGISTRY_HOST"
+          value: drycc-registry:5000
+      containers:
+      - name: drycc-registry-proxy
+        image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/registry:{{.Values.imageTag}}
+        imagePullPolicy: {{.Values.imagePullPolicy}}
+        {{- if .Values.diagnosticMode.enabled }}
+        command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 10 }}
+        args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 10 }}
+        {{- else }}
+        args:
+        - start-proxy
+        {{- end }}
+        {{- with index .Values "proxy" "resources" }}
+        resources:
+          {{- toYaml . | nindent 10 }}
+        {{- end }}
+        {{- if not .Values.diagnosticMode.enabled }}
+        startupProbe:
+          httpGet:
+            path: /
+            port: 8080
+          initialDelaySeconds: 30
+          periodSeconds: 5
+          timeoutSeconds: 1
+          successThreshold: 1
+          failureThreshold: 5
+        livenessProbe:
+          httpGet:
+            path: /
+            port: 8080
+          initialDelaySeconds: 30
+          periodSeconds: 5
+          timeoutSeconds: 1
+          successThreshold: 1
+          failureThreshold: 5
+        readinessProbe:
+          httpGet:
+            path: /
+            port: 8080
+          initialDelaySeconds: 30
+          periodSeconds: 5
+          timeoutSeconds: 1
+          successThreshold: 1
+          failureThreshold: 5
+        {{- end }}
+        env:
+        - name: "DRYCC_REGISTRY_HOST"
+          value: drycc-registry:5000
+        - name: "DRYCC_REGISTRY_USERNAME"
+          valueFrom:
+            secretKeyRef:
+              name: registry-secret
+              key: username
+        - name: "DRYCC_REGISTRY_PASSWORD"
+          valueFrom:
+            secretKeyRef:
+              name: registry-secret
+              key: password
+        ports:
+        - containerPort: 8080
+          hostPort: {{.Values.proxy.port}}
diff --git a/charts/registry/templates/registry-secret.yaml b/charts/registry/templates/registry-secret.yaml
index ce46a36..9fa12be 100644
--- a/charts/registry/templates/registry-secret.yaml
+++ b/charts/registry/templates/registry-secret.yaml
@@ -4,17 +4,16 @@ metadata:
   name: registry-secret
   labels:
     heritage: drycc
-  annotations:
-    drycc.cc/registry-location: "{{ .Values.global.registryLocation }}"
 type: Opaque
 data:
-  {{- if eq .Values.global.registryLocation "on-cluster" }}
-  host: {{ printf "drycc-registry.%s.svc.%s:80" .Release.Namespace .Values.global.clusterDomain | b64enc }}
-  secret: {{ randAlphaNum 32 | b64enc }}
-  {{- else }}
-  host: {{ .Values.host | b64enc }}
-  organization: {{ .Values.organization | b64enc }}
-  {{- end }}
-  username: {{ if .Values.username | default "" | ne "" }}{{ .Values.username | b64enc }}{{ else }}{{ randAlphaNum 32 | b64enc }}{{ end }}
-  password: {{ if .Values.password | default "" | ne "" }}{{ .Values.password | b64enc }}{{ else }}{{ randAlphaNum 32 | b64enc }}{{ end }}
+  secret: {{ include "common.secrets.lookup" (dict "secret" "registry-secret" "key" "secret" "defaultValue" (randAlphaNum 32) "context" $) }}
+  username: {{ include "common.secrets.lookup" (dict "secret" "registry-secret" "key" "username" "defaultValue" (.Values.username | default (randAlphaNum 32)) "context" $) }}
+  password: {{ include "common.secrets.lookup" (dict "secret" "registry-secret" "key" "password" "defaultValue" (.Values.password | default (randAlphaNum 32)) "context" $) }}
   redirect: {{ .Values.redirect | b64enc }}
+  {{- if (.Values.storageEndpoint) }}
+  storage-bucket: {{ .Values.storageBucket | b64enc }}
+  storage-endpoint: {{ .Values.storageEndpoint | b64enc }}
+  storage-accesskey: {{ .Values.storageAccesskey | b64enc }}
+  storage-secretkey: {{ .Values.storageSecretkey | b64enc }}
+  storage-path-style: {{ .Values.storagePathStyle | b64enc }}
+  {{- end }}
diff --git a/charts/registry/templates/registry-service-account.yaml b/charts/registry/templates/registry-service-account.yaml
index 3766311..a3b2b78 100644
--- a/charts/registry/templates/registry-service-account.yaml
+++ b/charts/registry/templates/registry-service-account.yaml
@@ -1,8 +1,6 @@
-{{- if eq .Values.global.registryLocation "on-cluster" }}
 apiVersion: v1
 kind: ServiceAccount
 metadata:
   name: drycc-registry
   labels:
     heritage: drycc
-{{- end }}
diff --git a/charts/registry/templates/registry-service.yaml b/charts/registry/templates/registry-service.yaml
index 1b792c1..fbafcf2 100644
--- a/charts/registry/templates/registry-service.yaml
+++ b/charts/registry/templates/registry-service.yaml
@@ -1,20 +1,17 @@
-{{- if eq .Values.global.registryLocation "on-cluster" }}
 apiVersion: v1
 kind: Service
 metadata:
   name: drycc-registry
   annotations:
+    prometheus.io/path: /metrics
+    prometheus.io/port: "9000"
+    prometheus.io/scrape: "true"
     {{- with .Values.service.annotations }}
     {{- toYaml . | nindent 4 }}
     {{- end }}
   labels:
     heritage: drycc
 spec:
-  ports:
-    - name: http
-      port: 80
-      targetPort: 5000
+  clusterIP: None
   selector:
     app: drycc-registry
-  sessionAffinity: ClientIP  
-{{- end }}
diff --git a/charts/registry/values.yaml b/charts/registry/values.yaml
index 5997f66..87a2895 100644
--- a/charts/registry/values.yaml
+++ b/charts/registry/values.yaml
@@ -2,8 +2,22 @@ imageOrg: "drycc"
 imagePullPolicy: "Always"
 imageTag: "canary"
 imageRegistry: "registry.drycc.cc"
-# limitsCpu: "100m"
-# limitsMemory: "50Mi"
+
+## Enable diagnostic mode
+##
+diagnosticMode:
+  ## @param diagnosticMode.enabled Enable diagnostic mode (all probes will be disabled and the command will be overridden)
+  ##
+  enabled: false
+  ## @param diagnosticMode.command Command to override all containers
+  ##
+  command:
+    - sleep
+  ## @param diagnosticMode.args Args to override all containers
+  ##
+  args:
+    - infinity
+
 nodeAffinityPreset:
   key: "drycc.cc/node"
   type: "soft"
@@ -11,23 +25,60 @@ nodeAffinityPreset:
   - "true"
 
 podAffinityPreset:
-  key: "security"
   type: ""
-  values:
-  - "drycc-security"
+  extraMatchLabels:
+    security: "drycc-security"
 
 podAntiAffinityPreset:
-  key: "app"
   type: "soft"
-  values:
-  - "drycc-registry"
+  extraMatchLabels:
+    app: "drycc-registry"
 
 # registry replicas
 replicas: 1
+# registry storage redirect
+redirect: "false"
+
+proxy:
+  # host port for the registry proxy in the daemonset
+  port: 5555
+  resources: {}
+  # limits:
+  #   cpu: 200m
+  #   memory: 50Mi
+  # requests:
+  #   cpu: 100m
+  #   memory: 30Mi
+
+resources: {}
+  # limits:
+  #   cpu: 200m
+  #   memory: 50Mi
+  # requests:
+  #   cpu: 100m
+  #   memory: 30Mi
 
 concurrencyPolicy: "Replace"
 
+# The following parameters will no longer use the built-in storage component.
+storageBucket: "builder"
+storageEndpoint: ""
+storageAccesskey: ""
+storageSecretkey: ""
+storagePathStyle: "auto"
+
+storage:
+  enabled: true
+
 # Service
 service:
   # Provide any additional service annotations
-  annotations: {}
\ No newline at end of file
+  annotations: {}
+
+resources: {}
+  # limits:
+  #   cpu: 200m
+  #   memory: 50Mi
+  # requests:
+  #   cpu: 100m
+  #   memory: 30Mi
diff --git a/contrib/ci/test.sh b/contrib/ci/test.sh
index dc59be0..6be70b3 100755
--- a/contrib/ci/test.sh
+++ b/contrib/ci/test.sh
@@ -2,36 +2,73 @@
 
 set -eoxf pipefail
 
-s3Accesskey=drycc
-s3Secretkey=123456789
+DRYCC_STORAGE_ACCESSKEY=f4c4281665bc11ee8e0400163e04a9cd
+DRYCC_STORAGE_SECRETKEY=f4c4281665bc11ee8e0400163e04a9cd
 
-STORAGE_JOB=$(docker run -d --name storage \
-  -e DRYCC_STORAGE_ACCESSKEY=$s3Accesskey \
-  -e DRYCC_STORAGE_SECRETKEY=$s3Secretkey \
-  "${DEV_REGISTRY}"/drycc/storage:canary minio server /data/storage/ --console-address :9001)
 
-sleep 5
-docker logs "${STORAGE_JOB}"
+STORAGE_JOB=$(podman run -d --rm --entrypoint init-stack \
+  -e RUSTFS_ACCESS_KEY="${DRYCC_STORAGE_ACCESSKEY}" \
+  -e RUSTFS_SECRET_KEY="${DRYCC_STORAGE_SECRETKEY}" \
+  "${DEV_REGISTRY}"/drycc/storage:canary rustfs /data)
 
-STORAGE_IP=$(docker inspect --format "{{ .NetworkSettings.IPAddress }}" "${STORAGE_JOB}")
+# wait for port
+STORAGE_IP=$(podman inspect --format "{{ .NetworkSettings.IPAddress }}" "${STORAGE_JOB}")
+echo -e "\\033[32m---> Waitting for ${STORAGE_IP}:9000\\033[0m"
+wait-for-port --host="${STORAGE_IP}" 9000
+echo -e "\\033[32m---> S3 service ${STORAGE_IP}:9000 ready...\\033[0m"
+podman logs "${STORAGE_JOB}"
 
-JOB=$(docker run --add-host storage:"${STORAGE_IP}" \
-  -d \
-  -p 5000:5000 \
-  -e REGISTRY_HTTP_SECRET=drycc \
+REGISTRY_JOB=$(podman run -d --rm \
   -e DRYCC_REGISTRY_REDIRECT=false \
   -e DRYCC_REGISTRY_USERNAME=admin \
   -e DRYCC_REGISTRY_PASSWORD=admin \
-  -e DRYCC_STORAGE_LOOKUP=path \
   -e DRYCC_STORAGE_BUCKET=registry \
-  -e DRYCC_STORAGE_ENDPOINT=http://storage:9000 \
-  -e DRYCC_STORAGE_ACCESSKEY=$s3Accesskey \
-  -e DRYCC_STORAGE_SECRETKEY=$s3Secretkey \
-  "$1")
+  -e DRYCC_STORAGE_ENDPOINT="http://${STORAGE_IP}:9000" \
+  -e DRYCC_STORAGE_ACCESSKEY="${DRYCC_STORAGE_ACCESSKEY}" \
+  -e DRYCC_STORAGE_SECRETKEY="${DRYCC_STORAGE_SECRETKEY}" \
+  -e DRYCC_STORAGE_PATH_STYLE=true \
+  "$1" start-registry)
+
+# shellcheck disable=SC2317
+function clean_before_exit {
+  # delay before exiting, so stdout/stderr flushes through the logging system
+  podman kill "${REGISTRY_JOB}"
+  podman kill "${STORAGE_JOB}"
+  podman kill "${PROXY_JOB}"
+}
+trap clean_before_exit EXIT
 
 # let the registry run for a few seconds
-sleep 5
+REGISTRY_IP=$(podman inspect --format "{{ .NetworkSettings.IPAddress }}" "${REGISTRY_JOB}")
+echo -e "\\033[32m---> Waitting for ${REGISTRY_IP}:5000\\033[0m"
+wait-for-port --host="${REGISTRY_IP}" 5000
+echo -e "\\033[32m---> S3 service ${REGISTRY_IP}:5000 ready...\\033[0m"
+
+# proxy job
+PROXY_JOB=$(podman run -d \
+  -p 15555:8080 \
+  -e DRYCC_REGISTRY_HOST="${REGISTRY_IP}:5000" \
+  -e DRYCC_REGISTRY_USERNAME=admin \
+  -e DRYCC_REGISTRY_PASSWORD=admin \
+  "$1" start-proxy)
+
+# let the registry proxy run for a few seconds
+REGISTRY_PROXY_IP=$(podman inspect --format "{{ .NetworkSettings.IPAddress }}" "${PROXY_JOB}")
+echo -e "\\033[32m---> Waitting for ${REGISTRY_PROXY_IP}:8080\\033[0m"
+wait-for-port --host="${REGISTRY_PROXY_IP}" 8080
+echo -e "\\033[32m---> S3 service ${REGISTRY_PROXY_IP}:8080 ready...\\033[0m"
+
 # check that the registry is still up
-docker logs "${JOB}"
-docker ps -q --no-trunc=true | grep "${JOB}"
-docker rm -f "${JOB}" "${STORAGE_JOB}"
+http_status_code=$(curl -X GET -s -o /dev/null -w "%{http_code}" "http://${REGISTRY_PROXY_IP}:8080/v2/")
+if [ "$http_status_code" != "200" ]; then
+    echo "Expected http status code: 200, actual: ${http_status_code}"
+    exit 1
+fi
+
+http_status_code=$(curl -X POST -s -o /dev/null -w "%{http_code}" "http://${REGISTRY_PROXY_IP}:8080/v2/")
+if [ "$http_status_code" != "403" ]; then
+    echo "Expected http status code: 403, actual: ${http_status_code}"
+    exit 1
+fi
+
+echo -e "\\033[32m---> All test success...\\033[0m"
\ No newline at end of file
diff --git a/includes.mk b/includes.mk
index 5bc8456..4634e38 100644
--- a/includes.mk
+++ b/includes.mk
@@ -1,6 +1,6 @@
-check-docker:
-	@if [ -z $$(which docker) ]; then \
-		echo "Missing \`docker\` client which is required for development"; \
+check-podman:
+	@if [ -z $$(which podman) ]; then \
+		echo "Missing \`podman\` client which is required for development"; \
 		exit 2; \
 	fi
 
diff --git a/main.go b/main.go
index 4488ee9..27b534c 100644
--- a/main.go
+++ b/main.go
@@ -14,11 +14,11 @@ const (
 	registryHtpasswd       = "/opt/drycc/registry/etc/htpasswd"
 	registryConfigEnvVar   = "DRYCC_REGISTRY_CONFIG"
 	registryRedirectEnvVar = "DRYCC_REGISTRY_REDIRECT"
-	storageLookupEnvVar    = "DRYCC_STORAGE_LOOKUP"
 	storageBucketEnvVar    = "DRYCC_STORAGE_BUCKET"
 	storageEndpointEnvVar  = "DRYCC_STORAGE_ENDPOINT"
 	storageAccesskeyEnvVar = "DRYCC_STORAGE_ACCESSKEY"
 	storageSecretkeyEnvVar = "DRYCC_STORAGE_SECRETKEY"
+	storagePathStyleEnvVar = "DRYCC_STORAGE_PATH_STYLE"
 	defaultCommand         = "serve"
 )
 
@@ -39,9 +39,8 @@ func main() {
 	os.Setenv("REGISTRY_STORAGE_S3_ACCESSKEY", os.Getenv(storageAccesskeyEnvVar))
 	os.Setenv("REGISTRY_STORAGE_S3_SECRETKEY", os.Getenv(storageSecretkeyEnvVar))
 	os.Setenv("REGISTRY_STORAGE_S3_BUCKET", os.Getenv(storageBucketEnvVar))
-	os.Setenv("REGISTRY_STORAGE_S3_ROOTDIRECTORY", "/registry")
 
-	if os.Getenv(storageLookupEnvVar) == "path" {
+	if os.Getenv(storagePathStyleEnvVar) == "true" {
 		os.Setenv("REGISTRY_STORAGE_S3_FORCEPATHSTYLE", "true")
 	}
 
@@ -50,17 +49,26 @@ func main() {
 	} else {
 		os.Setenv("REGISTRY_STORAGE_REDIRECT_DISABLE", "true")
 	}
+	// set default env
+	os.Setenv("REGISTRY_STORAGE_S3_V4AUTH", "true")
+	os.Setenv("REGISTRY_STORAGE_S3_SECURE", "false")
+	os.Setenv("REGISTRY_STORAGE_S3_SKIPVERIFY", "true")
+	os.Setenv("REGISTRY_STORAGE_DELETE_ENABLED", "true")
+	os.Setenv("REGISTRY_VALIDATION_DISABLED", "true")
+	os.Setenv("REGISTRY_STORAGE_S3_ROOTDIRECTORY", "/registry")
 
-	// run /bin/init_registry
+	// run /bin/init-registry
 	os.Setenv("REGISTRY_AUTH", "htpasswd")
 	os.Setenv("REGISTRY_AUTH_HTPASSWD_REALM", "basic-realm")
 	os.Setenv("REGISTRY_AUTH_HTPASSWD_PATH", registryHtpasswd)
-	cmd := exec.Command("/bin/init_registry")
+	cmd := exec.Command("/bin/init-registry")
 	cmd.Stdout = os.Stdout
 	cmd.Stderr = os.Stderr
 	if err := cmd.Run(); err != nil {
 		log.Fatal("Error creating the registry bucket: ", err)
 	}
+	// avoid conflicts with env variables
+	os.Unsetenv("REGISTRY_VERSION")
 	if len(os.Args) > 1 {
 		cmd = exec.Command(registryBinary, os.Args[1:]...)
 	} else {
diff --git a/rootfs/bin/init-registry b/rootfs/bin/init-registry
new file mode 100755
index 0000000..4d05fc8
--- /dev/null
+++ b/rootfs/bin/init-registry
@@ -0,0 +1,23 @@
+#!/usr/bin/env bash
+
+set -e
+
+
+mkdir -p ~/.config/rclone
+touch ~/.config/rclone/rclone.conf
+rclone config create storage s3 \
+  provider=Other \
+  access_key_id="${DRYCC_STORAGE_ACCESSKEY}" \
+  secret_access_key="${DRYCC_STORAGE_SECRETKEY}" \
+  endpoint="${DRYCC_STORAGE_ENDPOINT}" \
+  force_path_style="${DRYCC_STORAGE_PATH_STYLE:-true}" --no-output
+
+if ! rclone lsd storage: > /dev/null 2>&1; then
+  sleep 9s
+  echo "waiting for object storage to become ready..."
+fi
+
+rclone mkdir "storage:${DRYCC_STORAGE_BUCKET}"
+
+htpasswd -Bbn "${DRYCC_REGISTRY_USERNAME}" "${DRYCC_REGISTRY_PASSWORD}" > "${REGISTRY_AUTH_HTPASSWD_PATH}"
+echo "create ${REGISTRY_AUTH_HTPASSWD_PATH} success"
diff --git a/rootfs/bin/init_registry b/rootfs/bin/init_registry
deleted file mode 100755
index a3a6041..0000000
--- a/rootfs/bin/init_registry
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-
-mc config host add storage \
-  "${DRYCC_STORAGE_ENDPOINT}" \
-  "${DRYCC_STORAGE_ACCESSKEY}" \
-  "${DRYCC_STORAGE_SECRETKEY}" \
-  --lookup "${DRYCC_STORAGE_LOOKUP}" \
-  --api s3v4
-
-has_bucket(){
-    mc ls storage -json|jq -r '.key'|grep -w "${DRYCC_STORAGE_BUCKET}"
-}
-
-if  [ -z "$(has_bucket)" ] ;then
-    mc mb storage/"${DRYCC_STORAGE_BUCKET}"
-    if  [ -z "$(has_bucket)" ] ;then
-        echo "create bucket ${DRYCC_STORAGE_BUCKET} error"
-        exit 1
-    fi
-fi
-echo "create bucket ${DRYCC_STORAGE_BUCKET} success"
-
-htpasswd -Bbn "${DRYCC_REGISTRY_USERNAME}" "${DRYCC_REGISTRY_PASSWORD}" > "${REGISTRY_AUTH_HTPASSWD_PATH}"
-echo "create ${REGISTRY_AUTH_HTPASSWD_PATH} success"
\ No newline at end of file
diff --git a/rootfs/bin/start-proxy b/rootfs/bin/start-proxy
new file mode 100755
index 0000000..5354551
--- /dev/null
+++ b/rootfs/bin/start-proxy
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+
+REGISTRY_HOST=${DRYCC_REGISTRY_HOST:?no host}
+AUTHORIZATION=$(echo -ne "${DRYCC_REGISTRY_USERNAME:? no username}":"${DRYCC_REGISTRY_PASSWORD:? no password}" | base64 -w 0)
+
+cat /opt/drycc/nginx/conf/registry.conf.tpl > /opt/drycc/nginx/conf/registry.conf
+sed -i "s#%REGISTRY_HOST%#${REGISTRY_HOST}#g" /opt/drycc/nginx/conf/registry.conf
+sed -i "s#%AUTHORIZATION%#${AUTHORIZATION}#g" /opt/drycc/nginx/conf/registry.conf
+
+# wait for registry to come online
+while ! curl -sS "$REGISTRY_HOST" &>/dev/null; do
+	echo "waiting for the registry (%s) to come online..."
+	echo "$REGISTRY_HOST"
+	sleep 1
+done
+
+echo "starting registry-proxy..."
+exec nginx -g "daemon off;"
diff --git a/rootfs/config-example.yml b/rootfs/config-example.yml
index 3277f9a..a55566c 100644
--- a/rootfs/config-example.yml
+++ b/rootfs/config-example.yml
@@ -9,6 +9,11 @@ storage:
     rootdirectory: /var/lib/registry
 http:
   addr: :5000
+  debug:
+    addr: :9000
+    prometheus:
+      enabled: true
+      path: /metrics
   headers:
     X-Content-Type-Options: [nosniff]
 health:
diff --git a/rootfs/opt/drycc/nginx/conf/nginx.conf b/rootfs/opt/drycc/nginx/conf/nginx.conf
new file mode 100644
index 0000000..bd3f31d
--- /dev/null
+++ b/rootfs/opt/drycc/nginx/conf/nginx.conf
@@ -0,0 +1,22 @@
+worker_processes 1;
+
+error_log   /dev/stderr    warn;
+pid         /opt/drycc/nginx/logs/nginx.pid;
+
+events {
+    worker_connections  1024;
+}
+
+http {
+    include      /opt/drycc/nginx/conf/mime.types;
+    default_type application/octet-stream;
+
+    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
+                      '$status $body_bytes_sent "$http_referer" '
+                      '"$http_user_agent" "$http_x_forwarded_for"';
+
+    access_log  /dev/stdout main;
+    sendfile on;
+    keepalive_timeout 65;
+    include /opt/drycc/nginx/conf/registry.conf;
+}
diff --git a/rootfs/opt/drycc/nginx/conf/registry.conf.tpl b/rootfs/opt/drycc/nginx/conf/registry.conf.tpl
new file mode 100644
index 0000000..627f2f2
--- /dev/null
+++ b/rootfs/opt/drycc/nginx/conf/registry.conf.tpl
@@ -0,0 +1,24 @@
+upstream container-registry {
+    server %REGISTRY_HOST%;
+}
+
+server {
+    listen 8080;
+    server_name localhost;
+    # disable any limits to avoid HTTP 413 for large image uploads
+    client_max_body_size 0;
+    # required to avoid HTTP 411: see Issue #1486 (https://github.com/moby/moby/issues/1486)
+    chunked_transfer_encoding on;
+    location / {
+        proxy_pass                          http://container-registry;
+        proxy_set_header  Host              $http_host;   # required for container client's sake
+        proxy_set_header  X-Real-IP         $remote_addr; # pass on real client's IP
+        proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;
+        proxy_set_header  X-Forwarded-Proto $scheme;
+        proxy_read_timeout                  900;
+        proxy_set_header  Authorization     "Basic %AUTHORIZATION%";
+        limit_except      GET HEAD OPTIONS  {
+            deny all;
+        }
+    }
+}
\ No newline at end of file
diff --git a/versioning.mk b/versioning.mk
index 2bfd999..8b30958 100644
--- a/versioning.mk
+++ b/versioning.mk
@@ -11,13 +11,13 @@ info:
 	@echo "Immutable tag:   ${IMAGE}"
 	@echo "Mutable tag:     ${MUTABLE_IMAGE}"
 
-.PHONY: docker-push
-docker-push: docker-immutable-push docker-mutable-push
+.PHONY: podman-push
+podman-push: podman-immutable-push podman-mutable-push
 
-.PHONY: docker-immutable-push
-docker-immutable-push:
-	docker push ${IMAGE}
+.PHONY: podman-immutable-push
+podman-immutable-push:
+	podman push ${IMAGE}
 
-.PHONY: docker-mutable-push
-docker-mutable-push:
-	docker push ${MUTABLE_IMAGE}
+.PHONY: podman-mutable-push
+podman-mutable-push:
+	podman push ${MUTABLE_IMAGE}
