Merge changes I3e95263c,Ibf4d4e9f,Id9b6a1ec

* changes:
  Factor out Git commands into the GitRepo class
  Allow to specify multiple refSpec values
  Convert fetch-job.sh to fetch-job.py
diff --git a/.gitignore b/.gitignore
index 127f652..1628c5f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,5 @@
 .project
 
 .idea/
+
+dist
diff --git a/Documentation/examples/1-gerritcluster.yaml b/Documentation/examples/1-gerritcluster.yaml
index be5f033..4cf206f 100644
--- a/Documentation/examples/1-gerritcluster.yaml
+++ b/Documentation/examples/1-gerritcluster.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritCluster
 metadata:
   name: gerrit
diff --git a/Documentation/examples/2-gerritcluster-with-replica.yaml b/Documentation/examples/2-gerritcluster-with-replica.yaml
index 6c490dd..83093c4 100644
--- a/Documentation/examples/2-gerritcluster-with-replica.yaml
+++ b/Documentation/examples/2-gerritcluster-with-replica.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritCluster
 metadata:
   name: gerrit
diff --git a/Documentation/examples/3-gerritcluster-istio.yaml b/Documentation/examples/3-gerritcluster-istio.yaml
index a0ad6bc..a7b820f 100644
--- a/Documentation/examples/3-gerritcluster-istio.yaml
+++ b/Documentation/examples/3-gerritcluster-istio.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritCluster
 metadata:
   name: gerrit
diff --git a/Documentation/examples/4-gerritcluster-ha-replica.yaml b/Documentation/examples/4-gerritcluster-ha-replica.yaml
index 308285d..cb6b1c0 100644
--- a/Documentation/examples/4-gerritcluster-ha-replica.yaml
+++ b/Documentation/examples/4-gerritcluster-ha-replica.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritCluster
 metadata:
   name: gerrit
diff --git a/Documentation/examples/5-gerritcluster-refdb.yaml b/Documentation/examples/5-gerritcluster-refdb.yaml
index 9773d47..0281aed 100644
--- a/Documentation/examples/5-gerritcluster-refdb.yaml
+++ b/Documentation/examples/5-gerritcluster-refdb.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritCluster
 metadata:
   name: gerrit
diff --git a/Documentation/examples/6-gerritcluster-ha-primary.yaml b/Documentation/examples/6-gerritcluster-ha-primary.yaml
index 4b67609..dd1926d 100644
--- a/Documentation/examples/6-gerritcluster-ha-primary.yaml
+++ b/Documentation/examples/6-gerritcluster-ha-primary.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritCluster
 metadata:
   name: gerrit
diff --git a/Documentation/examples/gerritcluster-3-nodes-pr-kafka-multisite.yaml b/Documentation/examples/gerritcluster-3-nodes-pr-kafka-multisite.yaml
index 58bb493..a5ba33c 100644
--- a/Documentation/examples/gerritcluster-3-nodes-pr-kafka-multisite.yaml
+++ b/Documentation/examples/gerritcluster-3-nodes-pr-kafka-multisite.yaml
@@ -16,7 +16,7 @@
 type: Opaque
 
 ---
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritCluster
 metadata:
   name: gerrit
diff --git a/Documentation/operator-api-reference.md b/Documentation/operator-api-reference.md
index 20dafe3..53a10a3 100644
--- a/Documentation/operator-api-reference.md
+++ b/Documentation/operator-api-reference.md
@@ -87,7 +87,7 @@
 ---
 
 **Group**: gerritoperator.google.com \
-**Version**: v1beta16 \
+**Version**: v1beta17 \
 **Kind**: GerritCluster
 
 ---
@@ -104,7 +104,7 @@
 Example:
 
 ```yaml
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritCluster
 metadata:
   name: gerrit
@@ -164,6 +164,7 @@
     istio:
       gatewaySelector:
         istio: ingressgateway
+      mTLS: true
 
   refdb:
     database: NONE
@@ -426,7 +427,7 @@
 ---
 
 **Group**: gerritoperator.google.com \
-**Version**: v1beta16 \
+**Version**: v1beta17 \
 **Kind**: Gerrit
 
 ---
@@ -443,7 +444,7 @@
 Example:
 
 ```yaml
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: Gerrit
 metadata:
   name: gerrit
@@ -657,7 +658,7 @@
 ---
 
 **Group**: gerritoperator.google.com \
-**Version**: v1beta16 \
+**Version**: v1beta17 \
 **Kind**: Receiver
 
 ---
@@ -674,7 +675,7 @@
 Example:
 
 ```yaml
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: Receiver
 metadata:
   name: receiver
@@ -785,7 +786,7 @@
 ---
 
 **Group**: gerritoperator.google.com \
-**Version**: v1beta16 \
+**Version**: v1beta17 \
 **Kind**: GitGarbageCollection
 
 ---
@@ -802,7 +803,7 @@
 Example:
 
 ```yaml
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GitGarbageCollection
 metadata:
   name: gitgc
@@ -846,7 +847,7 @@
 ---
 
 **Group**: gerritoperator.google.com \
-**Version**: v1beta16 \
+**Version**: v1beta17 \
 **Kind**: GerritNetwork
 
 ---
@@ -862,7 +863,7 @@
 Example:
 
 ```yaml
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritNetwork
 metadata:
   name: gerrit-network
@@ -877,6 +878,7 @@
     istio:
       gatewaySelector:
         istio: ingressgateway
+      mTLS: true
   receiver:
     name: receiver
     httpPort: 80
@@ -895,7 +897,7 @@
 ---
 
 **Group**: gerritoperator.google.com \
-**Version**: v1beta16 \
+**Version**: v1beta17 \
 **Kind**: IncomingReplicationTask
 
 ---
@@ -911,7 +913,7 @@
 Example:
 
 ```yaml
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: IncomingReplicationTask
 metadata:
   name: incoming-repl-task
@@ -970,7 +972,7 @@
 ---
 
 **Group**: gerritoperator.google.com \
-**Version**: v1beta16 \
+**Version**: v1beta17 \
 **Kind**: GerritIndexer
 
 ---
@@ -986,7 +988,7 @@
 Example:
 
 ```yaml
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritIndexer
 metadata:
   name: gerrit-indexer
@@ -1169,6 +1171,7 @@
 | Field | Type | Description |
 |---|---|---|
 | `gatewaySelector` | `Map<String, String>` | Labels used to select the Istio Ingressgateway Deployment to use (default: `istio: ingressgateway`) |
+| `mTLS` | `boolean` | Whether mTLS is enabled in the Istio service mesh (default: `false`) |
 
 ## GlobalRefDbConfig
 
diff --git a/build b/build
index 9202147..50fc8db 100755
--- a/build
+++ b/build
@@ -16,10 +16,12 @@
 
 usage() {
     me=`basename "$0"`
-    echo >&2 "Usage: $me [--help] [--tag TAG] [--no-cache] [--gerrit-url URL] [--base-image IMAGE] [--platform PLATFORM] [--branch GERRIT_BRANCH] [IMAGE]"
+    echo >&2 "Usage: $me [--help] [--tag TAG] [--no-cache] [--gerrit-url URL] [--base-image IMAGE] [--platform PLATFORM] [--branch GERRIT_BRANCH] [--buildah] [IMAGE]"
     exit 1
 }
 
+BUILD_TOOL="docker"
+
 while test $# -gt 0 ; do
   case "$1" in
   --help)
@@ -67,6 +69,11 @@
     shift
     ;;
 
+  --buildah)
+    BUILD_TOOL="buildah"
+    shift
+    ;;
+
   *)
     break
   esac
@@ -79,25 +86,30 @@
 PLATFORM=${PLATFORM:-linux/amd64}  # Default value if PLATFORM is not set
 GERRIT_BRANCH=${GERRIT_BRANCH:-master} # Default value if GERRIT_BRANCH is not set
 DOCKER_BUILD_OPTS="--platform=$PLATFORM"
+if [[ $BUILD_TOOL = buildah ]]; then
+    DOCKER_BUILD_OPTS="$DOCKER_BUILD_OPTS --isolation=chroot"
+    GERRIT_VER_OPTS="$GERRIT_VER_OPTS --no-docker"
+fi
 test -n "$NO_CACHE" && DOCKER_BUILD_OPTS="$DOCKER_BUILD_OPTS --no-cache"
 
 if test -n "$GERRIT_WAR_URL"; then
     BUILD_ARGS="--build-arg GERRIT_WAR_URL=$GERRIT_WAR_URL"
+    GERRIT_VER_OPTS="$GERRIT_VER_OPTS --gerrit-url $GERRIT_WAR_URL"
 fi
 
 if test -n "$HEALTHCHECK_JAR_URL"; then
     BUILD_ARGS="$BUILD_ARGS --build-arg HEALTHCHECK_JAR_URL=$HEALTHCHECK_JAR_URL"
 fi
 
-export REV="$(./get_version.sh --output K8SGERRIT --platform $PLATFORM)"
+export K8SGERRIT_VER="$(./get_version.sh --output K8SGERRIT)"
 
 docker_build(){
     IMAGE=$1
 
-    docker build \
+    $BUILD_TOOL build \
         $DOCKER_BUILD_OPTS \
-        --build-arg TAG=$REV \
-        --build-arg GERRIT_VER="$(./get_version.sh --output GERRIT --platform $PLATFORM)" \
+        --build-arg TAG=$K8SGERRIT_VER \
+        --build-arg GERRIT_VER="$GERRIT_VER" \
         -t k8sgerrit/$IMAGE:$TAG \
         ./container-images/$IMAGE
 
@@ -110,19 +122,21 @@
 }
 
 docker_build_gerrit_base(){
-    BUILD_ARGS="$BUILD_ARGS --build-arg TAG=$REV --build-arg GERRIT_BRANCH=$GERRIT_BRANCH"
-    docker build \
+    BUILD_ARGS="$BUILD_ARGS --build-arg TAG=$K8SGERRIT_VER --build-arg GERRIT_BRANCH=$GERRIT_BRANCH"
+    $BUILD_TOOL build \
         $DOCKER_BUILD_OPTS \
         $BUILD_ARGS \
-        -t gerrit-base:$REV \
+        -t gerrit-base:$K8SGERRIT_VER \
         ./container-images/gerrit-base
     if test $? -ne 0; then
         echo -e "\n\nFailed to build gerrit-base image."
         exit 1
     fi
 
+    export GERRIT_VER="$(./get_version.sh --output GERRIT --platform $PLATFORM $GERRIT_VER_OPTS)"
+
     if test -z "$TAG"; then
-        export TAG="$(./get_version.sh --platform $PLATFORM)"
+        export TAG="$(./get_version.sh --platform $PLATFORM $GERRIT_VER_OPTS)"
     fi
 }
 
@@ -133,7 +147,7 @@
     BASE_BUILD_ARGS="--build-arg BASE_IMAGE=$BASE_IMAGE"
 fi
 
-docker build $DOCKER_BUILD_OPTS $BASE_BUILD_ARGS -t base:$REV ./container-images/base
+$BUILD_TOOL build $DOCKER_BUILD_OPTS $BASE_BUILD_ARGS -t base:$K8SGERRIT_VER ./container-images/base
 if test $? -ne 0; then
     echo -e "\n\nFailed to build base image."
     exit 1
@@ -150,7 +164,7 @@
             docker_build_gerrit_base
         else
             if test -z "$TAG"; then
-                TAG="$REV-unknown"
+                TAG="$K8SGERRIT_VER-unknown"
             fi
             echo -e "\nNo Image containing Gerrit will be built." \
                     "The Gerrit-version can thus not be determinded." \
diff --git a/container-images/base/Dockerfile b/container-images/base/Dockerfile
index b3b2c30..48f4aae 100644
--- a/container-images/base/Dockerfile
+++ b/container-images/base/Dockerfile
@@ -1,4 +1,4 @@
-ARG BASE_IMAGE=alpine:3.21.3
+ARG BASE_IMAGE=alpine:3.23.4
 FROM $BASE_IMAGE
 
 ENV LC_ALL=C.UTF-8
diff --git a/container-images/gerrit-base/Dockerfile b/container-images/gerrit-base/Dockerfile
index 6eb2fc9..eec00cc 100644
--- a/container-images/gerrit-base/Dockerfile
+++ b/container-images/gerrit-base/Dockerfile
@@ -6,7 +6,7 @@
       coreutils \
       curl \
       openssh-keygen \
-      openjdk21
+      openjdk25
 
 RUN mkdir -p /var/gerrit/bin && \
     mkdir -p /var/gerrit/etc && \
diff --git a/crd/current/gerritclusters.gerritoperator.google.com-v1.yml b/crd/current/gerritclusters.gerritoperator.google.com-v1.yml
index 192598b..54914f2 100644
--- a/crd/current/gerritclusters.gerritoperator.google.com-v1.yml
+++ b/crd/current/gerritclusters.gerritoperator.google.com-v1.yml
@@ -13,7 +13,7 @@
     singular: gerritcluster
   scope: Namespaced
   versions:
-  - name: v1beta16
+  - name: v1beta17
     schema:
       openAPIV3Schema:
         properties:
@@ -914,6 +914,8 @@
                         additionalProperties:
                           type: string
                         type: object
+                      mTLS:
+                        type: boolean
                     type: object
                   pathPrefix:
                     type: string
diff --git a/crd/current/gerritindexers.gerritoperator.google.com-v1.yml b/crd/current/gerritindexers.gerritoperator.google.com-v1.yml
index 586531b..1603b3c 100644
--- a/crd/current/gerritindexers.gerritoperator.google.com-v1.yml
+++ b/crd/current/gerritindexers.gerritoperator.google.com-v1.yml
@@ -13,7 +13,7 @@
     singular: gerritindexer
   scope: Namespaced
   versions:
-  - name: v1beta16
+  - name: v1beta17
     schema:
       openAPIV3Schema:
         properties:
diff --git a/crd/current/gerritmaintenances.gerritoperator.google.com-v1.yml b/crd/current/gerritmaintenances.gerritoperator.google.com-v1.yml
index b633fb1..f5c6c18 100644
--- a/crd/current/gerritmaintenances.gerritoperator.google.com-v1.yml
+++ b/crd/current/gerritmaintenances.gerritoperator.google.com-v1.yml
@@ -11,7 +11,7 @@
     singular: gerritmaintenance
   scope: Namespaced
   versions:
-  - name: v1beta16
+  - name: v1beta17
     schema:
       openAPIV3Schema:
         properties:
diff --git a/crd/current/gerritnetworks.gerritoperator.google.com-v1.yml b/crd/current/gerritnetworks.gerritoperator.google.com-v1.yml
index e1f1562..ac47f95 100644
--- a/crd/current/gerritnetworks.gerritoperator.google.com-v1.yml
+++ b/crd/current/gerritnetworks.gerritoperator.google.com-v1.yml
@@ -13,7 +13,7 @@
     singular: gerritnetwork
   scope: Namespaced
   versions:
-  - name: v1beta16
+  - name: v1beta17
     schema:
       openAPIV3Schema:
         properties:
@@ -53,6 +53,8 @@
                         additionalProperties:
                           type: string
                         type: object
+                      mTLS:
+                        type: boolean
                     type: object
                   pathPrefix:
                     type: string
diff --git a/crd/current/gerrits.gerritoperator.google.com-v1.yml b/crd/current/gerrits.gerritoperator.google.com-v1.yml
index bd20b5d..3d8a00b 100644
--- a/crd/current/gerrits.gerritoperator.google.com-v1.yml
+++ b/crd/current/gerrits.gerritoperator.google.com-v1.yml
@@ -13,7 +13,7 @@
     singular: gerrit
   scope: Namespaced
   versions:
-  - name: v1beta16
+  - name: v1beta17
     schema:
       openAPIV3Schema:
         properties:
diff --git a/crd/current/gitgcs.gerritoperator.google.com-v1.yml b/crd/current/gitgcs.gerritoperator.google.com-v1.yml
index a5756d3..6d70443 100644
--- a/crd/current/gitgcs.gerritoperator.google.com-v1.yml
+++ b/crd/current/gitgcs.gerritoperator.google.com-v1.yml
@@ -13,7 +13,7 @@
     singular: gitgarbagecollection
   scope: Namespaced
   versions:
-  - name: v1beta16
+  - name: v1beta17
     schema:
       openAPIV3Schema:
         properties:
diff --git a/crd/current/incomingreplicationtasks.gerritoperator.google.com-v1.yml b/crd/current/incomingreplicationtasks.gerritoperator.google.com-v1.yml
index 5fab4ad..7c00b02 100644
--- a/crd/current/incomingreplicationtasks.gerritoperator.google.com-v1.yml
+++ b/crd/current/incomingreplicationtasks.gerritoperator.google.com-v1.yml
@@ -11,7 +11,7 @@
     singular: incomingreplicationtask
   scope: Namespaced
   versions:
-  - name: v1beta16
+  - name: v1beta17
     schema:
       openAPIV3Schema:
         properties:
diff --git a/crd/current/receivers.gerritoperator.google.com-v1.yml b/crd/current/receivers.gerritoperator.google.com-v1.yml
index ded8d2c..f3f1fe1 100644
--- a/crd/current/receivers.gerritoperator.google.com-v1.yml
+++ b/crd/current/receivers.gerritoperator.google.com-v1.yml
@@ -13,7 +13,7 @@
     singular: receiver
   scope: Namespaced
   versions:
-  - name: v1beta16
+  - name: v1beta17
     schema:
       openAPIV3Schema:
         properties:
diff --git a/crd/deprecated/gerritclusters.gerritoperator.google.com-v1.yml b/crd/deprecated/gerritclusters.gerritoperator.google.com-v1.yml
index 31c484b..524d335 100644
--- a/crd/deprecated/gerritclusters.gerritoperator.google.com-v1.yml
+++ b/crd/deprecated/gerritclusters.gerritoperator.google.com-v1.yml
@@ -13,7 +13,7 @@
     singular: gerritcluster
   scope: Namespaced
   versions:
-    - name: v1beta15
+    - name: v1beta16
       schema:
         openAPIV3Schema:
           properties:
@@ -1566,10 +1566,22 @@
                   properties:
                     database:
                       enum:
+                        - DYNAMODB
                         - NONE
                         - SPANNER
                         - ZOOKEEPER
                       type: string
+                    dynamoDb:
+                      properties:
+                        endpoint:
+                          type: string
+                        locksTableName:
+                          type: string
+                        refsDbTableName:
+                          type: string
+                        region:
+                          type: string
+                      type: object
                     spanner:
                       properties:
                         database:
diff --git a/crd/deprecated/gerritindexers.gerritoperator.google.com-v1.yml b/crd/deprecated/gerritindexers.gerritoperator.google.com-v1.yml
index fe2574a..803a6b3 100644
--- a/crd/deprecated/gerritindexers.gerritoperator.google.com-v1.yml
+++ b/crd/deprecated/gerritindexers.gerritoperator.google.com-v1.yml
@@ -13,7 +13,7 @@
     singular: gerritindexer
   scope: Namespaced
   versions:
-    - name: v1beta15
+    - name: v1beta16
       schema:
         openAPIV3Schema:
           properties:
diff --git a/crd/deprecated/gerritmaintenances.gerritoperator.google.com-v1.yml b/crd/deprecated/gerritmaintenances.gerritoperator.google.com-v1.yml
index 800a9ff..91bbfb7 100644
--- a/crd/deprecated/gerritmaintenances.gerritoperator.google.com-v1.yml
+++ b/crd/deprecated/gerritmaintenances.gerritoperator.google.com-v1.yml
@@ -11,7 +11,7 @@
     singular: gerritmaintenance
   scope: Namespaced
   versions:
-    - name: v1beta15
+    - name: v1beta16
       schema:
         openAPIV3Schema:
           properties:
diff --git a/crd/deprecated/gerritnetworks.gerritoperator.google.com-v1.yml b/crd/deprecated/gerritnetworks.gerritoperator.google.com-v1.yml
index d5ebe0f..6916ebf 100644
--- a/crd/deprecated/gerritnetworks.gerritoperator.google.com-v1.yml
+++ b/crd/deprecated/gerritnetworks.gerritoperator.google.com-v1.yml
@@ -13,7 +13,7 @@
     singular: gerritnetwork
   scope: Namespaced
   versions:
-    - name: v1beta15
+    - name: v1beta16
       schema:
         openAPIV3Schema:
           properties:
diff --git a/crd/deprecated/gerrits.gerritoperator.google.com-v1.yml b/crd/deprecated/gerrits.gerritoperator.google.com-v1.yml
index a6942f2..c549565 100644
--- a/crd/deprecated/gerrits.gerritoperator.google.com-v1.yml
+++ b/crd/deprecated/gerrits.gerritoperator.google.com-v1.yml
@@ -13,7 +13,7 @@
     singular: gerrit
   scope: Namespaced
   versions:
-    - name: v1beta15
+    - name: v1beta16
       schema:
         openAPIV3Schema:
           properties:
@@ -663,10 +663,22 @@
                   properties:
                     database:
                       enum:
+                        - DYNAMODB
                         - NONE
                         - SPANNER
                         - ZOOKEEPER
                       type: string
+                    dynamoDb:
+                      properties:
+                        endpoint:
+                          type: string
+                        locksTableName:
+                          type: string
+                        refsDbTableName:
+                          type: string
+                        region:
+                          type: string
+                      type: object
                     spanner:
                       properties:
                         database:
diff --git a/crd/deprecated/gitgcs.gerritoperator.google.com-v1.yml b/crd/deprecated/gitgcs.gerritoperator.google.com-v1.yml
index ff0cc93..fc2f252 100644
--- a/crd/deprecated/gitgcs.gerritoperator.google.com-v1.yml
+++ b/crd/deprecated/gitgcs.gerritoperator.google.com-v1.yml
@@ -13,7 +13,7 @@
     singular: gitgarbagecollection
   scope: Namespaced
   versions:
-    - name: v1beta15
+    - name: v1beta16
       schema:
         openAPIV3Schema:
           properties:
diff --git a/crd/deprecated/incomingreplicationtasks.gerritoperator.google.com-v1.yml b/crd/deprecated/incomingreplicationtasks.gerritoperator.google.com-v1.yml
index 9469597..6aa75fe 100644
--- a/crd/deprecated/incomingreplicationtasks.gerritoperator.google.com-v1.yml
+++ b/crd/deprecated/incomingreplicationtasks.gerritoperator.google.com-v1.yml
@@ -11,7 +11,7 @@
     singular: incomingreplicationtask
   scope: Namespaced
   versions:
-    - name: v1beta15
+    - name: v1beta16
       schema:
         openAPIV3Schema:
           properties:
diff --git a/crd/deprecated/receivers.gerritoperator.google.com-v1.yml b/crd/deprecated/receivers.gerritoperator.google.com-v1.yml
index 8343a1e..6aa02a4 100644
--- a/crd/deprecated/receivers.gerritoperator.google.com-v1.yml
+++ b/crd/deprecated/receivers.gerritoperator.google.com-v1.yml
@@ -13,7 +13,7 @@
     singular: receiver
   scope: Namespaced
   versions:
-    - name: v1beta15
+    - name: v1beta16
       schema:
         openAPIV3Schema:
           properties:
diff --git a/get_version.sh b/get_version.sh
index 8647ba8..4920143 100755
--- a/get_version.sh
+++ b/get_version.sh
@@ -14,11 +14,12 @@
 
 usage() {
     me=`basename "$0"`
-    echo >&2 "Usage: $me [--help] [--output (K8SGERRIT | GERRIT | COMBINED)]"
+    echo >&2 "Usage: $me [--help] [--output (K8SGERRIT | GERRIT | COMBINED)] [--platform PLATFORM] [--gerrit-war-url GERRIT_WAR_URL] [--no-docker]"
     exit 1
 }
 
 OUTPUT="COMBINED"
+USE_DOCKER=true
 
 while test $# -gt 0 ; do
   case "$1" in
@@ -38,6 +39,17 @@
     shift
     ;;
 
+  --no-docker)
+    USE_DOCKER=false
+    shift
+    ;;
+
+  --gerrit-url)
+    shift
+    GERRIT_WAR_URL=$1
+    shift
+    ;;
+
   *)
     break
   esac
@@ -47,7 +59,7 @@
     echo "$(git describe --always --dirty --abbrev=10)"
 }
 
-getGerritVersion() {
+getGerritVersionDocker() {
     PLATFORM=${PLATFORM:-linux/amd64}  # Default value if PLATFORM is not set
     GERRIT_VERSION="$(
         docker run \
@@ -64,6 +76,41 @@
     echo "$GERRIT_VERSION"
 }
 
+getGerritVersionNoDocker() {
+    GERRIT_BRANCH=${GERRIT_BRANCH:-main}
+    GERRIT_WAR_URL=${GERRIT_WAR_URL:-https://gerrit-ci.gerritforge.com/job/Gerrit-bazel-${GERRIT_BRANCH}/lastSuccessfulBuild/artifact/gerrit/bazel-bin/release.war}
+
+    # Create temporary directory for WAR file
+    TMP_DIR="./dist"
+	mkdir -p $TMP_DIR
+
+    GERRIT_WAR_FILE="$TMP_DIR/gerrit.war"
+
+    if ! test -f "$GERRIT_WAR_FILE"; then
+        if ! curl -f -k -s -S -o "$GERRIT_WAR_FILE" "$GERRIT_WAR_URL" 2>&1 >&2; then
+            echo "Error: Failed to download Gerrit WAR file from $GERRIT_WAR_URL" >&2
+            exit 1
+        fi
+    fi
+
+    GERRIT_VERSION="$(java -jar "$GERRIT_WAR_FILE" version | cut -d' ' -f3)"
+
+    if [ -z "$GERRIT_VERSION" ]; then
+        echo "Error: Could not extract Gerrit version from WAR file" >&2
+        exit 1
+    fi
+
+    echo "$GERRIT_VERSION"
+}
+
+getGerritVersion() {
+    if [ "$USE_DOCKER" = "true" ]; then
+        getGerritVersionDocker
+    else
+        getGerritVersionNoDocker
+    fi
+}
+
 case $OUTPUT in
     K8SGERRIT)
         echo "$(getK8sVersion)"
diff --git a/helm-charts/gerrit-operator-crds/templates/gerritclusters.gerritoperator.google.com-v1.yml b/helm-charts/gerrit-operator-crds/templates/gerritclusters.gerritoperator.google.com-v1.yml
index 3010ba2..94c43d3 100644
--- a/helm-charts/gerrit-operator-crds/templates/gerritclusters.gerritoperator.google.com-v1.yml
+++ b/helm-charts/gerrit-operator-crds/templates/gerritclusters.gerritoperator.google.com-v1.yml
@@ -13,7 +13,7 @@
     singular: gerritcluster
   scope: Namespaced
   versions:
-    - name: v1beta16
+    - name: v1beta17
       schema:
         openAPIV3Schema:
           properties:
@@ -914,6 +914,8 @@
                           additionalProperties:
                             type: string
                           type: object
+                        mTLS:
+                          type: boolean
                       type: object
                     pathPrefix:
                       type: string
@@ -2561,7 +2563,7 @@
       storage: true
       subresources:
         status: {}
-    - name: v1beta15
+    - name: v1beta16
       schema:
         openAPIV3Schema:
           properties:
@@ -4114,10 +4116,22 @@
                   properties:
                     database:
                       enum:
+                        - DYNAMODB
                         - NONE
                         - SPANNER
                         - ZOOKEEPER
                       type: string
+                    dynamoDb:
+                      properties:
+                        endpoint:
+                          type: string
+                        locksTableName:
+                          type: string
+                        refsDbTableName:
+                          type: string
+                        region:
+                          type: string
+                      type: object
                     spanner:
                       properties:
                         database:
diff --git a/helm-charts/gerrit-operator-crds/templates/gerritindexers.gerritoperator.google.com-v1.yml b/helm-charts/gerrit-operator-crds/templates/gerritindexers.gerritoperator.google.com-v1.yml
index 46f26d3..4e5f95a 100644
--- a/helm-charts/gerrit-operator-crds/templates/gerritindexers.gerritoperator.google.com-v1.yml
+++ b/helm-charts/gerrit-operator-crds/templates/gerritindexers.gerritoperator.google.com-v1.yml
@@ -13,7 +13,7 @@
     singular: gerritindexer
   scope: Namespaced
   versions:
-    - name: v1beta16
+    - name: v1beta17
       schema:
         openAPIV3Schema:
           properties:
@@ -487,7 +487,7 @@
       storage: true
       subresources:
         status: {}
-    - name: v1beta15
+    - name: v1beta16
       schema:
         openAPIV3Schema:
           properties:
diff --git a/helm-charts/gerrit-operator-crds/templates/gerritmaintenances.gerritoperator.google.com-v1.yml b/helm-charts/gerrit-operator-crds/templates/gerritmaintenances.gerritoperator.google.com-v1.yml
index c038fe3..ef2204a 100644
--- a/helm-charts/gerrit-operator-crds/templates/gerritmaintenances.gerritoperator.google.com-v1.yml
+++ b/helm-charts/gerrit-operator-crds/templates/gerritmaintenances.gerritoperator.google.com-v1.yml
@@ -11,7 +11,7 @@
     singular: gerritmaintenance
   scope: Namespaced
   versions:
-    - name: v1beta16
+    - name: v1beta17
       schema:
         openAPIV3Schema:
           properties:
@@ -555,7 +555,7 @@
       storage: true
       subresources:
         status: {}
-    - name: v1beta15
+    - name: v1beta16
       schema:
         openAPIV3Schema:
           properties:
diff --git a/helm-charts/gerrit-operator-crds/templates/gerritnetworks.gerritoperator.google.com-v1.yml b/helm-charts/gerrit-operator-crds/templates/gerritnetworks.gerritoperator.google.com-v1.yml
index 037573a..5bb8b53 100644
--- a/helm-charts/gerrit-operator-crds/templates/gerritnetworks.gerritoperator.google.com-v1.yml
+++ b/helm-charts/gerrit-operator-crds/templates/gerritnetworks.gerritoperator.google.com-v1.yml
@@ -13,7 +13,7 @@
     singular: gerritnetwork
   scope: Namespaced
   versions:
-    - name: v1beta16
+    - name: v1beta17
       schema:
         openAPIV3Schema:
           properties:
@@ -53,6 +53,8 @@
                           additionalProperties:
                             type: string
                           type: object
+                        mTLS:
+                          type: boolean
                       type: object
                     pathPrefix:
                       type: string
@@ -141,7 +143,7 @@
       storage: true
       subresources:
         status: {}
-    - name: v1beta15
+    - name: v1beta16
       schema:
         openAPIV3Schema:
           properties:
diff --git a/helm-charts/gerrit-operator-crds/templates/gerrits.gerritoperator.google.com-v1.yml b/helm-charts/gerrit-operator-crds/templates/gerrits.gerritoperator.google.com-v1.yml
index fc3b2c8..5ff69a2 100644
--- a/helm-charts/gerrit-operator-crds/templates/gerrits.gerritoperator.google.com-v1.yml
+++ b/helm-charts/gerrit-operator-crds/templates/gerrits.gerritoperator.google.com-v1.yml
@@ -13,7 +13,7 @@
     singular: gerrit
   scope: Namespaced
   versions:
-    - name: v1beta16
+    - name: v1beta17
       schema:
         openAPIV3Schema:
           properties:
@@ -947,7 +947,7 @@
       storage: true
       subresources:
         status: {}
-    - name: v1beta15
+    - name: v1beta16
       schema:
         openAPIV3Schema:
           properties:
@@ -1597,10 +1597,22 @@
                   properties:
                     database:
                       enum:
+                        - DYNAMODB
                         - NONE
                         - SPANNER
                         - ZOOKEEPER
                       type: string
+                    dynamoDb:
+                      properties:
+                        endpoint:
+                          type: string
+                        locksTableName:
+                          type: string
+                        refsDbTableName:
+                          type: string
+                        region:
+                          type: string
+                      type: object
                     spanner:
                       properties:
                         database:
@@ -1627,8 +1639,6 @@
                         properties:
                           name:
                             type: string
-                          request:
-                            type: string
                         type: object
                       type: array
                     limits:
diff --git a/helm-charts/gerrit-operator-crds/templates/gitgcs.gerritoperator.google.com-v1.yml b/helm-charts/gerrit-operator-crds/templates/gitgcs.gerritoperator.google.com-v1.yml
index 6a6da61..780c9a7 100644
--- a/helm-charts/gerrit-operator-crds/templates/gitgcs.gerritoperator.google.com-v1.yml
+++ b/helm-charts/gerrit-operator-crds/templates/gitgcs.gerritoperator.google.com-v1.yml
@@ -13,7 +13,7 @@
     singular: gitgarbagecollection
   scope: Namespaced
   versions:
-    - name: v1beta16
+    - name: v1beta17
       schema:
         openAPIV3Schema:
           properties:
@@ -422,7 +422,7 @@
       storage: true
       subresources:
         status: {}
-    - name: v1beta15
+    - name: v1beta16
       schema:
         openAPIV3Schema:
           properties:
diff --git a/helm-charts/gerrit-operator-crds/templates/incomingreplicationtasks.gerritoperator.google.com-v1.yml b/helm-charts/gerrit-operator-crds/templates/incomingreplicationtasks.gerritoperator.google.com-v1.yml
index acf0b0c..1c78390 100644
--- a/helm-charts/gerrit-operator-crds/templates/incomingreplicationtasks.gerritoperator.google.com-v1.yml
+++ b/helm-charts/gerrit-operator-crds/templates/incomingreplicationtasks.gerritoperator.google.com-v1.yml
@@ -11,7 +11,7 @@
     singular: incomingreplicationtask
   scope: Namespaced
   versions:
-    - name: v1beta16
+    - name: v1beta17
       schema:
         openAPIV3Schema:
           properties:
@@ -568,7 +568,7 @@
       storage: true
       subresources:
         status: {}
-    - name: v1beta15
+    - name: v1beta16
       schema:
         openAPIV3Schema:
           properties:
diff --git a/helm-charts/gerrit-operator-crds/templates/receivers.gerritoperator.google.com-v1.yml b/helm-charts/gerrit-operator-crds/templates/receivers.gerritoperator.google.com-v1.yml
index baf05f4..8bfbf2e 100644
--- a/helm-charts/gerrit-operator-crds/templates/receivers.gerritoperator.google.com-v1.yml
+++ b/helm-charts/gerrit-operator-crds/templates/receivers.gerritoperator.google.com-v1.yml
@@ -13,7 +13,7 @@
     singular: receiver
   scope: Namespaced
   versions:
-    - name: v1beta16
+    - name: v1beta17
       schema:
         openAPIV3Schema:
           properties:
@@ -687,7 +687,7 @@
       storage: true
       subresources:
         status: {}
-    - name: v1beta15
+    - name: v1beta16
       schema:
         openAPIV3Schema:
           properties:
diff --git a/operator/pom.xml b/operator/pom.xml
index 48b1a54..e8707ee 100644
--- a/operator/pom.xml
+++ b/operator/pom.xml
@@ -16,7 +16,7 @@
 		<revision>1.0.0-SNAPSHOT</revision>
 
 		<fabric8.version>6.13.4</fabric8.version>
-		<flogger.version>0.8</flogger.version>
+		<flogger.version>0.9</flogger.version>
 		<guice.version>7.0.0</guice.version>
 		<javaoperatorsdk.version>4.9.7</javaoperatorsdk.version>
 		<jetty.version>11.0.25</jetty.version>
@@ -28,6 +28,7 @@
 		<platform.architecture>amd64</platform.architecture>
 		<docker.registry>docker.io</docker.registry>
 		<docker.org>k8sgerrit</docker.org>
+		<skip.docker.build>false</skip.docker.build>
 
 		<test.docker.registry>docker.io</test.docker.registry>
 		<test.docker.org>k8sgerritdev</test.docker.org>
@@ -35,6 +36,50 @@
 
 	<profiles>
 		<profile>
+			<id>tar</id>
+			<properties>
+				<skip.docker.build>true</skip.docker.build>
+			</properties>
+			<build>
+				<plugins>
+					<plugin>
+						<groupId>com.google.cloud.tools</groupId>
+						<artifactId>jib-maven-plugin</artifactId>
+						<executions>
+							<execution>
+								<id>build-tar</id>
+								<phase>package</phase>
+								<goals>
+									<goal>buildTar</goal>
+								</goals>
+								<configuration>
+									<container>
+										<mainClass>com.google.gerrit.k8s.operator.Main</mainClass>
+									</container>
+									<containerizingMode>packaged</containerizingMode>
+									<from>
+										<image>gcr.io/distroless/java17-debian12</image>
+										<platforms>
+											<platform>
+												<architecture>${platform.architecture}</architecture>
+												<os>${platform.os}</os>
+											</platform>
+										</platforms>
+									</from>
+									<to>
+										<image>gerrit-operator</image>
+										<tags>
+											<tag>${project.version}</tag>
+										</tags>
+									</to>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+		<profile>
 			<id>publish</id>
 			<build>
 				<plugins>
@@ -244,6 +289,7 @@
 			<groupId>com.google.truth</groupId>
 			<artifactId>truth</artifactId>
 			<version>1.4.4</version>
+			<scope>test</scope>
 		</dependency>
 		<dependency>
 			<groupId>org.junit.jupiter</groupId>
@@ -313,11 +359,13 @@
 				<artifactId>jib-maven-plugin</artifactId>
 				<executions>
 					<execution>
+						<id>docker-build</id>
 						<phase>package</phase>
 						<goals>
 							<goal>dockerBuild</goal>
 						</goals>
 						<configuration>
+							<skip>${skip.docker.build}</skip>
 							<container>
 								<mainClass>com.google.gerrit.k8s.operator.Main</mainClass>
 							</container>
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/Constants.java b/operator/src/main/java/com/google/gerrit/k8s/operator/Constants.java
index 6b7b830..3333d5f 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/Constants.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/Constants.java
@@ -17,7 +17,7 @@
 import com.google.inject.AbstractModule;
 
 public class Constants extends AbstractModule {
-  public static final String VERSION = "v1beta16";
+  public static final String VERSION = "v1beta17";
 
   // The resource kind always has to be plural for use in webhooks
   public static final String GERRIT_CLUSTER_KIND = "gerritclusters";
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/api/model/shared/GerritIstioConfig.java b/operator/src/main/java/com/google/gerrit/k8s/operator/api/model/shared/GerritIstioConfig.java
index 4c6318d..0e415ed 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/api/model/shared/GerritIstioConfig.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/api/model/shared/GerritIstioConfig.java
@@ -19,6 +19,7 @@
 
 public class GerritIstioConfig {
   private Map<String, String> gatewaySelector = Map.of("istio", "ingressgateway");
+  private boolean mTLS;
 
   public Map<String, String> getGatewaySelector() {
     return gatewaySelector;
@@ -28,9 +29,17 @@
     this.gatewaySelector = gatewaySelector;
   }
 
+  public boolean ismTLS() {
+    return mTLS;
+  }
+
+  public void setmTLS(boolean mTLS) {
+    this.mTLS = mTLS;
+  }
+
   @Override
   public int hashCode() {
-    return Objects.hash(gatewaySelector);
+    return Objects.hash(gatewaySelector, mTLS);
   }
 
   @Override
@@ -39,11 +48,11 @@
     if (obj == null) return false;
     if (getClass() != obj.getClass()) return false;
     GerritIstioConfig other = (GerritIstioConfig) obj;
-    return Objects.equals(gatewaySelector, other.gatewaySelector);
+    return Objects.equals(gatewaySelector, other.gatewaySelector) && mTLS == other.mTLS;
   }
 
   @Override
   public String toString() {
-    return "GerritIstioConfig [gatewaySelector=" + gatewaySelector + "]";
+    return "GerritIstioConfig [gatewaySelector=" + gatewaySelector + ", mTLS=" + mTLS + "]";
   }
 }
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/DynamoDbRefDbPluginConfigBuilder.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/DynamoDbRefDbPluginConfigBuilder.java
index b173409..87999b0 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/DynamoDbRefDbPluginConfigBuilder.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/DynamoDbRefDbPluginConfigBuilder.java
@@ -28,14 +28,13 @@
 
   private static List<RequiredOption<?>> collectRequiredOptions(Gerrit gerrit) {
     List<RequiredOption<?>> requiredOptions = new ArrayList<>();
-    String endpoint =
-            gerrit.getSpec().getRefdb().getDynamoDb().getEndpoint();
+    String endpoint = gerrit.getSpec().getRefdb().getDynamoDb().getEndpoint();
 
     if (endpoint == null || endpoint.isEmpty()) {
       endpoint =
-              "https://dynamodb."
-                      + gerrit.getSpec().getRefdb().getDynamoDb().getRegion()
-                      + ".amazonaws.com";
+          "https://dynamodb."
+              + gerrit.getSpec().getRefdb().getDynamoDb().getRegion()
+              + ".amazonaws.com";
     }
     requiredOptions.add(
         new RequiredOption<String>(
@@ -45,11 +44,7 @@
             gerrit.getSpec().getRefdb().getDynamoDb().getRegion()));
 
     requiredOptions.add(
-        new RequiredOption<String>(
-            "ref-database",
-            "aws-dynamodb-refdb",
-            "endpoint",
-            endpoint));
+        new RequiredOption<String>("ref-database", "aws-dynamodb-refdb", "endpoint", endpoint));
 
     requiredOptions.add(
         new RequiredOption<String>(
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritInitConfigMap.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritInitConfigMap.java
index 7b3d183..f3985cf 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritInitConfigMap.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritInitConfigMap.java
@@ -78,7 +78,7 @@
         config.setRefdb(SPANNER.toString().toLowerCase(Locale.US));
         break;
       case DYNAMODB:
-        config.setRefdb("aws-"+DYNAMODB.toString().toLowerCase(Locale.US));
+        config.setRefdb("aws-" + DYNAMODB.toString().toLowerCase(Locale.US));
         break;
       default:
         break;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritIstioDestinationRule.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritIstioDestinationRule.java
index 4158d79..22d5f46 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritIstioDestinationRule.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritIstioDestinationRule.java
@@ -17,13 +17,19 @@
 import com.google.gerrit.k8s.operator.api.model.gerrit.GerritTemplate;
 import com.google.gerrit.k8s.operator.api.model.network.GerritNetwork;
 import com.google.gerrit.k8s.operator.cluster.GerritClusterLabelFactory;
+import com.google.gerrit.k8s.operator.gerrit.dependent.GerritHeadlessService;
 import com.google.gerrit.k8s.operator.gerrit.dependent.GerritService;
 import com.google.gerrit.k8s.operator.util.CRUDReconcileAddKubernetesDependentResource;
+import io.fabric8.istio.api.networking.v1beta1.ClientTLSSettings;
+import io.fabric8.istio.api.networking.v1beta1.ClientTLSSettingsBuilder;
+import io.fabric8.istio.api.networking.v1beta1.ClientTLSSettingsTLSmode;
 import io.fabric8.istio.api.networking.v1beta1.DestinationRule;
 import io.fabric8.istio.api.networking.v1beta1.DestinationRuleBuilder;
+import io.fabric8.istio.api.networking.v1beta1.LoadBalancerSettings;
+import io.fabric8.istio.api.networking.v1beta1.LoadBalancerSettingsBuilder;
 import io.fabric8.istio.api.networking.v1beta1.LoadBalancerSettingsSimpleLB;
-import io.fabric8.istio.api.networking.v1beta1.TrafficPolicy;
-import io.fabric8.istio.api.networking.v1beta1.TrafficPolicyBuilder;
+import io.fabric8.kubernetes.api.model.ObjectMeta;
+import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter;
 import io.javaoperatorsdk.operator.processing.dependent.BulkDependentResource;
@@ -39,54 +45,98 @@
     super(DestinationRule.class);
   }
 
-  protected DestinationRule desired(
+  private DestinationRule desired(
       GerritNetwork gerritNetwork, String gerritName, boolean isReplica) {
 
     return new DestinationRuleBuilder()
-        .withNewMetadata()
-        .withName(getName(gerritName))
-        .withNamespace(gerritNetwork.getMetadata().getNamespace())
-        .withLabels(
-            GerritClusterLabelFactory.create(
-                gerritNetwork.getMetadata().getName(),
-                getName(gerritName),
-                this.getClass().getSimpleName()))
-        .endMetadata()
+        .withMetadata(getMetaData(gerritNetwork, gerritName, ""))
         .withNewSpec()
         .withHost(
             new GerritService().getHostname(gerritName, gerritNetwork.getMetadata().getNamespace()))
-        .withTrafficPolicy(getTrafficPolicy(isReplica))
+        .withNewTrafficPolicy()
+        .withLoadBalancer(getLoadBalancerSettings(isReplica))
+        .endTrafficPolicy()
         .endSpec()
         .build();
   }
 
-  private TrafficPolicy getTrafficPolicy(boolean isReplica) {
+  private DestinationRule desiredHeadless(
+      GerritNetwork gerritNetwork, String gerritName, boolean isReplica) {
+
+    return new DestinationRuleBuilder()
+        .withMetadata(getMetaData(gerritNetwork, gerritName, "-headless-service"))
+        .withNewSpec()
+        .withHost(
+            new GerritHeadlessService()
+                .getHostname(gerritName, gerritNetwork.getMetadata().getNamespace()))
+        .withNewTrafficPolicy()
+        .withLoadBalancer(getLoadBalancerSettings(isReplica))
+        .withTls(getTls(gerritNetwork))
+        .endTrafficPolicy()
+        .endSpec()
+        .build();
+  }
+
+  private DestinationRule desiredHeadlessPod(GerritNetwork gerritNetwork, String gerritName) {
+    return new DestinationRuleBuilder()
+        .withMetadata(getMetaData(gerritNetwork, gerritName, "-headless-pods"))
+        .withNewSpec()
+        .withHost(
+            "*."
+                + new GerritHeadlessService()
+                    .getHostname(gerritName, gerritNetwork.getMetadata().getNamespace()))
+        .withNewTrafficPolicy()
+        .withTls(getTls(gerritNetwork))
+        .endTrafficPolicy()
+        .endSpec()
+        .build();
+  }
+
+  private ObjectMeta getMetaData(
+      GerritNetwork gerritNetwork, String gerritName, String nameSuffix) {
+    return new ObjectMetaBuilder()
+        .withName(getName(gerritName, nameSuffix))
+        .withNamespace(gerritNetwork.getMetadata().getNamespace())
+        .withLabels(
+            GerritClusterLabelFactory.create(
+                gerritNetwork.getMetadata().getName(),
+                getName(gerritName, nameSuffix),
+                this.getClass().getSimpleName()))
+        .build();
+  }
+
+  private LoadBalancerSettings getLoadBalancerSettings(boolean isReplica) {
     if (isReplica) {
-      return new TrafficPolicyBuilder()
-          .withNewLoadBalancer()
+      return new LoadBalancerSettingsBuilder()
           .withNewLoadBalancerSettingsSimpleLbPolicy()
           .withSimple(LoadBalancerSettingsSimpleLB.LEAST_CONN)
           .endLoadBalancerSettingsSimpleLbPolicy()
-          .endLoadBalancer()
           .build();
     }
-    return new TrafficPolicyBuilder()
-        .withNewLoadBalancer()
+    return new LoadBalancerSettingsBuilder()
         .withNewLoadBalancerSettingsConsistentHashLbPolicy()
         .withNewConsistentHash()
         .withNewLoadBalancerSettingsConsistentHashLBUseSourceIpKey(true)
         .endConsistentHash()
         .endLoadBalancerSettingsConsistentHashLbPolicy()
-        .endLoadBalancer()
         .build();
   }
 
-  public static String getName(GerritTemplate gerrit) {
-    return gerrit.getMetadata().getName();
+  private ClientTLSSettings getTls(GerritNetwork gerritNetwork) {
+    return new ClientTLSSettingsBuilder()
+        .withMode(
+            gerritNetwork.getSpec().getIngress().getIstio().ismTLS()
+                ? ClientTLSSettingsTLSmode.ISTIO_MUTUAL
+                : ClientTLSSettingsTLSmode.DISABLE)
+        .build();
   }
 
-  public static String getName(String gerritName) {
-    return gerritName;
+  public static String getName(GerritTemplate gerrit, String suffix) {
+    return getName(gerrit.getMetadata().getName(), suffix);
+  }
+
+  public static String getName(String gerritName, String suffix) {
+    return gerritName + suffix;
   }
 
   @Override
@@ -95,11 +145,21 @@
     Map<String, DestinationRule> drs = new HashMap<>();
     if (gerritNetwork.hasPrimaryGerrit()) {
       String primaryGerritName = gerritNetwork.getSpec().getPrimaryGerrit().getName();
-      drs.put(primaryGerritName, desired(gerritNetwork, primaryGerritName, false));
+      DestinationRule dstRule = desired(gerritNetwork, primaryGerritName, false);
+      drs.put(dstRule.getMetadata().getName(), dstRule);
+      DestinationRule dstRuleHeadless = desiredHeadless(gerritNetwork, primaryGerritName, false);
+      drs.put(dstRuleHeadless.getMetadata().getName(), dstRuleHeadless);
+      DestinationRule dstRuleHeadlessPod = desiredHeadlessPod(gerritNetwork, primaryGerritName);
+      drs.put(dstRuleHeadlessPod.getMetadata().getName(), dstRuleHeadlessPod);
     }
     if (gerritNetwork.hasGerritReplica()) {
       String gerritReplicaName = gerritNetwork.getSpec().getGerritReplica().getName();
-      drs.put(gerritReplicaName, desired(gerritNetwork, gerritReplicaName, true));
+      DestinationRule dstRule = desired(gerritNetwork, gerritReplicaName, true);
+      drs.put(dstRule.getMetadata().getName(), dstRule);
+      DestinationRule dstRuleHeadless = desiredHeadless(gerritNetwork, gerritReplicaName, true);
+      drs.put(dstRuleHeadless.getMetadata().getName(), dstRuleHeadless);
+      DestinationRule dstRuleHeadlessPod = desiredHeadlessPod(gerritNetwork, gerritReplicaName);
+      drs.put(dstRuleHeadlessPod.getMetadata().getName(), dstRuleHeadlessPod);
     }
     return drs;
   }
diff --git a/operator/src/test/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritClusterIstioTest.java b/operator/src/test/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritClusterIstioTest.java
index b2bb58a..7d473e0 100644
--- a/operator/src/test/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritClusterIstioTest.java
+++ b/operator/src/test/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritClusterIstioTest.java
@@ -19,9 +19,12 @@
 import com.google.gerrit.k8s.operator.Constants.ClusterMode;
 import com.google.gerrit.k8s.operator.OperatorContext;
 import com.google.gerrit.k8s.operator.api.model.network.GerritNetwork;
+import io.fabric8.istio.api.networking.v1beta1.DestinationRule;
 import io.fabric8.istio.api.networking.v1beta1.Gateway;
 import io.fabric8.istio.api.networking.v1beta1.VirtualService;
 import io.javaoperatorsdk.operator.ReconcilerUtils;
+import java.util.List;
+import java.util.Map;
 import java.util.stream.Stream;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
@@ -31,7 +34,10 @@
   @ParameterizedTest
   @MethodSource("provideYamlManifests")
   public void expectedGerritClusterIstioComponentsCreated(
-      String inputFile, String expectedGatewayOutputFile, String expectedVirtualServiceOutputFile) {
+      String inputFile,
+      String expectedGatewayOutputFile,
+      String expectedVirtualServiceOutputFile,
+      List<String> expectedDestRuleFiles) {
     OperatorContext.createInstance(ClusterMode.HIGH_AVAILABILITY, "cluster.local");
     GerritNetwork gerritNetwork =
         ReconcilerUtils.loadYaml(GerritNetwork.class, this.getClass(), inputFile);
@@ -47,6 +53,18 @@
         ReconcilerUtils.loadYaml(
             VirtualService.class, this.getClass(), expectedVirtualServiceOutputFile);
     assertThat(virtualServiceResult.getSpec()).isEqualTo(expectedVirtualService.getSpec());
+
+    GerritIstioDestinationRule destRuleDependent = new GerritIstioDestinationRule();
+    Map<String, DestinationRule> destRuleResult =
+        destRuleDependent.desiredResources(gerritNetwork, null);
+
+    for (String expectedDestRuleFile : expectedDestRuleFiles) {
+      DestinationRule expectedDestRule =
+          ReconcilerUtils.loadYaml(DestinationRule.class, this.getClass(), expectedDestRuleFile);
+      assertThat(destRuleResult).containsKey(expectedDestRule.getMetadata().getName());
+      assertThat(destRuleResult.get(expectedDestRule.getMetadata().getName()).getSpec())
+          .isEqualTo(expectedDestRule.getSpec());
+    }
   }
 
   private static Stream<Arguments> provideYamlManifests() {
@@ -54,40 +72,93 @@
         Arguments.of(
             "../../gerritnetwork_primary_replica_tls.yaml",
             "gateway_tls.yaml",
-            "virtualservice_primary_replica.yaml"),
+            "virtualservice_primary_replica.yaml",
+            List.of(
+                "destinationrule_primary.yaml",
+                "destinationrule_primary_headless.yaml",
+                "destinationrule_primary_headless_pods.yaml",
+                "destinationrule_replica.yaml",
+                "destinationrule_replica_headless.yaml",
+                "destinationrule_replica_headless_pods.yaml")),
         Arguments.of(
             "../../gerritnetwork_primary_replica.yaml",
             "gateway.yaml",
-            "virtualservice_primary_replica.yaml"),
+            "virtualservice_primary_replica.yaml",
+            List.of(
+                "destinationrule_primary.yaml",
+                "destinationrule_primary_headless.yaml",
+                "destinationrule_primary_headless_pods.yaml",
+                "destinationrule_replica.yaml",
+                "destinationrule_replica_headless.yaml",
+                "destinationrule_replica_headless_pods.yaml")),
         Arguments.of(
-            "../../gerritnetwork_primary.yaml", "gateway.yaml", "virtualservice_primary.yaml"),
+            "../../gerritnetwork_primary.yaml",
+            "gateway.yaml",
+            "virtualservice_primary.yaml",
+            List.of(
+                "destinationrule_primary.yaml",
+                "destinationrule_primary_headless.yaml",
+                "destinationrule_primary_headless_pods.yaml")),
         Arguments.of(
-            "../../gerritnetwork_replica.yaml", "gateway.yaml", "virtualservice_replica.yaml"),
+            "../../gerritnetwork_replica.yaml",
+            "gateway.yaml",
+            "virtualservice_replica.yaml",
+            List.of(
+                "destinationrule_replica.yaml",
+                "destinationrule_replica_headless.yaml",
+                "destinationrule_replica_headless_pods.yaml")),
         Arguments.of(
-            "../../gerritnetwork_receiver.yaml", "gateway.yaml", "virtualservice_receiver.yaml"),
+            "../../gerritnetwork_receiver.yaml",
+            "gateway.yaml",
+            "virtualservice_receiver.yaml",
+            List.of()),
         Arguments.of(
             "../../gerritnetwork_receiver_replica.yaml",
             "gateway.yaml",
-            "virtualservice_receiver_replica.yaml"),
+            "virtualservice_receiver_replica.yaml",
+            List.of(
+                "destinationrule_replica.yaml",
+                "destinationrule_replica_headless.yaml",
+                "destinationrule_replica_headless_pods.yaml")),
         Arguments.of(
             "../../gerritnetwork_receiver_replica_tls.yaml",
             "gateway_tls.yaml",
-            "virtualservice_receiver_replica.yaml"),
+            "virtualservice_receiver_replica.yaml",
+            List.of(
+                "destinationrule_replica.yaml",
+                "destinationrule_replica_headless.yaml",
+                "destinationrule_replica_headless_pods.yaml")),
         Arguments.of(
             "../../gerritnetwork_primary_ssh.yaml",
             "gateway_primary_ssh.yaml",
-            "virtualservice_primary_ssh.yaml"),
+            "virtualservice_primary_ssh.yaml",
+            List.of(
+                "destinationrule_primary.yaml",
+                "destinationrule_primary_headless.yaml",
+                "destinationrule_primary_headless_pods.yaml")),
         Arguments.of(
             "../../gerritnetwork_replica_ssh.yaml",
             "gateway_replica_ssh.yaml",
-            "virtualservice_replica_ssh.yaml"),
+            "virtualservice_replica_ssh.yaml",
+            List.of(
+                "destinationrule_replica.yaml",
+                "destinationrule_replica_headless.yaml",
+                "destinationrule_replica_headless_pods.yaml")),
         Arguments.of(
             "../../gerritnetwork_primary_replica_ssh.yaml",
             "gateway_primary_replica_ssh.yaml",
-            "virtualservice_primary_replica_ssh.yaml"),
+            "virtualservice_primary_replica_ssh.yaml",
+            List.of(
+                "destinationrule_replica.yaml",
+                "destinationrule_replica_headless.yaml",
+                "destinationrule_replica_headless_pods.yaml")),
         Arguments.of(
             "../../gerritnetwork_receiver_replica_ssh.yaml",
             "gateway_receiver_replica_ssh.yaml",
-            "virtualservice_receiver_replica_ssh.yaml"));
+            "virtualservice_receiver_replica_ssh.yaml",
+            List.of(
+                "destinationrule_replica.yaml",
+                "destinationrule_replica_headless.yaml",
+                "destinationrule_replica_headless_pods.yaml")));
   }
 }
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/cluster/dependent/gerrit_maintenance.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/cluster/dependent/gerrit_maintenance.yaml
index d0951f9..0453ee5 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/cluster/dependent/gerrit_maintenance.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/cluster/dependent/gerrit_maintenance.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritMaintenance
 metadata:
   name: gerrit-gerrit-maintenance
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/cluster/dependent/gerrit_primary.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/cluster/dependent/gerrit_primary.yaml
index 5f02651..1993a68 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/cluster/dependent/gerrit_primary.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/cluster/dependent/gerrit_primary.yaml
@@ -1,9 +1,9 @@
-apiVersion: gerritoperator.google.com/v1beta16
+apiVersion: gerritoperator.google.com/v1beta17
 kind: Gerrit
 metadata:
   name: gerrit
   annotations:
-    gerritoperator.google.com/apiVersion: gerritoperator.google.com/v1beta16
+    gerritoperator.google.com/apiVersion: gerritoperator.google.com/v1beta17
 spec:
   configFiles: {}
   containerImages:
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/cluster/dependent/gerrit_replica.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/cluster/dependent/gerrit_replica.yaml
index e434ad1..7c867fb 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/cluster/dependent/gerrit_replica.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/cluster/dependent/gerrit_replica.yaml
@@ -1,9 +1,9 @@
-apiVersion: gerritoperator.google.com/v1beta16
+apiVersion: gerritoperator.google.com/v1beta17
 kind: Gerrit
 metadata:
   name: gerrit-replica
   annotations:
-    gerritoperator.google.com/apiVersion: gerritoperator.google.com/v1beta16
+    gerritoperator.google.com/apiVersion: gerritoperator.google.com/v1beta17
 spec:
   configFiles: {}
   containerImages:
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/cluster/gerritcluster_primary_replica.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/cluster/gerritcluster_primary_replica.yaml
index cb916c0..24966c1 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/cluster/gerritcluster_primary_replica.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/cluster/gerritcluster_primary_replica.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritCluster
 metadata:
   name: gerrit
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/gerrit/gerrit_es_primary.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/gerrit/gerrit_es_primary.yaml
index 0a4c4cd..314ea3d 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/gerrit/gerrit_es_primary.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/gerrit/gerrit_es_primary.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: Gerrit
 metadata:
   name: gerrit
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/gerrit/gerrit_ha_primary.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/gerrit/gerrit_ha_primary.yaml
index d6906f2..0b8ee24 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/gerrit/gerrit_ha_primary.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/gerrit/gerrit_ha_primary.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: Gerrit
 metadata:
   name: gerrit
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/gerrit/gerrit_single_primary.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/gerrit/gerrit_single_primary.yaml
index 541258f..895ddd3 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/gerrit/gerrit_single_primary.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/gerrit/gerrit_single_primary.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: Gerrit
 metadata:
   name: gerrit
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/dependent/cronjob_all_default.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/dependent/cronjob_all_default.yaml
index 5c0bdc3..c418a40 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/dependent/cronjob_all_default.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/dependent/cronjob_all_default.yaml
@@ -14,7 +14,7 @@
   annotations:
     app.kubernetes.io/managed-by: gerrit-operator
   ownerReferences:
-  - apiVersion: gerritoperator.google.com/v1beta16
+  - apiVersion: gerritoperator.google.com/v1beta17
     kind: GitGarbageCollection
     name: gitgc
     uid: abcd1234
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/dependent/cronjob_all_nfs_workaround.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/dependent/cronjob_all_nfs_workaround.yaml
index fd1af7a..20c8d1e 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/dependent/cronjob_all_nfs_workaround.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/dependent/cronjob_all_nfs_workaround.yaml
@@ -14,7 +14,7 @@
   annotations:
     app.kubernetes.io/managed-by: gerrit-operator
   ownerReferences:
-  - apiVersion: gerritoperator.google.com/v1beta16
+  - apiVersion: gerritoperator.google.com/v1beta17
     kind: GitGarbageCollection
     name: gitgc
     uid: abcd1234
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/dependent/cronjob_all_options_enabled.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/dependent/cronjob_all_options_enabled.yaml
index 1a55fc5..19d2835 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/dependent/cronjob_all_options_enabled.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/dependent/cronjob_all_options_enabled.yaml
@@ -14,7 +14,7 @@
   annotations:
     app.kubernetes.io/managed-by: gerrit-operator
   ownerReferences:
-  - apiVersion: gerritoperator.google.com/v1beta16
+  - apiVersion: gerritoperator.google.com/v1beta17
     kind: GitGarbageCollection
     name: gitgc
     uid: abcd1234
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/dependent/cronjob_selected_default.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/dependent/cronjob_selected_default.yaml
index 1899e32..c03cf4e 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/dependent/cronjob_selected_default.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/dependent/cronjob_selected_default.yaml
@@ -14,7 +14,7 @@
   annotations:
     app.kubernetes.io/managed-by: gerrit-operator
   ownerReferences:
-  - apiVersion: gerritoperator.google.com/v1beta16
+  - apiVersion: gerritoperator.google.com/v1beta17
     kind: GitGarbageCollection
     name: gitgc
     uid: abcd1234
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/dependent/cronjob_selected_options_enabled.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/dependent/cronjob_selected_options_enabled.yaml
index 0f4651c..8e3f3ca 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/dependent/cronjob_selected_options_enabled.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/dependent/cronjob_selected_options_enabled.yaml
@@ -14,7 +14,7 @@
   annotations:
     app.kubernetes.io/managed-by: gerrit-operator
   ownerReferences:
-  - apiVersion: gerritoperator.google.com/v1beta16
+  - apiVersion: gerritoperator.google.com/v1beta17
     kind: GitGarbageCollection
     name: gitgc
     uid: abcd1234
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/gerritcluster_minimal.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/gerritcluster_minimal.yaml
index 075dbae..c07ec0b 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/gerritcluster_minimal.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/gerritcluster_minimal.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritCluster
 metadata:
   name: gerrit
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/gerritcluster_nfs_workaround.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/gerritcluster_nfs_workaround.yaml
index 986798c..d41cf7c 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/gerritcluster_nfs_workaround.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/gerritcluster_nfs_workaround.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritCluster
 metadata:
   name: gerrit
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/gitgc_all_default.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/gitgc_all_default.yaml
index 3d649a2..cd86d4d 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/gitgc_all_default.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/gitgc_all_default.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GitGarbageCollection
 metadata:
   name: gitgc
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/gitgc_all_options_enabled.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/gitgc_all_options_enabled.yaml
index 52aba5a..02f8d1b 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/gitgc_all_options_enabled.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/gitgc_all_options_enabled.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GitGarbageCollection
 metadata:
   name: gitgc
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/gitgc_selected_default.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/gitgc_selected_default.yaml
index f35f781..7db79d4 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/gitgc_selected_default.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/gitgc_selected_default.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GitGarbageCollection
 metadata:
   name: gitgc
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/gitgc_selected_options_enabled.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/gitgc_selected_options_enabled.yaml
index e7c0ee1..785e8b7 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/gitgc_selected_options_enabled.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/gitgc/gitgc_selected_options_enabled.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GitGarbageCollection
 metadata:
   name: gitgc
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/indexer/gerritcluster_es.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/indexer/gerritcluster_es.yaml
index 97e5a30..6befbcc 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/indexer/gerritcluster_es.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/indexer/gerritcluster_es.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritCluster
 metadata:
   name: gerrit
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/indexer/gerritcluster_minimal.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/indexer/gerritcluster_minimal.yaml
index 01cdb9a..e9fd6ac 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/indexer/gerritcluster_minimal.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/indexer/gerritcluster_minimal.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritCluster
 metadata:
   name: gerrit
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/indexer/indexer.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/indexer/indexer.yaml
index 3837e45..318c4e2 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/indexer/indexer.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/indexer/indexer.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritIndexer
 metadata:
   name: gerrit-indexer
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/indexer/indexer_es.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/indexer/indexer_es.yaml
index 6f18b49..e7d6863 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/indexer/indexer_es.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/indexer/indexer_es.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritIndexer
 metadata:
   name: gerrit-indexer
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/maintenance/gerrit-maintenance.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/maintenance/gerrit-maintenance.yaml
index d0951f9..0453ee5 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/maintenance/gerrit-maintenance.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/maintenance/gerrit-maintenance.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritMaintenance
 metadata:
   name: gerrit-gerrit-maintenance
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_primary.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_primary.yaml
index 5581e5e..3e91bab 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_primary.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_primary.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritNetwork
 metadata:
   name: gerrit
@@ -11,6 +11,8 @@
       enabled: false
     ambassador:
       id: ["my-id-1", "my-id-2"]
+    istio:
+      mTLS: true
   primaryGerrit:
     name: primary
     httpPort: 48080
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_primary_replica.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_primary_replica.yaml
index 45e7e61..e66c8ff 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_primary_replica.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_primary_replica.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritNetwork
 metadata:
   name: gerrit
@@ -11,6 +11,8 @@
       enabled: false
     ambassador:
       id: ["my-id-1", "my-id-2"]
+    istio:
+      mTLS: true
   primaryGerrit:
     name: primary
     httpPort: 48080
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_primary_replica_ssh.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_primary_replica_ssh.yaml
index 50fd437..bfe4d7b 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_primary_replica_ssh.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_primary_replica_ssh.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritNetwork
 metadata:
   name: gerrit
@@ -11,6 +11,8 @@
       enabled: false
     ssh:
       enabled: true
+    istio:
+      mTLS: true
   primaryGerrit:
     name: primary
     httpPort: 48080
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_primary_replica_tls.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_primary_replica_tls.yaml
index 963be49..b08c798 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_primary_replica_tls.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_primary_replica_tls.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritNetwork
 metadata:
   name: gerrit
@@ -12,6 +12,8 @@
       secret: tls-secret
     ambassador:
       id: ["my-id-1", "my-id-2"]
+    istio:
+      mTLS: true
   primaryGerrit:
     name: primary
     httpPort: 48080
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_primary_ssh.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_primary_ssh.yaml
index c3be283..cd987ea 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_primary_ssh.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_primary_ssh.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritNetwork
 metadata:
   name: gerrit
@@ -11,6 +11,8 @@
       enabled: false
     ssh:
       enabled: true
+    istio:
+      mTLS: true
   primaryGerrit:
     name: primary
     httpPort: 48080
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_receiver_replica.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_receiver_replica.yaml
index 69ed3e9..84e3e93 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_receiver_replica.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_receiver_replica.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritNetwork
 metadata:
   name: gerrit
@@ -11,6 +11,8 @@
       enabled: false
     ambassador:
       id: ["my-id-1", "my-id-2"]
+    istio:
+      mTLS: true
   gerritReplica:
     name: replica
     httpPort: 48080
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_receiver_replica_ssh.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_receiver_replica_ssh.yaml
index 7b84fda..5b44bc5 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_receiver_replica_ssh.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_receiver_replica_ssh.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritNetwork
 metadata:
   name: gerrit
@@ -11,6 +11,8 @@
       enabled: false
     ssh:
       enabled: true
+    istio:
+      mTLS: true
   gerritReplica:
     name: replica
     httpPort: 48080
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_receiver_replica_tls.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_receiver_replica_tls.yaml
index 8a12110..2b049b5 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_receiver_replica_tls.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_receiver_replica_tls.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritNetwork
 metadata:
   name: gerrit
@@ -12,6 +12,8 @@
       secret: tls-secret
     ambassador:
       id: ["my-id-1", "my-id-2"]
+    istio:
+      mTLS: true
   gerritReplica:
     name: replica
     httpPort: 48080
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_replica.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_replica.yaml
index a6bde98..bebdbe8 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_replica.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_replica.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritNetwork
 metadata:
   name: gerrit
@@ -11,6 +11,8 @@
       enabled: false
     ambassador:
       id: ["my-id-1", "my-id-2"]
+    istio:
+      mTLS: true
   gerritReplica:
     name: replica
     httpPort: 48080
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_replica_ssh.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_replica_ssh.yaml
index bbf946a..d6a89c4 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_replica_ssh.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/gerritnetwork_replica_ssh.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritNetwork
 metadata:
   name: gerrit
@@ -11,6 +11,8 @@
       enabled: false
     ssh:
       enabled: true
+    istio:
+      mTLS: true
   gerritReplica:
     name: replica
     httpPort: 48080
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/istio/dependent/destinationrule_primary.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/istio/dependent/destinationrule_primary.yaml
new file mode 100644
index 0000000..6b7f332
--- /dev/null
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/istio/dependent/destinationrule_primary.yaml
@@ -0,0 +1,11 @@
+apiVersion: networking.istio.io/v1
+kind: DestinationRule
+metadata:
+  name: primary
+  namespace: gerrit
+spec:
+  host: primary-service.gerrit.svc.cluster.local
+  trafficPolicy:
+    loadBalancer:
+      consistentHash:
+        useSourceIp: true
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/istio/dependent/destinationrule_primary_headless.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/istio/dependent/destinationrule_primary_headless.yaml
new file mode 100644
index 0000000..27330bc
--- /dev/null
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/istio/dependent/destinationrule_primary_headless.yaml
@@ -0,0 +1,13 @@
+apiVersion: networking.istio.io/v1
+kind: DestinationRule
+metadata:
+  name: primary-headless-service
+  namespace: gerrit
+spec:
+  host: primary.gerrit.svc.cluster.local
+  trafficPolicy:
+    loadBalancer:
+      consistentHash:
+        useSourceIp: true
+    tls:
+      mode: ISTIO_MUTUAL
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/istio/dependent/destinationrule_primary_headless_pods.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/istio/dependent/destinationrule_primary_headless_pods.yaml
new file mode 100644
index 0000000..f2ecd7c
--- /dev/null
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/istio/dependent/destinationrule_primary_headless_pods.yaml
@@ -0,0 +1,10 @@
+apiVersion: networking.istio.io/v1
+kind: DestinationRule
+metadata:
+  name: primary-headless-pods
+  namespace: gerrit
+spec:
+  host: '*.primary.gerrit.svc.cluster.local'
+  trafficPolicy:
+    tls:
+      mode: ISTIO_MUTUAL
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/istio/dependent/destinationrule_replica.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/istio/dependent/destinationrule_replica.yaml
new file mode 100644
index 0000000..812b1a8
--- /dev/null
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/istio/dependent/destinationrule_replica.yaml
@@ -0,0 +1,10 @@
+apiVersion: networking.istio.io/v1
+kind: DestinationRule
+metadata:
+  name: replica
+  namespace: gerrit
+spec:
+  host: replica-service.gerrit.svc.cluster.local
+  trafficPolicy:
+    loadBalancer:
+      simple: LEAST_CONN
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/istio/dependent/destinationrule_replica_headless.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/istio/dependent/destinationrule_replica_headless.yaml
new file mode 100644
index 0000000..823d2d3
--- /dev/null
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/istio/dependent/destinationrule_replica_headless.yaml
@@ -0,0 +1,12 @@
+apiVersion: networking.istio.io/v1
+kind: DestinationRule
+metadata:
+  name: replica-headless-service
+  namespace: gerrit
+spec:
+  host: replica.gerrit.svc.cluster.local
+  trafficPolicy:
+    loadBalancer:
+      simple: LEAST_CONN
+    tls:
+      mode: ISTIO_MUTUAL
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/istio/dependent/destinationrule_replica_headless_pods.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/istio/dependent/destinationrule_replica_headless_pods.yaml
new file mode 100644
index 0000000..3be0b9c
--- /dev/null
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/istio/dependent/destinationrule_replica_headless_pods.yaml
@@ -0,0 +1,10 @@
+apiVersion: networking.istio.io/v1
+kind: DestinationRule
+metadata:
+  name: replica-headless-pods
+  namespace: gerrit
+spec:
+  host: '*.replica.gerrit.svc.cluster.local'
+  trafficPolicy:
+    tls:
+      mode: ISTIO_MUTUAL
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/receiver/receiver.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/receiver/receiver.yaml
index 409e5b1..30e7c46 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/receiver/receiver.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/receiver/receiver.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: Receiver
 metadata:
   name: receiver
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/receiver/receiver_minimal.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/receiver/receiver_minimal.yaml
index b96ed29..022a933 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/receiver/receiver_minimal.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/receiver/receiver_minimal.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: Receiver
 metadata:
   name: receiver
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/tasks/incomingrepl/gerritcluster_incomingrepl.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/tasks/incomingrepl/gerritcluster_incomingrepl.yaml
index 068ac6b..1c1fa16 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/tasks/incomingrepl/gerritcluster_incomingrepl.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/tasks/incomingrepl/gerritcluster_incomingrepl.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: GerritCluster
 metadata:
   name: gerrit
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/tasks/incomingrepl/incomingrepltask.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/tasks/incomingrepl/incomingrepltask.yaml
index 24147a4..8a7dd07 100644
--- a/operator/src/test/resources/com/google/gerrit/k8s/operator/tasks/incomingrepl/incomingrepltask.yaml
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/tasks/incomingrepl/incomingrepltask.yaml
@@ -1,4 +1,4 @@
-apiVersion: "gerritoperator.google.com/v1beta16"
+apiVersion: "gerritoperator.google.com/v1beta17"
 kind: IncomingReplicationTask
 metadata:
   name: incoming-replication-test
diff --git a/publish b/publish
index 5205375..4ded786 100755
--- a/publish
+++ b/publish
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/bash -x
 
 # Copyright (C) 2018 The Android Open Source Project
 #
@@ -16,13 +16,14 @@
 
 usage() {
     me=`basename "$0"`
-    echo >&2 "Usage: $me [--help] [--update-latest] [--registry REGISTRY] [--org ORGANIZATION] [--no-push] [--tag TAG] [IMAGE]"
+    echo >&2 "Usage: $me [--help] [--buildah] [--update-latest] [--registry REGISTRY] [--org ORGANIZATION] [--no-push] [--tag TAG] [IMAGE]"
     exit 1
 }
 
 UPDATE_LATEST=false
 ORGANIZATION=k8sgerrit
 PUSH_IMAGES=true
+BUILD_TOOL=docker
 
 while test $# -gt 0 ; do
   case "$1" in
@@ -52,6 +53,10 @@
     TAG=$1
     shift
     ;;
+  --buildah)
+    BUILD_TOOL="buildah"
+    shift
+    ;;
   *)
     break
   esac
@@ -65,20 +70,26 @@
 source container-images/publish_list
 IMAGES=$(get_image_list)
 
+if [[ "$BUILD_TOOL" = "docker" ]]; then
+	IMG_TAG_CMD="docker image tag"
+elif [[ "$BUILD_TOOL" = "buildah" ]]; then
+	IMG_TAG_CMD="buildah tag"
+fi
+
 test -n "$REGISTRY" && [[ "$REGISTRY" != */ ]] && REGISTRY="$REGISTRY/"
 
 publish_image(){
   IMAGE=$1
   if test "$UPDATE_LATEST" = "true" ; then
-    docker image tag k8sgerrit/$IMAGE:$TAG ${REGISTRY}${ORGANIZATION}/$IMAGE:latest
+    $IMG_TAG_CMD k8sgerrit/$IMAGE:$TAG ${REGISTRY}${ORGANIZATION}/$IMAGE:latest
     if test "$PUSH_IMAGES" = "true" ; then
-      docker push "${REGISTRY}${ORGANIZATION}/$IMAGE:latest"
+      $BUILD_TOOL push "${REGISTRY}${ORGANIZATION}/$IMAGE:latest"
     fi
   fi
 
-  docker image tag k8sgerrit/$IMAGE:$TAG ${REGISTRY}${ORGANIZATION}/$IMAGE:$TAG
+  $IMG_TAG_CMD k8sgerrit/$IMAGE:$TAG ${REGISTRY}${ORGANIZATION}/$IMAGE:$TAG
   if test "$PUSH_IMAGES" = "true" ; then
-    docker push "${REGISTRY}${ORGANIZATION}/$IMAGE:$TAG"
+    $BUILD_TOOL push "${REGISTRY}${ORGANIZATION}/$IMAGE:$TAG"
   fi
 }
 
diff --git a/setup.cfg b/setup.cfg
index 7708c62..3fe9480 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -3,6 +3,7 @@
 
 markers =
   docker: Tests that require to run and interact with a docker container
+  dockerinspect: Tests that inspect image layers
   incremental: Test classes containing tests that need to run incrementally
   integration: Integration tests
   slow: Tests that run slower than the average test
diff --git a/tests/conftest.py b/tests/conftest.py
index ec5dd63..495cbdd 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -70,6 +70,21 @@
         help="If set, the docker cache will be used when building container images.",
     )
     parser.addoption(
+        "--tag",
+        action="store",
+        help="Tag of the images to test. If set, the build will be skipped.",
+    )
+    parser.addoption(
+        "--repo",
+        action="store",
+        help="Repository from where to pull the images. Only used when --tag is set.",
+    )
+    parser.addoption(
+        "--docker-host",
+        action="store",
+        help="Docker host to be used by docker client.",
+    )
+    parser.addoption(
         "--skip-slow", action="store_true", help="If set, skip slow tests."
     )
 
@@ -97,7 +112,11 @@
 
 
 @pytest.fixture(scope="session")
-def docker_client():
+def docker_client(request):
+    if request.config.getoption("--docker-host"):
+        return docker.DockerClient(
+            base_url=request.config.getoption("--docker-host"),
+            version="1.41")
     return docker.from_env()
 
 
@@ -117,6 +136,21 @@
 
 
 @pytest.fixture(scope="session")
+def docker_get(request, docker_client, docker_build):
+    def docker_get(image, name):
+        if request.config.getoption("--tag"):
+            if name in ["base", "gerrit-base"]:
+                return
+            return docker_client.images.get(
+                f"{request.config.getoption('--repo')}/{name}:{request.config.getoption('--tag')}",
+            )
+
+        return docker_build(image, name)
+
+    return docker_get
+
+
+@pytest.fixture(scope="session")
 def docker_build(
     request,
     docker_client,
@@ -142,7 +176,7 @@
 
 
 @pytest.fixture(scope="session")
-def docker_network(request, docker_client):
+def docker_network(docker_client):
     network = docker_client.networks.create(
         name="k8sgerrit-test-network", scope="local"
     )
@@ -153,35 +187,35 @@
 
 
 @pytest.fixture(scope="session")
-def base_image(container_images, docker_build):
-    return docker_build(container_images["base"], "base")
+def base_image(container_images, docker_get):
+    return docker_get(container_images["base"], "base")
 
 
 @pytest.fixture(scope="session")
-def gerrit_base_image(container_images, docker_build, base_image):
-    return docker_build(container_images["gerrit-base"], "gerrit-base")
+def gerrit_base_image(container_images, docker_get, base_image):
+    return docker_get(container_images["gerrit-base"], "gerrit-base")
 
 
 @pytest.fixture(scope="session")
-def gitgc_image(container_images, docker_build, base_image):
-    return docker_build(container_images["git-gc"], "git-gc")
+def gitgc_image(container_images, docker_get, base_image):
+    return docker_get(container_images["git-gc"], "git-gc")
 
 
 @pytest.fixture(scope="session")
-def apache_git_http_backend_image(container_images, docker_build, base_image):
-    return docker_build(
+def apache_git_http_backend_image(container_images, docker_get, base_image):
+    return docker_get(
         container_images["apache-git-http-backend"], "apache-git-http-backend"
     )
 
 
 @pytest.fixture(scope="session")
-def gerrit_image(container_images, docker_build, base_image, gerrit_base_image):
-    return docker_build(container_images["gerrit"], "gerrit")
+def gerrit_image(container_images, docker_get, base_image, gerrit_base_image):
+    return docker_get(container_images["gerrit"], "gerrit")
 
 
 @pytest.fixture(scope="session")
-def gerrit_init_image(container_images, docker_build, base_image, gerrit_base_image):
-    return docker_build(container_images["gerrit-init"], "gerrit-init")
+def gerrit_init_image(container_images, docker_get, base_image, gerrit_base_image):
+    return docker_get(container_images["gerrit-init"], "gerrit-init")
 
 
 @pytest.fixture(scope="session")
diff --git a/tests/container-images/apache-git-http-backend/test_container_build_apache_git_http_backend.py b/tests/container-images/apache-git-http-backend/test_container_build_apache_git_http_backend.py
index 8d17602..fc7faca 100644
--- a/tests/container-images/apache-git-http-backend/test_container_build_apache_git_http_backend.py
+++ b/tests/container-images/apache-git-http-backend/test_container_build_apache_git_http_backend.py
@@ -15,6 +15,6 @@
 import pytest
 
 
-@pytest.mark.structure
+@pytest.mark.build
 def test_build_apache_git_http_backend_image(apache_git_http_backend_image):
     assert apache_git_http_backend_image.id is not None
diff --git a/tests/container-images/apache-git-http-backend/test_container_structure_apache_git_http_backend.py b/tests/container-images/apache-git-http-backend/test_container_structure_apache_git_http_backend.py
index baaef0e..78603af 100755
--- a/tests/container-images/apache-git-http-backend/test_container_structure_apache_git_http_backend.py
+++ b/tests/container-images/apache-git-http-backend/test_container_structure_apache_git_http_backend.py
@@ -13,15 +13,6 @@
 # limitations under the License.
 
 import pytest
-import utils
-
-
-# pylint: disable=E1101
-@pytest.mark.structure
-def test_apache_git_http_backend_inherits_from_base(apache_git_http_backend_image):
-    assert utils.check_if_ancestor_image_is_inherited(
-        apache_git_http_backend_image, "base:latest"
-    )
 
 
 @pytest.mark.docker
@@ -55,6 +46,7 @@
 
 
 @pytest.mark.structure
+@pytest.mark.dockerinspect
 def test_apache_git_http_backend_has_entrypoint(apache_git_http_backend_image):
     entrypoint = apache_git_http_backend_image.attrs["Config"]["Entrypoint"]
     assert len(entrypoint) == 2
diff --git a/tests/container-images/base/test_container_build_base.py b/tests/container-images/base/test_container_build_base.py
deleted file mode 100644
index e0a8ff4..0000000
--- a/tests/container-images/base/test_container_build_base.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# 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.
-
-import pytest
-
-
-@pytest.mark.structure
-def test_build_base(base_image):
-    assert base_image.id is not None
diff --git a/tests/container-images/base/test_container_structure_base.py b/tests/container-images/base/test_container_structure_base.py
index 528d2b4..5cad844 100755
--- a/tests/container-images/base/test_container_structure_base.py
+++ b/tests/container-images/base/test_container_structure_base.py
@@ -15,9 +15,19 @@
 import pytest
 
 
-@pytest.fixture(scope="module")
-def container_run(docker_client, container_endless_run_factory, base_image):
-    container_run = container_endless_run_factory(docker_client, base_image)
+@pytest.fixture(
+    scope="module",
+    params=[
+        "apache_git_http_backend_image",
+        "gerrit_image",
+        "gerrit_init_image",
+        "gitgc_image",
+    ],
+)
+def container_run(request, docker_client, container_endless_run_factory):
+    container_run = container_endless_run_factory(
+        docker_client, request.getfixturevalue(request.param)
+    )
     yield container_run
     container_run.stop(timeout=1)
 
diff --git a/tests/container-images/conftest.py b/tests/container-images/conftest.py
index 8a4b8f2..3135180 100644
--- a/tests/container-images/conftest.py
+++ b/tests/container-images/conftest.py
@@ -55,7 +55,7 @@
     def start(self):
         self.container = self.docker_client.containers.run(
             image=self.image.id,
-            user="gerrit",
+            user="1000",
             volumes=self._define_volume_mounts(),
             ports={8080: str(self.port)},
             network=self.docker_network.name,
@@ -87,7 +87,7 @@
             image=image.id,
             entrypoint="/bin/ash",
             command=["-c", "tail -f /dev/null"],
-            user="gerrit",
+            user="1000",
             detach=True,
             auto_remove=True,
             platform="linux/amd64",
diff --git a/tests/container-images/gerrit-base/test_container_build_gerrit_base.py b/tests/container-images/gerrit-base/test_container_build_gerrit_base.py
deleted file mode 100644
index bd3dd5c..0000000
--- a/tests/container-images/gerrit-base/test_container_build_gerrit_base.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# 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.
-
-import pytest
-
-
-@pytest.mark.structure
-def test_build_gerrit_base(gerrit_base_image):
-    assert gerrit_base_image.id is not None
diff --git a/tests/container-images/gerrit-base/test_container_structure_gerrit_base.py b/tests/container-images/gerrit-base/test_container_structure_gerrit_base.py
index b726f0a..0e9171b 100755
--- a/tests/container-images/gerrit-base/test_container_structure_gerrit_base.py
+++ b/tests/container-images/gerrit-base/test_container_structure_gerrit_base.py
@@ -16,25 +16,19 @@
 
 import pytest
 
-import utils
+
+JAVA_VER = 25
 
 
-JAVA_VER = 21
-
-
-@pytest.fixture(scope="module")
-def container_run(docker_client, container_endless_run_factory, gerrit_base_image):
-    container_run = container_endless_run_factory(docker_client, gerrit_base_image)
+@pytest.fixture(scope="module", params=["gerrit_image", "gerrit_init_image"])
+def container_run(request, docker_client, container_endless_run_factory):
+    container_run = container_endless_run_factory(
+        docker_client, request.getfixturevalue(request.param)
+    )
     yield container_run
     container_run.stop(timeout=1)
 
 
-# pylint: disable=E1101
-@pytest.mark.structure
-def test_gerrit_base_inherits_from_base(gerrit_base_image):
-    assert utils.check_if_ancestor_image_is_inherited(gerrit_base_image, "base:latest")
-
-
 @pytest.mark.docker
 @pytest.mark.structure
 def test_gerrit_base_contains_java(container_run):
@@ -92,9 +86,3 @@
 def test_gerrit_base_war_dir_permissions(container_run):
     exit_code, _ = container_run.exec_run("test -O /var/war")
     assert exit_code == 0
-
-
-@pytest.mark.structure
-def test_gerrit_base_has_entrypoint(gerrit_base_image):
-    entrypoint = gerrit_base_image.attrs["Config"]["Entrypoint"]
-    assert "/var/tools/start" in entrypoint
diff --git a/tests/container-images/gerrit-init/test_container_build_gerrit_init.py b/tests/container-images/gerrit-init/test_container_build_gerrit_init.py
index 0fe3532..1ab4460 100644
--- a/tests/container-images/gerrit-init/test_container_build_gerrit_init.py
+++ b/tests/container-images/gerrit-init/test_container_build_gerrit_init.py
@@ -15,6 +15,6 @@
 import pytest
 
 
-@pytest.mark.structure
+@pytest.mark.build
 def test_build_gerrit_init(gerrit_init_image):
     assert gerrit_init_image.id is not None
diff --git a/tests/container-images/gerrit-init/test_container_integration_gerrit_init.py b/tests/container-images/gerrit-init/test_container_integration_gerrit_init.py
index 0670002..85f49af 100644
--- a/tests/container-images/gerrit-init/test_container_integration_gerrit_init.py
+++ b/tests/container-images/gerrit-init/test_container_integration_gerrit_init.py
@@ -28,7 +28,7 @@
     tmp_site_dir = tmp_path_factory.mktemp("gerrit_site")
     container_run = docker_client.containers.run(
         image=gerrit_init_image.id,
-        user="gerrit",
+        user="1000",
         volumes={tmp_site_dir: {"bind": "/var/gerrit", "mode": "rw"}},
         detach=True,
         auto_remove=True,
@@ -64,7 +64,7 @@
         image=gerrit_init_image.id,
         entrypoint="/bin/ash",
         command=["-c", "tail -f /dev/null"],
-        user="gerrit",
+        user="1000",
         volumes={
             tmp_site_dir: {"bind": "/var/gerrit", "mode": "rw"},
             init_config_dir: {"bind": "/var/config", "mode": "rw"},
diff --git a/tests/container-images/gerrit-init/test_container_integration_gerrit_init_reindexing.py b/tests/container-images/gerrit-init/test_container_integration_gerrit_init_reindexing.py
index 08e355e..7d60118 100644
--- a/tests/container-images/gerrit-init/test_container_integration_gerrit_init_reindexing.py
+++ b/tests/container-images/gerrit-init/test_container_integration_gerrit_init_reindexing.py
@@ -31,7 +31,7 @@
         entrypoint="/bin/ash",
         command=["-c", "tail -f /dev/null"],
         volumes={str(temp_site): {"bind": "/var/gerrit", "mode": "rw"}},
-        user="gerrit",
+        user="1000",
         detach=True,
         auto_remove=True,
         platform="linux/amd64",
diff --git a/tests/container-images/gerrit-init/test_container_structure_gerrit_init.py b/tests/container-images/gerrit-init/test_container_structure_gerrit_init.py
index 21230e9..1ee56aa 100755
--- a/tests/container-images/gerrit-init/test_container_structure_gerrit_init.py
+++ b/tests/container-images/gerrit-init/test_container_structure_gerrit_init.py
@@ -45,14 +45,6 @@
     return request.param
 
 
-# pylint: disable=E1101
-@pytest.mark.structure
-def test_gerrit_init_inherits_from_gerrit_base(gerrit_init_image):
-    assert utils.check_if_ancestor_image_is_inherited(
-        gerrit_init_image, "gerrit-base:latest"
-    )
-
-
 @pytest.mark.docker
 @pytest.mark.structure
 def test_gerrit_init_contains_expected_scripts(container_run, expected_script):
@@ -68,6 +60,7 @@
 
 
 @pytest.mark.structure
+@pytest.mark.dockerinspect
 def test_gerrit_init_has_entrypoint(gerrit_init_image):
     entrypoint = gerrit_init_image.attrs["Config"]["Entrypoint"]
     assert len(entrypoint) >= 1
diff --git a/tests/container-images/gerrit/test_container_build_gerrit.py b/tests/container-images/gerrit/test_container_build_gerrit.py
index fb94e92..f885c7c 100644
--- a/tests/container-images/gerrit/test_container_build_gerrit.py
+++ b/tests/container-images/gerrit/test_container_build_gerrit.py
@@ -15,6 +15,6 @@
 import pytest
 
 
-@pytest.mark.structure
+@pytest.mark.build
 def test_build_gerrit(gerrit_image):
     assert gerrit_image.id is not None
diff --git a/tests/container-images/gerrit/test_container_structure_gerrit.py b/tests/container-images/gerrit/test_container_structure_gerrit.py
index 7ece25e..55f8bf6 100755
--- a/tests/container-images/gerrit/test_container_structure_gerrit.py
+++ b/tests/container-images/gerrit/test_container_structure_gerrit.py
@@ -24,16 +24,15 @@
     container_run.stop(timeout=1)
 
 
-# pylint: disable=E1101
-@pytest.mark.structure
-def test_gerrit_inherits_from_gerrit_base(gerrit_image):
-    assert utils.check_if_ancestor_image_is_inherited(
-        gerrit_image, "gerrit-base:latest"
-    )
-
-
 @pytest.mark.docker
 @pytest.mark.structure
 def test_gerrit_contains_start_script(container_run):
     exit_code, _ = container_run.exec_run("test -f /var/tools/start")
     assert exit_code == 0
+
+
+@pytest.mark.structure
+@pytest.mark.dockerinspect
+def test_gerrit_base_has_entrypoint(gerrit_image):
+    entrypoint = gerrit_image.attrs["Config"]["Entrypoint"]
+    assert "/var/tools/start" in entrypoint
diff --git a/tests/container-images/git-gc/test_container_build_gitgc.py b/tests/container-images/git-gc/test_container_build_gitgc.py
index f94078c..3dfe708 100644
--- a/tests/container-images/git-gc/test_container_build_gitgc.py
+++ b/tests/container-images/git-gc/test_container_build_gitgc.py
@@ -15,6 +15,6 @@
 import pytest
 
 
-@pytest.mark.structure
+@pytest.mark.build
 def test_build_gitgc(gitgc_image):
     assert gitgc_image.id is not None
diff --git a/tests/container-images/git-gc/test_container_structure_gitgc.py b/tests/container-images/git-gc/test_container_structure_gitgc.py
index 65a3be0..c87ee8a 100644
--- a/tests/container-images/git-gc/test_container_structure_gitgc.py
+++ b/tests/container-images/git-gc/test_container_structure_gitgc.py
@@ -24,12 +24,6 @@
     container_run.stop(timeout=1)
 
 
-# pylint: disable=E1101
-@pytest.mark.structure
-def test_gitgc_inherits_from_base(gitgc_image):
-    assert utils.check_if_ancestor_image_is_inherited(gitgc_image, "base:latest")
-
-
 @pytest.mark.docker
 @pytest.mark.structure
 def test_gitgc_contains_gc_script(container_run):
@@ -38,6 +32,7 @@
 
 
 @pytest.mark.structure
+@pytest.mark.dockerinspect
 def test_gitgc_has_entrypoint(gitgc_image):
     entrypoint = gitgc_image.attrs["Config"]["Entrypoint"]
     assert len(entrypoint) == 1