diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 000000000..3587c419b
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,48 @@
+# Copyright 2021 VMware
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# These base images are behind a proxy for rate-limit reasons
+# If building locally, you can simply use:
+# docker build -t cartographer:dev --build-arg BASE_IMAGE="ubuntu:jammy" --build-arg GOLANG_IMAGE=golang:1.19 .
+
+ARG BASE_IMAGE=harbor-repo.vmware.com/dockerhub-proxy-cache/library/ubuntu:jammy
+ARG GOLANG_IMAGE=harbor-repo.vmware.com/dockerhub-proxy-cache/library/golang:1.19
+
+FROM ${BASE_IMAGE} AS ytt
+
+RUN set -x && \
+	apt-get update && \
+	apt-get install -y curl=7.81.0-1ubuntu1.14
+
+ARG ytt_CHECKSUM=a6729fb8514f10ab58f9ed3b50cd90ef79bf16d1cb29173baa84e1af0bc5ad4f
+ARG ytt_VERSION=0.45.3
+
+RUN set -eux && \
+	url=https://github.com/vmware-tanzu/carvel-ytt/releases/download/v${ytt_VERSION}/ytt-linux-amd64 ; \
+	curl -sSL $url -o /usr/local/bin/ytt && \
+	echo "${ytt_CHECKSUM}  /usr/local/bin/ytt" | sha256sum -c && \
+	chmod +x /usr/local/bin/ytt
+
+FROM ${GOLANG_IMAGE} AS cartographer
+WORKDIR /src
+COPY go.mod go.sum ./
+RUN go mod download
+COPY cmd/ ./cmd/
+COPY pkg/ ./pkg/
+RUN GOOS=linux GOARCH=amd64 go build -o /build/ github.com/vmware-tanzu/cartographer/cmd/cartographer
+
+FROM gcr.io/paketo-buildpacks/run-jammy-tiny@sha256:35702d19f93e06041db1573b1140742df2182494cc93f646fd57c6d8922dc7a7
+COPY --from=ytt 	/usr/local/bin/ytt	/usr/local/bin/ytt
+COPY --from=cartographer /build/cartographer	/usr/local/bin/cartographer
+ENTRYPOINT [ "cartographer" ]
\ No newline at end of file
diff --git a/config/manager/deployment.yaml b/config/manager/deployment.yaml
index 35eb86493..0f2e727c9 100644
--- a/config/manager/deployment.yaml
+++ b/config/manager/deployment.yaml
@@ -46,7 +46,7 @@ spec:
             secretName: cartographer-webhook
       containers:
         - name: cartographer-controller
-          image: ko://github.com/vmware-tanzu/cartographer/cmd/cartographer
+          image: #@ data.values.controller_image
           args:
             - -cert-dir=/cert
             - -metrics-port=9998
diff --git a/hack/lever_build_request.yaml b/hack/lever_build_request.yaml
new file mode 100644
index 000000000..2895340d6
--- /dev/null
+++ b/hack/lever_build_request.yaml
@@ -0,0 +1,36 @@
+# Copyright 2023 VMware
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#@ load("@ytt:data", "data")
+---
+apiVersion: supplychain.cc.build/v1alpha2
+kind: Request
+metadata:
+  name: #@ "cartographer-" + data.values.build_suffix
+  namespace: default
+spec:
+  artifacts:
+    images:
+      - name: #@ data.values.release_image
+  buildType: kaniko
+  buildConfig:
+    kanikoBuildConfig:
+      dockerfile: Dockerfile
+      extraArgs: []
+  source:
+    git:
+      ref:
+        commit: #@ data.values.commit_ref
+        branch: unused
+      url: https://github.com/vmware-tanzu/cartographer.git
diff --git a/hack/release.sh b/hack/release.sh
index ac4c43f1e..08cd2380b 100755
--- a/hack/release.sh
+++ b/hack/release.sh
@@ -17,24 +17,41 @@ set -o errexit
 set -o nounset
 set -o pipefail
 
-ROOT=$(cd "$(dirname $0)"/.. && pwd)
+ROOT=$(cd "$(dirname "$0")"/.. && pwd)
 readonly ROOT
 
 readonly SCRATCH=${SCRATCH:-$(mktemp -d)}
-readonly REGISTRY=${REGISTRY:-"$($ROOT/hack/ip.py):5001"}
+readonly REGISTRY=${REGISTRY:-"$("$ROOT"/hack/ip.py):5001"}
 readonly RELEASE_DATE=${RELEASE_DATE:-$(TZ=UTC date +"%Y-%m-%dT%H:%M:%SZ")}
 
-readonly YTT_VERSION=0.42.0
-readonly YTT_CHECKSUM=aa7074d08dc35e588ab0e014f53e98aec0cfed6c3babf8a953c4225007e49ae7
-
 main() {
         readonly RELEASE_VERSION=${RELEASE_VERSION:-"v0.0.0-dev"}
-        readonly PREVIOUS_VERSION=${PREVIOUS_VERSION:-$(git_previous_version $RELEASE_VERSION)}
+        readonly RELEASE_IMAGE=${RELEASE_IMAGE:-$REGISTRY/cartographer:$RELEASE_VERSION}
+        readonly PREVIOUS_VERSION=${PREVIOUS_VERSION:-$(git_previous_version "$RELEASE_VERSION")}
 
-        show_vars
-        cd $ROOT
+        readonly RELEASE_USING_LEVER=${RELEASE_USING_LEVER:-false}
+        readonly LEVER_COMMIT_REF=${LEVER_COMMIT_REF:-"$(git rev-parse HEAD)"}
+        readonly LEVER_KUBECONFIG=${LEVER_KUBECONFIG:-""}
 
-        download_ytt_to_kodata
+        show_vars
+        cd "$ROOT"
+
+        if [[ "$RELEASE_USING_LEVER" == true ]]; then
+        # Lever build flow
+                if [[ "$REGISTRY" == "192.168."* ]]; then
+                        echo "REGISTRY must be set to a registry accessible by lever when RELEASE_USING_LEVER is true"
+                        exit 1
+                fi
+                if [[ "$LEVER_KUBECONFIG" == "" ]]; then
+                        echo "LEVER_KUBECONFIG must be set when RELEASE_USING_LEVER is true"
+                        exit 1
+                fi
+                echo "Building using lever"
+                lever_build_request
+        else
+                echo "Building locally"
+                build_image
+        fi
         generate_release
         create_release_notes
 }
@@ -45,29 +62,71 @@ show_vars() {
         REGISTRY:               $REGISTRY
         RELEASE_DATE:           $RELEASE_DATE
         RELEASE_VERSION:        $RELEASE_VERSION
+        RELEASE_IMAGE:          $RELEASE_IMAGE
         ROOT:                   $ROOT
         SCRATCH:                $SCRATCH
-        YTT_VERSION:            $YTT_VERSION
+        RELEASE_USING_LEVER:    $RELEASE_USING_LEVER
+        LEVER_KUBECONFIG:       $LEVER_KUBECONFIG
+        LEVER_COMMIT_REF:       $LEVER_COMMIT_REF
         "
 }
 
-download_ytt_to_kodata() {
-        local url=https://github.com/vmware-tanzu/carvel-ytt/releases/download/v${YTT_VERSION}/ytt-linux-amd64
-        local fname=ytt-linux-amd64
-
-        local dest
-        dest=$(realpath ./cmd/cartographer/kodata/$fname)
+lever_build_request() {
+        # Lever build request expects LEVER_KUBECONFIG to be the kubeconfig yaml to access the lever cluster
+        BUILD_SUFFIX="$(git rev-parse HEAD | head -c 6)-$(echo $RANDOM | shasum | head -c 6; echo)"
+        ytt --ignore-unknown-comments -f ./hack/lever_build_request.yaml \
+        --data-value build_suffix="$BUILD_SUFFIX" \
+        --data-value commit_ref="$LEVER_COMMIT_REF" \
+        --data-value release_image="$RELEASE_IMAGE" \
+        | kubectl --kubeconfig <(printf '%s' "${LEVER_KUBECONFIG}") apply -f -
+        wait_for_lever_build "cartographer-$BUILD_SUFFIX"
+}
 
-        test -x $dest && echo "${YTT_CHECKSUM}  $dest" | sha256sum -c && {
-                echo "ytt already found in kodata."
-                return
-        }
+wait_for_lever_build() {
+        local build_name=$1
+        local conditions_json=""
+        local components_status="-- "
+        local build_status="-- "
+        local srp_status="-- "
+        local ready_status="-- "
+
+        local counter=1
+
+        echo "Waiting for lever build $build_name to complete..."
+        # Lever build request has a few build statuses with the final status being the aggregate and the one we wait on
+        while [[ $ready_status != 'False' && $ready_status != 'True' ]]; do
+                conditions_json=$(kubectl --kubeconfig <(printf '%s' "${LEVER_KUBECONFIG}") get request/"$build_name" -o jsonpath='{.status.conditions}')
+                components_status=$(echo "$conditions_json" | jq -r 'map(select(.type == "ComponentsReady"))[0].status')
+                build_status=$(echo "$conditions_json" | jq -r 'map(select(.type == "BuildReady"))[0].status')
+                srp_status=$(echo "$conditions_json" | jq -r 'map(select(.type == "SRPResourceSubmitted"))[0].status')
+                ready_status=$(echo "$conditions_json" | jq -r 'map(select(.type == "Ready"))[0].status')
+                loading_char=$(printf "%${counter}s")
+                printf "ComponentsReady: %s; BuildReady: %s; SRPResourceSubmitted: %s; Ready: %s; ${loading_char// /.}\033[0K\r" "$components_status" "$build_status" "$srp_status" "$ready_status"
+                counter=$((counter + 1))
+                if [[ $counter -gt 3 ]]; then
+                        counter=1
+                fi
+                sleep 2
+        done
+
+        if [[ $ready_status == 'False' ]]; then
+                echo "Lever build $build_name failed"
+                ready_message=$(echo "$conditions_json" | jq 'map(select(.type == "Ready"))[0].message')
+                echo "Error: $ready_message"
+                exit 1
+        else
+                # Output here is being parsed by the release pipeline to pass references to package-for-cartographer and catalog
+                echo "Lever build $build_name succeeded. Image published:"
+                kubectl --kubeconfig <(printf '%s' "${LEVER_KUBECONFIG}") get request/"$build_name" -o jsonpath='{.status.artifactStatus.images[0].name}'
+                echo ""
+                kubectl --kubeconfig <(printf '%s' "${LEVER_KUBECONFIG}") get request/"$build_name" -o jsonpath='{.status.artifactStatus.images[0].image.tag}'
+        fi
+}
 
-        pushd "$(mktemp -d)"
-        curl -sSOL $url
-        echo "${YTT_CHECKSUM}  $fname" | sha256sum -c
-        install -m 0755 $fname $dest
-        popd
+build_image() {
+        # Build the image locally using Docker instead of ko
+        docker build "$ROOT" -t "$RELEASE_IMAGE" --build-arg BASE_IMAGE="ubuntu:jammy" --build-arg GOLANG_IMAGE="golang:1.19"
+        docker push "$RELEASE_IMAGE"
 }
 
 generate_release() {
@@ -75,14 +134,13 @@ generate_release() {
         ytt --ignore-unknown-comments -f ./config \
                 -f ./hack/overlays/webhook-configuration.yaml \
                 -f ./hack/overlays/component-labels.yaml \
-                --data-value version=$RELEASE_VERSION |
-                KO_DOCKER_REPO=$REGISTRY ko resolve -B -f- > \
-                        ./release/cartographer.yaml
+                --data-value version="$RELEASE_VERSION" \
+                --data-value controller_image="$RELEASE_IMAGE" > ./release/cartographer.yaml
 }
 
 create_release_notes() {
         local changeset
-        changeset="$(git_changeset $RELEASE_VERSION $PREVIOUS_VERSION)"
+        changeset="$(git_changeset "$RELEASE_VERSION" "$PREVIOUS_VERSION")"
 
         local assets_checksums
         assets_checksums=$(checksums ./release)
@@ -93,7 +151,7 @@ create_release_notes() {
 checksums() {
         local assets_directory=$1
 
-        pushd $assets_directory &>/dev/null
+        pushd "$assets_directory" &>/dev/null
         find . -name "*" -type f -exec sha256sum {} +
         popd &>/dev/null
 }
@@ -102,9 +160,9 @@ git_changeset() {
         local current_version=$1
         local previous_version=$2
 
-        [[ $current_version != v* ]] && current_version=v$current_version
-        [[ $previous_version != v* ]] && previous_version=v$previous_version
-        [[ $(git tag -l $current_version) == "" ]] && current_version=HEAD
+        [[ "$current_version" != v* ]] && current_version="v${current_version}"
+        [[ "$previous_version" != v* ]] && previous_version="v${previous_version}"
+        [[ $(git tag -l "$current_version") == "" ]] && current_version=HEAD
 
         git -c log.showSignature=false \
                 log \
@@ -119,12 +177,12 @@ git_previous_version() {
         local current_version=$1
 
         local version_filter
-        version_filter=$(printf '^%s$' $current_version)
+        version_filter=$(printf '^%s$' "$current_version")
 
-        [[ $(git tag -l $current_version) == "" ]] && version_filter='.'
+        [[ $(git tag -l "$current_version") == "" ]] && version_filter='.'
 
         git tag --sort=-v:refname -l |
-                grep -A30 $version_filter |
+                grep -A30 "$version_filter" |
                 grep -E '^v[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+$' |
                 head -n1
 }
@@ -171,6 +229,8 @@ Thanks to these contributors who contributed to <NEW_TAG>!
 %s
 ```
   '
+        # wokeignore:rule=disable
+        # shellcheck disable=SC2059
         printf "$fmt" "$previous_version" "$changeset" "$checksums"
 }
 
diff --git a/hack/setup.sh b/hack/setup.sh
index c3c290d4c..5af1ea026 100755
--- a/hack/setup.sh
+++ b/hack/setup.sh
@@ -365,7 +365,7 @@ test_runnable_example() {
           exit 1
         else
           echo "waiting 5 seconds for expected passing test to succeed"
-          (( counter+1 ))
+          (( counter+=1 ))
         fi
       done
 
@@ -382,7 +382,7 @@ test_runnable_example() {
           exit 1
         else
           echo "waiting 5 seconds for expected failing test to fail"
-          (( counter+1 ))
+          (( counter+=1 ))
         fi
       done
 
@@ -403,7 +403,7 @@ test_runnable_example() {
           exit 1
         else
           echo "waiting 5 seconds for expected passing test to succeed"
-          (( counter+1 ))
+          (( counter+=1 ))
         fi
       done