Repository: rook/rook
Pull request #15295 (2025-01-20 05:49:23)

ci: Increase rgw multisite test timeout for s3 client call
<!-- Thank you for contributing to Rook! -->

<!-- STEPS TO FOLLOW:
  1. Add a description of the changes (frequently the same as the commit description)
  2. Enter the issue number next to "Resolves #" below (if there is no tracking issue resolved, **remove that section**)
  3. Review our Contributing documentation at https://rook.io/docs/rook/latest/Contributing/development-flow/
  4. Follow the steps in the checklist below, starting with the **Commit Message Formatting**.
-->

<!-- Uncomment this section with the issue number if an issue is being resolved
**Issue resolved by this Pull Request:**
Resolves #
--->

**Checklist:**

- [ ] **Commit Message Formatting**: Commit titles and messages follow guidelines in the [developer guide](https://rook.io/docs/rook/latest/Contributing/development-flow/#commit-structure).
- [ ] Reviewed the developer guide on [Submitting a Pull Request](https://rook.io/docs/rook/latest/Contributing/development-flow/#submitting-a-pull-request)
- [ ] [Pending release notes](https://github.com/rook/rook/blob/master/PendingReleaseNotes.md) updated with breaking and/or notable changes for the next minor release.
- [ ] Documentation has been updated, if necessary.
- [ ] Unit tests have been added, if necessary.
- [ ] Integration tests have been added, if necessary.



Patch:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index b83cca118..41c11b36a 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -30,7 +30,7 @@ jobs:
           go-version: "1.23"
 
       - name: Set up Helm
-        uses: azure/setup-helm@b9e51907a09c216f16ebe8536097933489208112 # v4.3.0
+        uses: azure/setup-helm@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 # v4.2.0
         with:
           version: v3.6.2
 
diff --git a/.github/workflows/daily-nightly-jobs.yml b/.github/workflows/daily-nightly-jobs.yml
index f42a7a1d6..47ae30eba 100644
--- a/.github/workflows/daily-nightly-jobs.yml
+++ b/.github/workflows/daily-nightly-jobs.yml
@@ -145,7 +145,7 @@ jobs:
           tests/scripts/collect-logs.sh
 
       - name: Artifact
-        uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         if: failure()
         with:
           name: ceph-smoke-suite-reef-artifact
@@ -185,7 +185,7 @@ jobs:
           tests/scripts/collect-logs.sh
 
       - name: Artifact
-        uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         if: failure()
         with:
           name: ceph-smoke-suite-squid-artifact
@@ -225,7 +225,7 @@ jobs:
           tests/scripts/collect-logs.sh
 
       - name: Artifact
-        uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         if: failure()
         with:
           name: ceph-smoke-suite-master-artifact
@@ -265,7 +265,7 @@ jobs:
           tests/scripts/collect-logs.sh
 
       - name: Artifact
-        uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         if: failure()
         with:
           name: ceph-object-suite-master-artifact
@@ -305,7 +305,7 @@ jobs:
           tests/scripts/collect-logs.sh
 
       - name: Artifact
-        uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         if: failure()
         with:
           name: ceph-upgrade-suite-squid-artifact
@@ -346,7 +346,7 @@ jobs:
           tests/scripts/collect-logs.sh
 
       - name: Artifact
-        uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         if: failure()
         with:
           name: ceph-upgrade-suite-reef-artifact
diff --git a/.github/workflows/golangci-lint.yaml b/.github/workflows/golangci-lint.yaml
index a853988a7..bd348960b 100644
--- a/.github/workflows/golangci-lint.yaml
+++ b/.github/workflows/golangci-lint.yaml
@@ -31,7 +31,7 @@ jobs:
         with:
           go-version: "1.23"
       - name: golangci-lint
-        uses: golangci/golangci-lint-action@2226d7cb06a077cd73e56eedd38eecad18e5d837 # v6.5.0
+        uses: golangci/golangci-lint-action@2e788936b09dd82dc280e845628a40d2ba6b204c # v6.3.1
         with:
           # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
           version: v1.62
diff --git a/.github/workflows/helm-lint.yaml b/.github/workflows/helm-lint.yaml
index 4f5aff093..7283819fb 100644
--- a/.github/workflows/helm-lint.yaml
+++ b/.github/workflows/helm-lint.yaml
@@ -31,7 +31,7 @@ jobs:
           fetch-depth: 0
 
       - name: Set up Helm
-        uses: azure/setup-helm@b9e51907a09c216f16ebe8536097933489208112 # v4.3.0
+        uses: azure/setup-helm@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 # v4.2.0
         with:
           version: v3.6.2
 
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
index 91f0accb6..65bccd344 100644
--- a/.github/workflows/scorecards.yml
+++ b/.github/workflows/scorecards.yml
@@ -33,7 +33,7 @@ jobs:
           persist-credentials: false
 
       - name: "Run analysis"
-        uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1
+        uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
         with:
           results_file: results.sarif
           results_format: sarif
@@ -55,7 +55,7 @@ jobs:
       # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
       # format to the repository Actions tab.
       - name: "Upload artifact"
-        uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         with:
           name: SARIF file
           path: results.sarif
@@ -64,6 +64,6 @@ jobs:
       # Upload the results to GitHub's code scanning dashboard (optional).
       # Commenting out will disable upload of results to your repo's Code Scanning dashboard
       - name: "Upload to code-scanning"
-        uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
+        uses: github/codeql-action/upload-sarif@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9
         with:
           sarif_file: results.sarif
diff --git a/Documentation/CRDs/Cluster/ceph-cluster-crd.md b/Documentation/CRDs/Cluster/ceph-cluster-crd.md
index 51b4e4fc1..3cfd60d2d 100755
--- a/Documentation/CRDs/Cluster/ceph-cluster-crd.md
+++ b/Documentation/CRDs/Cluster/ceph-cluster-crd.md
@@ -26,7 +26,7 @@ Settings can be specified at the global level to apply to the cluster as a whole
 * `external`:
     * `enable`: if `true`, the cluster will not be managed by Rook but via an external entity. This mode is intended to connect to an existing cluster. In this case, Rook will only consume the external cluster. However, Rook will be able to deploy various daemons in Kubernetes such as object gateways, mds and nfs if an image is provided and will refuse otherwise. If this setting is enabled **all** the other options will be ignored except `cephVersion.image` and `dataDirHostPath`. See [external cluster configuration](external-cluster/external-cluster.md). If `cephVersion.image` is left blank, Rook will refuse the creation of extra CRs like object, file and nfs.
 * `cephVersion`: The version information for launching the ceph daemons.
-    * `image`: The image used for running the ceph daemons. For example, `quay.io/ceph/ceph:v19.2.1`. For more details read the [container images section](#ceph-container-images).
+    * `image`: The image used for running the ceph daemons. For example, `quay.io/ceph/ceph:v19.2.0`. For more details read the [container images section](#ceph-container-images).
         For the latest ceph images, see the [Ceph DockerHub](https://hub.docker.com/r/ceph/ceph/tags/).
         To ensure a consistent version of the image is running across all nodes in the cluster, it is recommended to use a very specific image version.
         Tags also exist that would give the latest version, but they are only recommended for test environments. For example, the tag `v19` will be updated each time a new Squid build is released.
@@ -124,8 +124,8 @@ These are general purpose Ceph container with all necessary daemons and dependen
 | -------------------- | --------------------------------------------------------- |
 | vRELNUM              | Latest release in this series (e.g., **v19** = Squid)     |
 | vRELNUM.Y            | Latest stable release in this stable series (e.g., v19.2) |
-| vRELNUM.Y.Z          | A specific release (e.g., v19.2.1)                        |
-| vRELNUM.Y.Z-YYYYMMDD | A specific build (e.g., v19.2.1-20250202)                 |
+| vRELNUM.Y.Z          | A specific release (e.g., v19.2.0)                        |
+| vRELNUM.Y.Z-YYYYMMDD | A specific build (e.g., v19.2.0-20240927)                 |
 
 A specific will contain a specific release of Ceph as well as security fixes from the Operating System.
 
@@ -431,7 +431,7 @@ metadata:
   namespace: rook-ceph
 spec:
   cephVersion:
-    image: quay.io/ceph/ceph:v19.2.1
+    image: quay.io/ceph/ceph:v19.2.0
   dataDirHostPath: /var/lib/rook
   mon:
     count: 3
@@ -538,7 +538,7 @@ metadata:
   namespace: rook-ceph
 spec:
   cephVersion:
-    image: quay.io/ceph/ceph:v19.2.1
+    image: quay.io/ceph/ceph:v19.2.0
   dataDirHostPath: /var/lib/rook
   mon:
     count: 3
@@ -668,7 +668,7 @@ kubectl -n rook-ceph get CephCluster -o yaml
       deviceClasses:
       - name: hdd
     version:
-      image: quay.io/ceph/ceph:v19.2.1
+      image: quay.io/ceph/ceph:v19.2.0
       version: 16.2.6-0
     conditions:
     - lastHeartbeatTime: "2021-03-02T21:22:11Z"
diff --git a/Documentation/CRDs/Cluster/host-cluster.md b/Documentation/CRDs/Cluster/host-cluster.md
index a9cd511a2..9d2608863 100644
--- a/Documentation/CRDs/Cluster/host-cluster.md
+++ b/Documentation/CRDs/Cluster/host-cluster.md
@@ -22,7 +22,7 @@ metadata:
 spec:
   cephVersion:
     # see the "Cluster Settings" section below for more details on which image of ceph to run
-    image: quay.io/ceph/ceph:v19.2.1
+    image: quay.io/ceph/ceph:v19.2.0
   dataDirHostPath: /var/lib/rook
   mon:
     count: 3
@@ -49,7 +49,7 @@ metadata:
   namespace: rook-ceph
 spec:
   cephVersion:
-    image: quay.io/ceph/ceph:v19.2.1
+    image: quay.io/ceph/ceph:v19.2.0
   dataDirHostPath: /var/lib/rook
   mon:
     count: 3
@@ -101,7 +101,7 @@ metadata:
   namespace: rook-ceph
 spec:
   cephVersion:
-    image: quay.io/ceph/ceph:v19.2.1
+    image: quay.io/ceph/ceph:v19.2.0
   dataDirHostPath: /var/lib/rook
   mon:
     count: 3
diff --git a/Documentation/CRDs/Cluster/pvc-cluster.md b/Documentation/CRDs/Cluster/pvc-cluster.md
index 6485e1e8d..651bd17ec 100644
--- a/Documentation/CRDs/Cluster/pvc-cluster.md
+++ b/Documentation/CRDs/Cluster/pvc-cluster.md
@@ -18,7 +18,7 @@ metadata:
   namespace: rook-ceph
 spec:
   cephVersion:
-    image: quay.io/ceph/ceph:v19.2.1
+    image: quay.io/ceph/ceph:v19.2.0
   dataDirHostPath: /var/lib/rook
   mon:
     count: 3
@@ -72,7 +72,7 @@ spec:
           requests:
             storage: 10Gi
   cephVersion:
-    image: quay.io/ceph/ceph:v19.2.1
+    image: quay.io/ceph/ceph:v19.2.0
     allowUnsupported: false
   dashboard:
     enabled: true
@@ -128,7 +128,7 @@ metadata:
   namespace: rook-ceph
 spec:
   cephVersion:
-    image: quay.io/ceph/ceph:v19.2.1
+    image: quay.io/ceph/ceph:v19.2.0
   dataDirHostPath: /var/lib/rook
   mon:
     count: 3
diff --git a/Documentation/CRDs/Cluster/stretch-cluster.md b/Documentation/CRDs/Cluster/stretch-cluster.md
index bc45459cd..d35fa7cc7 100644
--- a/Documentation/CRDs/Cluster/stretch-cluster.md
+++ b/Documentation/CRDs/Cluster/stretch-cluster.md
@@ -34,7 +34,7 @@ spec:
       - name: b
       - name: c
   cephVersion:
-    image: quay.io/ceph/ceph:v19.2.1
+    image: quay.io/ceph/ceph:v19.2.0
     allowUnsupported: true
   # Either storageClassDeviceSets or the storage section can be specified for creating OSDs.
   # This example uses all devices for simplicity.
diff --git a/Documentation/Contributing/development-flow.md b/Documentation/Contributing/development-flow.md
index 30d519d99..60714b2ad 100644
--- a/Documentation/Contributing/development-flow.md
+++ b/Documentation/Contributing/development-flow.md
@@ -207,11 +207,6 @@ All pull requests must pass all continuous integration (CI) tests before they ca
 automatically run against every pull request. The results of these tests along with code review feedback determine whether
 your request will be merged.
 
-For developers who would like to locally test their changes
-as much as possible before pushing
-(a behavior that is not required but highly encouraged),
-the unit tests and various linters can easily be run locally.
-
 ## Unit Tests
 
 From the root of your local Rook repo execute the following to run all of the unit tests:
@@ -248,21 +243,6 @@ Common cases that may need tests:
 * an input is specified incorrectly, for each input
 * a resource the code relies on doesn't exist, for each dependency
 
-## Linting
-
-Run `make lint` to run all linters. This will require the following CLI tools to be installed on the system:
-
-* [yamllint](https://www.yamllint.com/)
-* [shellcheck](https://www.shellcheck.net/)
-* [pylint](https://www.pylint.org/)
-* [markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2)
-
-Many of those tools are available via `brew` for MacOS or as Linux distribution packages.
-Otherwise refer to the respective project websites for installation instructions.
-
-!!! tip
-    Run `make help` to see a list of all possible make targets
-
 ## Integration Tests
 
 Rook's upstream continuous integration (CI) tests will run integration tests against your changes
diff --git a/Documentation/Getting-Started/Prerequisites/authenticated-registry.md b/Documentation/Getting-Started/Prerequisites/authenticated-registry.md
index 424310797..c382db2b3 100644
--- a/Documentation/Getting-Started/Prerequisites/authenticated-registry.md
+++ b/Documentation/Getting-Started/Prerequisites/authenticated-registry.md
@@ -3,7 +3,7 @@ title: Authenticated Container Registries
 ---
 
 If you want to use an image from authenticated docker registry (e.g. for image cache/mirror), you'll need to
-add `imagePullSecrets` to all relevant service accounts. See the next section for the required service accounts.
+add an `imagePullSecret` to all relevant service accounts. See the next section for the required service accounts.
 
 The whole process is described in the [official kubernetes documentation](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#add-imagepullsecrets-to-a-service-account).
 
diff --git a/Documentation/Helm-Charts/operator-chart.md b/Documentation/Helm-Charts/operator-chart.md
index 4307fc083..2ceddfee6 100644
--- a/Documentation/Helm-Charts/operator-chart.md
+++ b/Documentation/Helm-Charts/operator-chart.md
@@ -67,9 +67,7 @@ The following table lists the configurable parameters of the rook-operator chart
 | `csi.csiAddons.enabled` | Enable CSIAddons | `false` |
 | `csi.csiAddons.repository` | CSIAddons sidecar image repository | `"quay.io/csiaddons/k8s-sidecar"` |
 | `csi.csiAddons.tag` | CSIAddons sidecar image tag | `"v0.11.0"` |
-| `csi.csiAddonsCephFSProvisionerPort` | CSI Addons server port for the Ceph FS provisioner | `9070` |
 | `csi.csiAddonsPort` | CSI Addons server port | `9070` |
-| `csi.csiAddonsRBDProvisionerPort` | CSI Addons server port for the RBD provisioner | `9070` |
 | `csi.csiCephFSPluginResource` | CEPH CSI CephFS plugin resource requirement list | see values.yaml |
 | `csi.csiCephFSPluginVolume` | The volume of the CephCSI CephFS plugin DaemonSet | `nil` |
 | `csi.csiCephFSPluginVolumeMount` | The volume mounts of the CephCSI CephFS plugin DaemonSet | `nil` |
@@ -159,7 +157,6 @@ The following table lists the configurable parameters of the rook-operator chart
 | `logLevel` | Global log level for the operator. Options: `ERROR`, `WARNING`, `INFO`, `DEBUG` | `"INFO"` |
 | `monitoring.enabled` | Enable monitoring. Requires Prometheus to be pre-installed. Enabling will also create RBAC rules to allow Operator to create ServiceMonitors | `false` |
 | `nodeSelector` | Kubernetes [`nodeSelector`](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) to add to the Deployment. | `{}` |
-| `obcAllowAdditionalConfigFields` | Many OBC additional config fields may be risky for administrators to allow users control over. The safe and default-allowed fields are 'maxObjects' and 'maxSize'. Other fields should be considered risky. To allow all additional configs, use this value:   "maxObjects,maxSize,bucketMaxObjects,bucketMaxSize,bucketPolicy,bucketLifecycle,bucketOwner" | "maxObjects,maxSize" |
 | `obcProvisionerNamePrefix` | Specify the prefix for the OBC provisioner in place of the cluster namespace | `ceph cluster namespace` |
 | `operatorPodLabels` | Custom pod labels for the operator | `{}` |
 | `priorityClassName` | Set the priority class for the rook operator deployment if desired | `nil` |
diff --git a/Documentation/Storage-Configuration/Object-Storage-RGW/ceph-object-bucket-claim.md b/Documentation/Storage-Configuration/Object-Storage-RGW/ceph-object-bucket-claim.md
index b19d38602..284b1dba3 100644
--- a/Documentation/Storage-Configuration/Object-Storage-RGW/ceph-object-bucket-claim.md
+++ b/Documentation/Storage-Configuration/Object-Storage-RGW/ceph-object-bucket-claim.md
@@ -91,25 +91,11 @@ If both `bucketName` and `generateBucketName` are blank or omitted then the stor
 
     * `maxObjects`: The maximum number of objects in the bucket as a quota on the user account automatically created for the bucket.
     * `maxSize`: The maximum size of the bucket as a quota on the user account automatically created for the bucket. Please note minimum recommended value is 4K.
-    * `bucketMaxObjects`: (disabled by default) The maximum number of objects in the bucket as an individual bucket quota. This is useful when the bucket is shared among multiple users.
-    * `bucketMaxSize`: (disabled by default) The maximum size of the bucket as an individual bucket quota.
-    * `bucketPolicy`: (disabled by default) A raw JSON format string that defines an AWS S3 format the bucket policy. If set, the policy string will override any existing policy set on the bucket and any default bucket policy that the bucket provisioner potentially would have automatically generated.
-    * `bucketLifecycle`: (disabled by default) A raw JSON format string that defines an AWS S3 format bucket lifecycle configuration. Note that the rules must be sorted by `ID` in order to be idempotent.
-    * `bucketOwner`: (disabled by default)  The name of a pre-existing ceph rgw user account that will own the bucket. A `CephObjectStoreUser` resource may be used to create an ceph rgw user account. If the bucket already exists and is owned by a different user, the bucket will be re-linked to the specified user.
-
-Several OBC `additionalConfig` fields are disabled by default. Default-disabled additional config
-fields may be risky for administrators to allow users control over, and they should be enabled only
-with caution.
-
-The default allowed fields are `maxObjects` and `maxSize`. These are designed to fit into the OBC
-framework's original design goals. Other fields can be allowed but exert control outside of OBC's
-original design goals and should be considered risky. At best, users may be able to break their own
-OBCs in unexpected ways. At worst, users may brick the whole S3 object store for all users
-(`bucketPolicy` in particular). Administrators should take care to enable features only when they
-are personally willing to take on the risks.
-
-OBC `additionalConfig` fields can be enabled and disabled using the the `rook-ceph-operator-config`
-configmap value `ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS`.
+    * `bucketMaxObjects`: The maximum number of objects in the bucket as an individual bucket quota. This is useful when the bucket is shared among multiple users.
+    * `bucketMaxSize`: The maximum size of the bucket as an individual bucket quota.
+    * `bucketPolicy`: A raw JSON format string that defines an AWS S3 format the bucket policy. If set, the policy string will override any existing policy set on the bucket and any default bucket policy that the bucket provisioner potentially would have automatically generated.
+    * `bucketLifecycle`: A raw JSON format string that defines an AWS S3 format bucket lifecycle configuration. Note that the rules must be sorted by `ID` in order to be idempotent.
+    * `bucketOwner`: The name of a pre-existing ceph rgw user account that will own the bucket. A `CephObjectStoreUser` resource may be used to create an ceph rgw user account. If the bucket already exists and is owned by a different user, the bucket will be re-linked to the specified user.
 
 ### OBC Custom Resource after Bucket Provisioning
 
diff --git a/Documentation/Upgrade/ceph-upgrade.md b/Documentation/Upgrade/ceph-upgrade.md
index efc1c77d4..6ec671f33 100644
--- a/Documentation/Upgrade/ceph-upgrade.md
+++ b/Documentation/Upgrade/ceph-upgrade.md
@@ -39,7 +39,7 @@ Official Ceph container images can be found on [Quay](https://quay.io/repository
 
 These images are tagged in a few ways:
 
-* The most explicit form of tags are full-ceph-version-and-build tags (e.g., `v19.2.1-20250202`).
+* The most explicit form of tags are full-ceph-version-and-build tags (e.g., `v19.2.0-20240927`).
     These tags are recommended for production clusters, as there is no possibility for the cluster to
     be heterogeneous with respect to the version of Ceph running in containers.
 * Ceph major version tags (e.g., `v19`) are useful for development and test clusters so that the
@@ -56,7 +56,7 @@ CephCluster CRD (`spec.cephVersion.image`).
 
 ```console
 ROOK_CLUSTER_NAMESPACE=rook-ceph
-NEW_CEPH_IMAGE='quay.io/ceph/ceph:v19.2.1-20250202'
+NEW_CEPH_IMAGE='quay.io/ceph/ceph:v19.2.0-20240927'
 kubectl -n $ROOK_CLUSTER_NAMESPACE patch CephCluster $ROOK_CLUSTER_NAMESPACE --type=merge -p "{\"spec\": {\"cephVersion\": {\"image\": \"$NEW_CEPH_IMAGE\"}}}"
 ```
 
@@ -68,7 +68,7 @@ employed by the new Rook operator release. Employing an outdated Ceph version wi
 in unexpected behaviour.
 
 ```console
-kubectl -n rook-ceph set image deploy/rook-ceph-tools rook-ceph-tools=quay.io/ceph/ceph:v19.2.1-20250202
+kubectl -n rook-ceph set image deploy/rook-ceph-tools rook-ceph-tools=quay.io/ceph/ceph:v19.2.0-20240927
 ```
 
 #### **3. Wait for the pod updates**
@@ -86,9 +86,9 @@ Confirm the upgrade is completed when the versions are all on the desired Ceph v
 kubectl -n $ROOK_CLUSTER_NAMESPACE get deployment -l rook_cluster=$ROOK_CLUSTER_NAMESPACE -o jsonpath='{range .items[*]}{"ceph-version="}{.metadata.labels.ceph-version}{"\n"}{end}' | sort | uniq
 This cluster is not yet finished:
     ceph-version=v18.2.4-0
-    ceph-version=v19.2.1-0
+    ceph-version=v19.2.0-0
 This cluster is finished:
-    ceph-version=v19.2.1-0
+    ceph-version=v19.2.0-0
 ```
 
 #### **4. Verify cluster health**
diff --git a/Documentation/Upgrade/node-maintenance.md b/Documentation/Upgrade/node-maintenance.md
deleted file mode 100644
index 5aa2d0646..000000000
--- a/Documentation/Upgrade/node-maintenance.md
+++ /dev/null
@@ -1,162 +0,0 @@
----
-title: Node Maintenance
----
-
-This guide outlines a comprehensive procedure for safely shutting down, maintaining, and restarting a nodes in the Rook Ceph cluster.
-
-Although updating nodes by failure domain (such as zones or racks) is preferred to minimize downtime, this guide describes the full shutdown process if downtime is acceptable.
-
-## Overview
-
-The basic maintenance procedure involves the following steps:
-
-1. Stop I/O: Scale down all applications to stop any I/O activity to Ceph.
-2. Set Ceph Flags: Optionally set the noout (and related) flags to prevent Ceph from marking OSDs as out.
-3. Shutdown Ceph Components: Scale down all Ceph deployments (with mons being the last) while ensuring the operator is scaled down first.
-4. Perform Maintenance: Conduct the necessary maintenance on your nodes.
-5. Restart Ceph Components: Scale up Ceph deployments (starting with mons) and then bring up the operator.
-6. Unset Ceph Flags: Remove any flags that were set before the shutdown.
-7. Restart Applications: Scale applications back up to resume operations.
-
-## Pre-Maintenance Steps
-
-Backup & Notifications: Always ensure you have a recent backup and inform stakeholders of the planned downtime.
-
-Update Strategy: Whenever possible, plan node updates by failure domain to avoid full cluster downtime.
-
-### Environment
-
-These instructions will work for as long the environment is parameterized correctly.
-Set the following environment variables, which will be used throughout this document.
-
-```console
-# Parameterize the environment
-export ROOK_OPERATOR_NAMESPACE=rook-ceph
-export ROOK_CLUSTER_NAMESPACE=rook-ceph
-```
-
-## Shutdown Procedure
-
-### **1. Scale Down Applications**
-
-Before any Ceph-specific actions, scale down all applications interacting with Ceph to ensure no new I/O is initiated.
-
-### **2. Set Ceph Flags**
-
-Setting the noout flag (and any additional flags) can help prevent Ceph from marking OSDs as out during the maintenance window.
-
-```console
-kubectl -n $ROOK_CLUSTER_NAMESPACE exec -it deploy/rook-ceph-tools -- bash
-ceph osd set noout
-# Optionally, set other flags as needed (e.g., norebalance, nodown)
-```
-
-!!! tip
-    The ordering of Ceph flags is not critical. You may set them in any order during shutdown and unset them in reverse order during startup.
-
-### **3. Scale Down the Operator**
-
-Scale down the rook-ceph-operator first to avoid it automatically restarting deployments you are scaling down.
-
-```console
-kubectl -n $ROOK_OPERATOR_NAMESPACE scale deployment rook-ceph-operator --replicas=0
-```
-
-### **4. Scale Down Ceph Deployments**
-
-Scale down the Ceph components in a controlled order. For example, you may choose the following order:
-
-```console
-RGW deployments > CSI CephFS plugin provisioners > CSI RBD plugin provisioners > OSD deployments > MON deployments > MGR deployments
-```
-
-A script to scale these down looks like:
-
-```console
-for _category in rook-ceph-rgw csi-cephfsplugin-provisioner csi-rbdplugin-provisioner rook-ceph-osd rook-ceph-mon rook-ceph-mgr rook-ceph-exporter rook-ceph-crashcollector; do
-    for _item in $(kubectl get deployment -n rook-ceph | awk '/^'"${_category}"'/{print $1}'); do
-        kubectl -n rook-ceph scale deployment ${_item} --replicas=0;
-        while [[ $(kubectl get deployment -n rook-ceph ${_item} -o jsonpath='{.status.readyReplicas}') != "" ]]; do
-            sleep 5;
-        done;
-    done;
-done
-```
-
-### **5. Node Maintenance**
-
-With all Ceph components safely scaled down, you can now perform your node maintenance tasks such as OS upgrades, hardware replacements, or other planned updates.
-
-## Startup Procedure
-
-### **1. Scale Up Ceph Components**
-
-Bring up the Ceph deployments in the following order to help ensure a healthy state before the operator takes over:
-
-Scale up MON deployments first:
-
-```console
-for _item in $(kubectl get deployment -n $ROOK_CLUSTER_NAMESPACE | awk '/^rook-ceph-mon/{print $1}'); do
-    kubectl -n $ROOK_CLUSTER_NAMESPACE scale deployment ${_item} --replicas=1;
-    while [[ $(kubectl get deployment -n $ROOK_CLUSTER_NAMESPACE ${_item} -o jsonpath='{.status.replicas}') != "1" ]]; do
-        sleep 5;
-    done;
-done
-```
-
-Then scale up OSD and MGR deployments:
-
-```console
-for _category in rook-ceph-mgr rook-ceph-osd; do
-    for _item in $(kubectl get deployment -n $ROOK_CLUSTER_NAMESPACE | awk '/^'"${_category}"'/{print $1}'); do
-        kubectl -n $ROOK_CLUSTER_NAMESPACE scale deployment ${_item} --replicas=1;
-        while [[ $(kubectl get deployment -n $ROOK_CLUSTER_NAMESPACE ${_item} -o jsonpath='{.status.replicas}') != "1" ]]; do
-            sleep 5;
-        done;
-    done;
-done
-```
-
-Finally, scale up other deployments:
-
-```console
-for _category in rook-ceph-exporter rook-ceph-crashcollector; do
-    for _item in $(kubectl get deployment -n $ROOK_CLUSTER_NAMESPACE | awk '/^'"${_category}"'/{print $1}'); do
-        kubectl -n $ROOK_CLUSTER_NAMESPACE scale deployment ${_item} --replicas=1;
-        while [[ $(kubectl get deployment -n $ROOK_CLUSTER_NAMESPACE ${_item} -o jsonpath='{.status.replicas}') != "1" ]]; do
-            sleep 5;
-        done;
-    done;
-done
-```
-
-### **2. Scale Up the Operator**
-
-Once the Ceph components are stable and healthy, scale the operator back up to allow it to reconcile any remaining changes.
-
-```console
-kubectl -n $ROOK_OPERATOR_NAMESPACE scale deployment rook-ceph-operator --replicas=1
-```
-
-!!! tip
-    Bringing the operator up after the core components ensures a smoother reconciliation process.
-
-### **3. Unset Ceph Flags**
-
-After the Ceph cluster is back online and healthy, unset any flags that were previously set.
-
-```console
-kubectl -n $ROOK_CLUSTER_NAMESPACE exec -it deploy/rook-ceph-tools -- bash
-ceph osd unset noout
-# Unset any additional flags as necessary
-```
-
-### **4. Scale Up Applications**
-
-Finally, scale your applications back to their original replica count to resume normal operations.
-
-### CSI Plugins Behavior
-
-Observation: Even when the csi-cephfsplugin-provisioner and csi-rbdplugin-provisioner deployments are scaled to 0 replicas, the corresponding CSI plugin pods (from DaemonSets) remain running.
-
-Explanation: Scaling down the csi-cephfsplugin-provisioner and csi-rbdplugin-provisioner deployments prevents new provisioning requests, but the CSI plugin pods persist because they are managed by DaemonSets. Since DaemonSets are stateless, they can be deleted if needed, and the Rook operator will automatically recreate them when restarted. However, deletion is not required to stop provisioning.
diff --git a/Makefile b/Makefile
index f5eb211e6..da2bbbd2a 100644
--- a/Makefile
+++ b/Makefile
@@ -157,20 +157,12 @@ fmt: ## Check formatting of go sources.
 	@$(MAKE) go.init
 	@$(MAKE) go.fmt
 
-.PHONY: markdownlint
-markdownlint: ## Check formatting of documentation sources
-	markdownlint-cli2 "Documentation/**/**.md" "#Documentation/Helm-Charts/**" --config .markdownlint-cli2.cjs
-
 .PHONY: yamllint
 yamllint:
 	yamllint -c .yamllint deploy/examples/ --no-warnings
 
-.PHONY: golangci-lint
-golangci-lint:
-	go run github.com/golangci/golangci-lint/cmd/golangci-lint@latest run
-
 .PHONY: lint
-lint: yamllint pylint shellcheck vet markdownlint golangci-lint ## Run various linters
+lint: yamllint pylint shellcheck vet ## Run various linters
 
 .PHONY: pylint
 pylint:
diff --git a/PendingReleaseNotes.md b/PendingReleaseNotes.md
index 252f70b28..f40151ba6 100644
--- a/PendingReleaseNotes.md
+++ b/PendingReleaseNotes.md
@@ -2,14 +2,5 @@
 
 ## Breaking Changes
 
-Object:
-
-- Some ObjectBucketClaim options were added in Rook v1.16 that allowed more control over buckets.
-    These controls allow users to self-serve their own S3 policies, which many administrators might
-    consider a risk, depending on their environment. Rook has taken steps to ensure potentially risky
-    configurations are disabled by default to ensure the safest off-the-shelf configurations.
-    Administrators who wish to allow users to use the full range of OBC configurations must use the
-    new `ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS` to enable users to set potentially risky options.
-    See https://github.com/rook/rook/pull/15376 for more information.
 
 ## Features
diff --git a/deploy/charts/rook-ceph-cluster/templates/ingress.yaml b/deploy/charts/rook-ceph-cluster/templates/ingress.yaml
index f915575f4..6d86c5f1d 100644
--- a/deploy/charts/rook-ceph-cluster/templates/ingress.yaml
+++ b/deploy/charts/rook-ceph-cluster/templates/ingress.yaml
@@ -10,7 +10,7 @@ metadata:
   {{- end }}
 spec:
   rules:
-    - host: {{ .Values.ingress.dashboard.host.name | quote }}
+    - host: {{ .Values.ingress.dashboard.host.name }}
       http:
         paths:
           - path: {{ .Values.ingress.dashboard.host.path | default "/" }}
diff --git a/deploy/charts/rook-ceph-cluster/values.yaml b/deploy/charts/rook-ceph-cluster/values.yaml
index b7d2e1d6d..71db21001 100644
--- a/deploy/charts/rook-ceph-cluster/values.yaml
+++ b/deploy/charts/rook-ceph-cluster/values.yaml
@@ -25,7 +25,7 @@ toolbox:
   # -- Enable Ceph debugging pod deployment. See [toolbox](../Troubleshooting/ceph-toolbox.md)
   enabled: false
   # -- Toolbox image, defaults to the image used by the Ceph cluster
-  image: #quay.io/ceph/ceph:v19.2.1
+  image: #quay.io/ceph/ceph:v19.2.0
   # -- Toolbox tolerations
   tolerations: []
   # -- Toolbox affinity
@@ -94,9 +94,9 @@ cephClusterSpec:
     # v18 is Reef, v19 is Squid
     # RECOMMENDATION: In production, use a specific version tag instead of the general v18 flag, which pulls the latest release and could result in different
     # versions running within the cluster. See tags available at https://hub.docker.com/r/ceph/ceph/tags/.
-    # If you want to be more precise, you can always use a timestamp tag such as quay.io/ceph/ceph:v19.2.1-20250202
+    # If you want to be more precise, you can always use a timestamp tag such as quay.io/ceph/ceph:v19.2.0-20240927
     # This tag might not contain a new Ceph version, just security fixes from the underlying operating system, which will reduce vulnerabilities
-    image: quay.io/ceph/ceph:v19.2.1
+    image: quay.io/ceph/ceph:v19.2.0
     # Whether to allow unsupported versions of Ceph. Currently Reef and Squid are supported.
     # Future versions such as Tentacle (v20) would require this to be set to `true`.
     # Do not set to true in production.
diff --git a/deploy/charts/rook-ceph/templates/configmap.yaml b/deploy/charts/rook-ceph/templates/configmap.yaml
index 03e79640f..1acc2c5e8 100644
--- a/deploy/charts/rook-ceph/templates/configmap.yaml
+++ b/deploy/charts/rook-ceph/templates/configmap.yaml
@@ -14,9 +14,6 @@ data:
 {{- end }}
 {{- if .Values.obcProvisionerNamePrefix }}
   ROOK_OBC_PROVISIONER_NAME_PREFIX: {{ .Values.obcProvisionerNamePrefix | quote }}
-{{- end }}
-{{- if .Values.obcAllowAdditionalConfigFields }}
-  ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS: {{ .Values.obcAllowAdditionalConfigFields | quote }}
 {{- end }}
   ROOK_CEPH_ALLOW_LOOP_DEVICES: {{ .Values.allowLoopDevices | quote }}
   ROOK_ENABLE_DISCOVERY_DAEMON: {{ .Values.enableDiscoveryDaemon | quote }}
@@ -208,12 +205,6 @@ data:
 {{- if .Values.csi.csiAddonsPort }}
   CSIADDONS_PORT: {{ .Values.csi.csiAddonsPort | quote }}
 {{- end }}
-{{- if .Values.csi.csiAddonsRBDProvisionerPort }}
-  CSIADDONS_RBD_PROVISIONER_PORT: {{ .Values.csi.csiAddonsRBDProvisionerPort | quote }}
-{{- end }}
-{{- if .Values.csi.csiAddonsCephFSProvisionerPort }}
-  CSIADDONS_CEPHFS_PROVISIONER_PORT: {{ .Values.csi.csiAddonsCephFSProvisionerPort | quote }}
-{{- end }}
 {{- if .Values.csi.forceCephFSKernelClient }}
   CSI_FORCE_CEPHFS_KERNEL_CLIENT: {{ .Values.csi.forceCephFSKernelClient | quote }}
 {{- end }}
diff --git a/deploy/charts/rook-ceph/values.yaml b/deploy/charts/rook-ceph/values.yaml
index 4ce764357..e8475643d 100644
--- a/deploy/charts/rook-ceph/values.yaml
+++ b/deploy/charts/rook-ceph/values.yaml
@@ -443,12 +443,6 @@ csi:
   # -- CSI Addons server port
   # @default -- `9070`
   csiAddonsPort:
-  # -- CSI Addons server port for the RBD provisioner
-  # @default -- `9070`
-  csiAddonsRBDProvisionerPort:
-  # -- CSI Addons server port for the Ceph FS provisioner
-  # @default -- `9070`
-  csiAddonsCephFSProvisionerPort:
 
   # -- Enable Ceph Kernel clients on kernel < 4.17. If your kernel does not support quotas for CephFS
   # you may want to disable this setting. However, this will cause an issue during upgrades
@@ -661,13 +655,6 @@ enableOBCWatchOperatorNamespace: true
 # @default -- `ceph cluster namespace`
 obcProvisionerNamePrefix:
 
-# -- Many OBC additional config fields may be risky for administrators to allow users control over.
-# The safe and default-allowed fields are 'maxObjects' and 'maxSize'.
-# Other fields should be considered risky. To allow all additional configs, use this value:
-#   "maxObjects,maxSize,bucketMaxObjects,bucketMaxSize,bucketPolicy,bucketLifecycle,bucketOwner"
-# @default -- "maxObjects,maxSize"
-obcAllowAdditionalConfigFields: "maxObjects,maxSize"
-
 monitoring:
   # -- Enable monitoring. Requires Prometheus to be pre-installed.
   # Enabling will also create RBAC rules to allow Operator to create ServiceMonitors
diff --git a/deploy/examples/cluster-external-management.yaml b/deploy/examples/cluster-external-management.yaml
index 290e58456..777e6419b 100644
--- a/deploy/examples/cluster-external-management.yaml
+++ b/deploy/examples/cluster-external-management.yaml
@@ -20,4 +20,4 @@ spec:
   dataDirHostPath: /var/lib/rook
   # providing an image is required, if you want to create other CRs (rgw, mds, nfs)
   cephVersion:
-    image: quay.io/ceph/ceph:v19.2.1 # Should match external cluster version
+    image: quay.io/ceph/ceph:v19.2.0 # Should match external cluster version
diff --git a/deploy/examples/cluster-on-local-pvc.yaml b/deploy/examples/cluster-on-local-pvc.yaml
index a8e2df3de..f38f1f21e 100644
--- a/deploy/examples/cluster-on-local-pvc.yaml
+++ b/deploy/examples/cluster-on-local-pvc.yaml
@@ -174,7 +174,7 @@ spec:
           requests:
             storage: 10Gi
   cephVersion:
-    image: quay.io/ceph/ceph:v19.2.1
+    image: quay.io/ceph/ceph:v19.2.0
     allowUnsupported: false
   skipUpgradeChecks: false
   continueUpgradeAfterChecksEvenIfNotHealthy: false
diff --git a/deploy/examples/cluster-on-pvc.yaml b/deploy/examples/cluster-on-pvc.yaml
index 593335195..522277ab4 100644
--- a/deploy/examples/cluster-on-pvc.yaml
+++ b/deploy/examples/cluster-on-pvc.yaml
@@ -34,7 +34,7 @@ spec:
           requests:
             storage: 10Gi
   cephVersion:
-    image: quay.io/ceph/ceph:v19.2.1
+    image: quay.io/ceph/ceph:v19.2.0
     allowUnsupported: false
   skipUpgradeChecks: false
   continueUpgradeAfterChecksEvenIfNotHealthy: false
diff --git a/deploy/examples/cluster-stretched-aws.yaml b/deploy/examples/cluster-stretched-aws.yaml
index eb742ea00..c2ac50725 100644
--- a/deploy/examples/cluster-stretched-aws.yaml
+++ b/deploy/examples/cluster-stretched-aws.yaml
@@ -45,7 +45,7 @@ spec:
   mgr:
     count: 2
   cephVersion:
-    image: quay.io/ceph/ceph:v19.2.1
+    image: quay.io/ceph/ceph:v19.2.0
     allowUnsupported: true
   skipUpgradeChecks: false
   continueUpgradeAfterChecksEvenIfNotHealthy: false
diff --git a/deploy/examples/cluster-stretched.yaml b/deploy/examples/cluster-stretched.yaml
index 677a8e53d..d45fbac1c 100644
--- a/deploy/examples/cluster-stretched.yaml
+++ b/deploy/examples/cluster-stretched.yaml
@@ -39,7 +39,7 @@ spec:
   mgr:
     count: 2
   cephVersion:
-    image: quay.io/ceph/ceph:v19.2.1
+    image: quay.io/ceph/ceph:v19.2.0
     allowUnsupported: true
   skipUpgradeChecks: false
   continueUpgradeAfterChecksEvenIfNotHealthy: false
diff --git a/deploy/examples/cluster.yaml b/deploy/examples/cluster.yaml
index 415805160..b82bfd8fb 100644
--- a/deploy/examples/cluster.yaml
+++ b/deploy/examples/cluster.yaml
@@ -19,9 +19,9 @@ spec:
     # v18 is Reef, v19 is Squid
     # RECOMMENDATION: In production, use a specific version tag instead of the general v19 flag, which pulls the latest release and could result in different
     # versions running within the cluster. See tags available at https://hub.docker.com/r/ceph/ceph/tags/.
-    # If you want to be more precise, you can always use a timestamp tag such as quay.io/ceph/ceph:v19.2.1-20250202
+    # If you want to be more precise, you can always use a timestamp tag such as quay.io/ceph/ceph:v19.2.0-20240927
     # This tag might not contain a new Ceph version, just security fixes from the underlying operating system, which will reduce vulnerabilities
-    image: quay.io/ceph/ceph:v19.2.1
+    image: quay.io/ceph/ceph:v19.2.0
     # Whether to allow unsupported versions of Ceph. Currently Reef and Squid are supported.
     # Future versions such as Tentacle (v20) would require this to be set to `true`.
     # Do not set to true in production.
diff --git a/deploy/examples/images.txt b/deploy/examples/images.txt
index 53cf473bf..a54eecc83 100644
--- a/deploy/examples/images.txt
+++ b/deploy/examples/images.txt
@@ -1,6 +1,6 @@
  docker.io/rook/ceph:master
  gcr.io/k8s-staging-sig-storage/objectstorage-sidecar:v20240513-v0.1.0-35-gefb3255
- quay.io/ceph/ceph:v19.2.1
+ quay.io/ceph/ceph:v19.2.0
  quay.io/ceph/cosi:v0.1.2
  quay.io/cephcsi/cephcsi:v3.13.0
  quay.io/csiaddons/k8s-sidecar:v0.11.0
diff --git a/deploy/examples/operator-openshift.yaml b/deploy/examples/operator-openshift.yaml
index 26eead857..cea5a6fab 100644
--- a/deploy/examples/operator-openshift.yaml
+++ b/deploy/examples/operator-openshift.yaml
@@ -541,12 +541,6 @@ data:
   # Custom prefix value for the OBC provisioner instead of ceph cluster namespace, do not set on existing cluster
   # ROOK_OBC_PROVISIONER_NAME_PREFIX: "custom-prefix"
 
-  # Many OBC additional config fields may be risky for administrators to allow users control over.
-  # The safe and default-allowed fields are 'maxObjects' and 'maxSize'.
-  # Other fields should be considered risky. To allow all additional configs, use this value:
-  #   "maxObjects,maxSize,bucketMaxObjects,bucketMaxSize,bucketPolicy,bucketLifecycle,bucketOwner"
-  # ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS: "maxObjects,maxSize" # default allowed configs
-
   # Whether to start the discovery daemon to watch for raw storage devices on nodes in the cluster.
   # This daemon does not need to run if you are only going to create your OSDs based on StorageClassDeviceSets with PVCs.
   ROOK_ENABLE_DISCOVERY_DAEMON: "false"
diff --git a/deploy/examples/operator.yaml b/deploy/examples/operator.yaml
index b1fcd62b3..b15b60e18 100644
--- a/deploy/examples/operator.yaml
+++ b/deploy/examples/operator.yaml
@@ -470,12 +470,7 @@ data:
   # CSI_CEPHFS_LIVENESS_METRICS_PORT: "9081"
   # Configure CSI RBD liveness metrics port
   # CSI_RBD_LIVENESS_METRICS_PORT: "9080"
-
-  # We can override the ports for each individual component that uses the CSIADDONS sidecar
-  # This is useful if we're running in hostNetwork, where ports may conflict on the same host
   # CSIADDONS_PORT: "9070"
-  # CSIADDONS_RBD_PROVISIONER_PORT: "9070"
-  # CSIADDONS_CEPHFS_PROVISIONER_PORT: "9070"
 
   # Set CephFS Kernel mount options to use https://docs.ceph.com/en/latest/man/8/mount.ceph/#options
   # Set to "ms_mode=secure" when connections.encrypted is enabled in CephCluster CR
@@ -496,12 +491,6 @@ data:
   # Custom prefix value for the OBC provisioner instead of ceph cluster namespace, do not set on existing cluster
   # ROOK_OBC_PROVISIONER_NAME_PREFIX: "custom-prefix"
 
-  # Many OBC additional config fields may be risky for administrators to allow users control over.
-  # The safe and default-allowed fields are 'maxObjects' and 'maxSize'.
-  # Other fields should be considered risky. To allow all additional configs, use this value:
-  #   "maxObjects,maxSize,bucketMaxObjects,bucketMaxSize,bucketPolicy,bucketLifecycle,bucketOwner"
-  # ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS: "maxObjects,maxSize" # default allowed configs
-
   # Whether to start the discovery daemon to watch for raw storage devices on nodes in the cluster.
   # This daemon does not need to run if you are only going to create your OSDs based on StorageClassDeviceSets with PVCs.
   ROOK_ENABLE_DISCOVERY_DAEMON: "false"
@@ -577,9 +566,6 @@ data:
 
   # RevisionHistoryLimit value for all deployments created by rook.
   # ROOK_REVISION_HISTORY_LIMIT: "3"
-
-  #  Custom label to identify node hostname. If not set `kubernetes.io/hostname` will be used
-  ROOK_CUSTOM_HOSTNAME_LABEL: ""
 ---
 # OLM: BEGIN OPERATOR DEPLOYMENT
 apiVersion: apps/v1
@@ -660,6 +646,10 @@ spec:
             - name: ROOK_UNREACHABLE_NODE_TOLERATION_SECONDS
               value: "5"
 
+            #  Custom label to identify node hostname. If not set `kubernetes.io/hostname` will be used
+            - name: ROOK_CUSTOM_HOSTNAME_LABEL
+              value: ""
+
             # The name of the node to pass with the downward API
             - name: NODE_NAME
               valueFrom:
diff --git a/design/ceph/ceph-cluster-cleanup.md b/design/ceph/ceph-cluster-cleanup.md
index c50b8d6d0..ba534bbcf 100644
--- a/design/ceph/ceph-cluster-cleanup.md
+++ b/design/ceph/ceph-cluster-cleanup.md
@@ -34,7 +34,7 @@ metadata:
   namespace: rook-ceph
 spec:
   cephVersion:
-    image: quay.io/ceph/ceph:v19.2.1
+    image: quay.io/ceph/ceph:v19.2.0
   dataDirHostPath: /var/lib/rook
   mon:
     count: 3
diff --git a/go.mod b/go.mod
index 9f0e2076b..bb6436432 100644
--- a/go.mod
+++ b/go.mod
@@ -20,23 +20,23 @@ require (
 	github.com/banzaicloud/k8s-objectmatcher v1.8.0
 	github.com/ceph/ceph-csi-operator/api v0.0.0-20241211130321-1c8ad98a7d6e
 	github.com/ceph/ceph-csi/api v0.0.0-20241216133622-88b7e0d6684f
-	github.com/ceph/go-ceph v0.32.0
+	github.com/ceph/go-ceph v0.31.0
 	github.com/coreos/pkg v0.0.0-20240122114842-bbd7aa9bf6fb
 	github.com/csi-addons/kubernetes-csi-addons v0.11.0
 	github.com/gemalto/kmip-go v0.0.10
 	github.com/go-ini/ini v1.67.0
-	github.com/google/go-cmp v0.7.0
+	github.com/google/go-cmp v0.6.0
 	github.com/google/uuid v1.6.0
-	github.com/hashicorp/vault/api v1.16.0
+	github.com/hashicorp/vault/api v1.15.0
 	github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.7.5
 	github.com/kube-object-storage/lib-bucket-provisioner v0.0.0-20221122204822-d1a8c34382f1
 	github.com/libopenstorage/secrets v0.0.0-20240416031220-a17cf7f72c6c
 	github.com/pkg/errors v0.9.1
-	github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.80.1
-	github.com/prometheus-operator/prometheus-operator/pkg/client v0.80.1
+	github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.80.0
+	github.com/prometheus-operator/prometheus-operator/pkg/client v0.80.0
 	github.com/rook/rook/pkg/apis v0.0.0-20241216163035-3170ac6a0c58
 	github.com/sethvargo/go-password v0.3.1
-	github.com/spf13/cobra v1.9.1
+	github.com/spf13/cobra v1.8.1
 	github.com/spf13/pflag v1.0.6
 	github.com/stretchr/testify v1.10.0
 	github.com/sykesm/zap-logfmt v0.0.4
@@ -68,9 +68,6 @@ require (
 	github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect
 	github.com/ansel1/merry v1.8.0 // indirect
 	github.com/ansel1/merry/v2 v2.2.1 // indirect
-	github.com/aws/aws-sdk-go-v2 v1.36.0 // indirect
-	github.com/aws/aws-sdk-go-v2/credentials v1.17.57 // indirect
-	github.com/aws/smithy-go v1.22.2 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/blang/semver/v4 v4.0.0 // indirect
 	github.com/cenkalti/backoff/v4 v4.3.0 // indirect
@@ -85,7 +82,7 @@ require (
 	github.com/fxamacker/cbor/v2 v2.7.0 // indirect
 	github.com/gemalto/flume v0.13.1 // indirect
 	github.com/go-errors/errors v1.5.1 // indirect
-	github.com/go-jose/go-jose/v4 v4.0.5 // indirect
+	github.com/go-jose/go-jose/v4 v4.0.4 // indirect
 	github.com/go-logr/logr v1.4.2 // indirect
 	github.com/go-logr/zapr v1.3.0 // indirect
 	github.com/go-openapi/jsonpointer v0.21.0 // indirect
diff --git a/go.sum b/go.sum
index f92a4e581..52bba797f 100644
--- a/go.sum
+++ b/go.sum
@@ -148,42 +148,6 @@ github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:l
 github.com/aws/aws-sdk-go v1.44.164/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
 github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk=
 github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
-github.com/aws/aws-sdk-go-v2 v1.36.0 h1:b1wM5CcE65Ujwn565qcwgtOTT1aT4ADOHHgglKjG7fk=
-github.com/aws/aws-sdk-go-v2 v1.36.0/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM=
-github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8=
-github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc=
-github.com/aws/aws-sdk-go-v2/config v1.29.4 h1:ObNqKsDYFGr2WxnoXKOhCvTlf3HhwtoGgc+KmZ4H5yg=
-github.com/aws/aws-sdk-go-v2/config v1.29.4/go.mod h1:j2/AF7j/qxVmsNIChw1tWfsVKOayJoGRDjg1Tgq7NPk=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.57 h1:kFQDsbdBAR3GZsB8xA+51ptEnq9TIj3tS4MuP5b+TcQ=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.57/go.mod h1:2kerxPUUbTagAr/kkaHiqvj/bcYHzi2qiJS/ZinllU0=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 h1:7lOW8NUwE9UZekS1DYoiPdVAqZ6A+LheHWb+mHbNOq8=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27/go.mod h1:w1BASFIPOPUae7AgaH4SbjNbfdkxuggLyGfNFTn8ITY=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 h1:lWm9ucLSRFiI4dQQafLrEOmEDGry3Swrz0BIRdiHJqQ=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31/go.mod h1:Huu6GG0YTfbPphQkDSo4dEGmQRTKb9k9G7RdtyQWxuI=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 h1:ACxDklUKKXb48+eg5ROZXi1vDgfMyfIA/WyvqHcHI0o=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31/go.mod h1:yadnfsDwqXeVaohbGc/RaD287PuyRw2wugkh5ZL2J6k=
-github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk=
-github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
-github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26 h1:GeNJsIFHB+WW5ap2Tec4K6dzcVTsRbsT1Lra46Hv9ME=
-github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26/go.mod h1:zfgMpwHDXX2WGoG84xG2H+ZlPTkJUU4YUvx2svLQYWo=
-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 h1:D4oz8/CzT9bAEYtVhSBmFj2dNOtaHOtMKc2vHBwYizA=
-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2/go.mod h1:Za3IHqTQ+yNcRHxu1OFucBh0ACZT4j4VQFF0BqpZcLY=
-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7 h1:tB4tNw83KcajNAzaIMhkhVI2Nt8fAZd5A5ro113FEMY=
-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7/go.mod h1:lvpyBGkZ3tZ9iSsUIcC2EWp+0ywa7aK3BLT+FwZi+mQ=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 h1:O+8vD2rGjfihBewr5bT+QUfYUHIxCVgG61LHoT59shM=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12/go.mod h1:usVdWJaosa66NMvmCrr08NcWDBRv4E6+YFG2pUdw1Lk=
-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 h1:Hi0KGbrnr57bEHWM0bJ1QcBzxLrL/k2DHvGYhb8+W1w=
-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7/go.mod h1:wKNgWgExdjjrm4qvfbTorkvocEstaoDl4WCvGfeCy9c=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.72.0 h1:SAfh4pNx5LuTafKKWR02Y+hL3A+3TX8cTKG1OIAJaBk=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.72.0/go.mod h1:r+xl5yzMk9083rMR+sJ5TYj9Tihvf/l1oxzZXDgGj2Q=
-github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 h1:c5WJ3iHz7rLIgArznb3JCSQT3uUMiz9DLZhIX+1G8ok=
-github.com/aws/aws-sdk-go-v2/service/sso v1.24.14/go.mod h1:+JJQTxB6N4niArC14YNtxcQtwEqzS3o9Z32n7q33Rfs=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 h1:f1L/JtUkVODD+k1+IiSJUUv8A++2qVr+Xvb3xWXETMU=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13/go.mod h1:tvqlFoja8/s0o+UruA1Nrezo/df0PzdunMDDurUfg6U=
-github.com/aws/aws-sdk-go-v2/service/sts v1.33.12 h1:fqg6c1KVrc3SYWma/egWue5rKI4G2+M4wMQN2JosNAA=
-github.com/aws/aws-sdk-go-v2/service/sts v1.33.12/go.mod h1:7Yn+p66q/jt38qMoVfNvjbm3D89mGBnkwDcijgtih8w=
-github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ=
-github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
 github.com/banzaicloud/k8s-objectmatcher v1.8.0 h1:Nugn25elKtPMTA2br+JgHNeSQ04sc05MDPmpJnd1N2A=
 github.com/banzaicloud/k8s-objectmatcher v1.8.0/go.mod h1:p2LSNAjlECf07fbhDyebTkPUIYnU05G+WfGgkTmgeMg=
 github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
@@ -207,8 +171,8 @@ github.com/ceph/ceph-csi-operator/api v0.0.0-20241211130321-1c8ad98a7d6e h1:Qoca
 github.com/ceph/ceph-csi-operator/api v0.0.0-20241211130321-1c8ad98a7d6e/go.mod h1:odEUoarG26wXBCC2l4O4nMWhAz6VTKr2FRkv9yELgi8=
 github.com/ceph/ceph-csi/api v0.0.0-20241216133622-88b7e0d6684f h1:ja5xvJlv3Sq5dlQVQMeM/dlCwcUXgZXg/kTPmMMQhJ8=
 github.com/ceph/ceph-csi/api v0.0.0-20241216133622-88b7e0d6684f/go.mod h1:NfzKbdhaXhWh1PJCExML9dAXHCqEDppf26ueLWYdxlk=
-github.com/ceph/go-ceph v0.32.0 h1:iXRUGdPmH7h9Vf/WA1Dg3Wo1tgL7gcUbylfpbxrlGLs=
-github.com/ceph/go-ceph v0.32.0/go.mod h1:42eoJzyLS3VREzqrg2ot44NtuluQZi55hFRSoLF36GQ=
+github.com/ceph/go-ceph v0.31.0 h1:3yqMVsRN89VyMhJ+qiF91mQHw4RBjbWVVERGX7bYxkk=
+github.com/ceph/go-ceph v0.31.0/go.mod h1:VnMYp08oE2o2of29p/oGkxnM8VNF/+LJyVQ5pZSj3Wg=
 github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
@@ -247,7 +211,7 @@ github.com/coreos/pkg v0.0.0-20240122114842-bbd7aa9bf6fb h1:GIzvVQ9UkUlOhSDlqmrQ
 github.com/coreos/pkg v0.0.0-20240122114842-bbd7aa9bf6fb/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
 github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
+github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
@@ -330,8 +294,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
 github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
 github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
 github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
-github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
-github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
+github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E=
+github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
@@ -477,8 +441,8 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
 github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
-github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -594,8 +558,8 @@ github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0m
 github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
 github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
 github.com/hashicorp/vault/api v1.10.0/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8=
-github.com/hashicorp/vault/api v1.16.0 h1:nbEYGJiAPGzT9U4oWgaaB0g+Rj8E59QuHKyA5LhwQN4=
-github.com/hashicorp/vault/api v1.16.0/go.mod h1:KhuUhzOD8lDSk29AtzNjgAu2kxRA9jL9NAbkFlqvkBA=
+github.com/hashicorp/vault/api v1.15.0 h1:O24FYQCWwhwKnF7CuSqP30S51rTV7vz1iACXE/pj5DA=
+github.com/hashicorp/vault/api v1.15.0/go.mod h1:+5YTO09JGn0u+b6ySD/LLVf8WkJCPLAL2Vkmrn2+CM8=
 github.com/hashicorp/vault/api/auth/approle v0.5.0/go.mod h1:CHOQIA1AZACfjTzHggmyfiOZ+xCSKNRFqe48FTCzH0k=
 github.com/hashicorp/vault/api/auth/approle v0.8.0 h1:FuVtWZ0xD6+wz1x0l5s0b4852RmVXQNEiKhVXt6lfQY=
 github.com/hashicorp/vault/api/auth/approle v0.8.0/go.mod h1:NV7O9r5JUtNdVnqVZeMHva81AIdpG0WoIQohNt1VCPM=
@@ -819,11 +783,11 @@ github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4
 github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
 github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.44.1/go.mod h1:3WYi4xqXxGGXWDdQIITnLNmuDzO5n6wYva9spVhR4fg=
 github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.46.0/go.mod h1:3WYi4xqXxGGXWDdQIITnLNmuDzO5n6wYva9spVhR4fg=
-github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.80.1 h1:DP+PUNVOc+Bkft8a4QunLzaZ0RspWuD3tBbcPHr2PeE=
-github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.80.1/go.mod h1:6x4x0t9BP35g4XcjkHE9EB3RxhyfxpdpmZKd/Qyk8+M=
+github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.80.0 h1:ckSycH7xHtpcvXsmEY/qEziRhDQKqKqbsHi9kX/BO7A=
+github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.80.0/go.mod h1:6x4x0t9BP35g4XcjkHE9EB3RxhyfxpdpmZKd/Qyk8+M=
 github.com/prometheus-operator/prometheus-operator/pkg/client v0.46.0/go.mod h1:k4BrWlVQQsvBiTcDnKEMgyh/euRxyxgrHdur/ZX/sdA=
-github.com/prometheus-operator/prometheus-operator/pkg/client v0.80.1 h1:7/TPlTy7tkMXC1KwS2WdUdU8J220DszlSQ1+KHjXwHc=
-github.com/prometheus-operator/prometheus-operator/pkg/client v0.80.1/go.mod h1:0lW4yp2XE7AKohpGB2UGdomjP08rbr4f8JwAN6sUDWU=
+github.com/prometheus-operator/prometheus-operator/pkg/client v0.80.0 h1:TvrTp81Ra7c3n+P4tArjx/st9x92q1BlxfjhqeP3aSA=
+github.com/prometheus-operator/prometheus-operator/pkg/client v0.80.0/go.mod h1:G2GGhVurNB5qQh7DZVSAtdN4Szvp6IxWhc1+n2rclw4=
 github.com/prometheus/client_golang v0.9.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
@@ -897,8 +861,8 @@ github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3
 github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
 github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
 github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
-github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
-github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
+github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
+github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
diff --git a/pkg/apis/go.mod b/pkg/apis/go.mod
index 93888c7e0..d8a915ccc 100644
--- a/pkg/apis/go.mod
+++ b/pkg/apis/go.mod
@@ -15,7 +15,7 @@ replace (
 )
 
 require (
-	github.com/hashicorp/vault/api v1.16.0
+	github.com/hashicorp/vault/api v1.15.0
 	github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.7.5
 	github.com/kube-object-storage/lib-bucket-provisioner v0.0.0-20221122204822-d1a8c34382f1
 	github.com/libopenstorage/secrets v0.0.0-20240416031220-a17cf7f72c6c
@@ -33,7 +33,7 @@ require (
 	github.com/emicklei/go-restful/v3 v3.12.1 // indirect
 	github.com/fsnotify/fsnotify v1.7.0 // indirect
 	github.com/fxamacker/cbor/v2 v2.7.0 // indirect
-	github.com/go-jose/go-jose/v4 v4.0.5 // indirect
+	github.com/go-jose/go-jose/v4 v4.0.4 // indirect
 	github.com/go-logr/logr v1.4.2 // indirect
 	github.com/go-openapi/jsonpointer v0.21.0 // indirect
 	github.com/go-openapi/jsonreference v0.21.0 // indirect
@@ -41,7 +41,7 @@ require (
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang/protobuf v1.5.4 // indirect
 	github.com/google/gnostic-models v0.6.9 // indirect
-	github.com/google/go-cmp v0.7.0 // indirect
+	github.com/google/go-cmp v0.6.0 // indirect
 	github.com/google/gofuzz v1.2.0 // indirect
 	github.com/google/uuid v1.6.0 // indirect
 	github.com/hashicorp/errwrap v1.1.0 // indirect
diff --git a/pkg/apis/go.sum b/pkg/apis/go.sum
index a9c792c87..4f039e0ce 100644
--- a/pkg/apis/go.sum
+++ b/pkg/apis/go.sum
@@ -219,8 +219,8 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
-github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
-github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
+github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E=
+github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
@@ -359,8 +359,8 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
 github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
-github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -468,8 +468,8 @@ github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0m
 github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
 github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
 github.com/hashicorp/vault/api v1.10.0/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8=
-github.com/hashicorp/vault/api v1.16.0 h1:nbEYGJiAPGzT9U4oWgaaB0g+Rj8E59QuHKyA5LhwQN4=
-github.com/hashicorp/vault/api v1.16.0/go.mod h1:KhuUhzOD8lDSk29AtzNjgAu2kxRA9jL9NAbkFlqvkBA=
+github.com/hashicorp/vault/api v1.15.0 h1:O24FYQCWwhwKnF7CuSqP30S51rTV7vz1iACXE/pj5DA=
+github.com/hashicorp/vault/api v1.15.0/go.mod h1:+5YTO09JGn0u+b6ySD/LLVf8WkJCPLAL2Vkmrn2+CM8=
 github.com/hashicorp/vault/api/auth/approle v0.5.0/go.mod h1:CHOQIA1AZACfjTzHggmyfiOZ+xCSKNRFqe48FTCzH0k=
 github.com/hashicorp/vault/api/auth/approle v0.8.0 h1:FuVtWZ0xD6+wz1x0l5s0b4852RmVXQNEiKhVXt6lfQY=
 github.com/hashicorp/vault/api/auth/approle v0.8.0/go.mod h1:NV7O9r5JUtNdVnqVZeMHva81AIdpG0WoIQohNt1VCPM=
diff --git a/pkg/daemon/ceph/client/upgrade.go b/pkg/daemon/ceph/client/upgrade.go
index e214320e1..8de07b5d1 100644
--- a/pkg/daemon/ceph/client/upgrade.go
+++ b/pkg/daemon/ceph/client/upgrade.go
@@ -344,6 +344,21 @@ func OSDUpdateShouldCheckOkToStop(context *clusterd.Context, clusterInfo *Cluste
 		return false
 	}
 
+	// aio means all in one
+	aio, err := allOSDsSameHost(context, clusterInfo)
+	if err != nil {
+		if errors.Is(err, errNoHostInCRUSH) {
+			logger.Warning("the CRUSH map has no 'host' entries so not performing ok-to-stop checks")
+			return false
+		}
+		logger.Warningf("failed to determine if all osds are running on the same host. will check if OSDs are ok-to-stop. if all OSDs are running on one host %s. %v", userIntervention, err)
+		return true
+	}
+	if aio {
+		logger.Warningf("all OSDs are running on the same host. not performing upgrade check. running in best-effort")
+		return false
+	}
+
 	return true
 }
 
diff --git a/pkg/operator/ceph/cluster/controller.go b/pkg/operator/ceph/cluster/controller.go
index f499cccce..11eecc632 100644
--- a/pkg/operator/ceph/cluster/controller.go
+++ b/pkg/operator/ceph/cluster/controller.go
@@ -187,7 +187,10 @@ func add(opManagerContext context.Context, mgr manager.Manager, r reconcile.Reco
 
 	// Watch for changes on the hotplug config map
 	// TODO: to improve, can we run this against the operator namespace only?
-	disableVal := k8sutil.GetOperatorSetting(disableHotplugEnv, "false")
+	disableVal, err := k8sutil.GetOperatorSetting(opManagerContext, context.Clientset, opcontroller.OperatorSettingConfigMapName, disableHotplugEnv, "false")
+	if err != nil {
+		logger.Errorf("failed to get %s setting %s. %v", disableHotplugEnv, opcontroller.OperatorSettingConfigMapName, err)
+	}
 	if disableVal != "true" {
 		logger.Info("enabling hotplug orchestration")
 		s := source.Kind[client.Object](
diff --git a/pkg/operator/ceph/cluster/mon/mon.go b/pkg/operator/ceph/cluster/mon/mon.go
index df8fc7ff3..b0d2d94e7 100644
--- a/pkg/operator/ceph/cluster/mon/mon.go
+++ b/pkg/operator/ceph/cluster/mon/mon.go
@@ -386,18 +386,6 @@ func (c *Cluster) ConfigureArbiter() error {
 		return errors.Wrapf(err, "failed to find two failure domains %q in the CRUSH map", failureDomain)
 	}
 
-	// Before entering stretch mode, we must create at least one pool based on the default stretch rule
-	// Wait for the .mgr pool to be created, which we expect is defined as a CephBlockPool
-	// We may be able to remove this code waiting for the pool once this is in the ceph release:
-	//  https://github.com/ceph/ceph/pull/61371
-	logger.Info("enabling stretch mode... waiting for the builtin .mgr pool to be created")
-	err = wait.PollUntilContextTimeout(c.ClusterInfo.Context, pollInterval, totalWaitTime, true, func(ctx context.Context) (bool, error) {
-		return c.builtinMgrPoolExists(), nil
-	})
-	if err != nil {
-		return errors.Wrapf(err, "failed to wait for .mgr pool to be created before setting the stretch tiebreaker")
-	}
-
 	// Set the mon tiebreaker
 	if err := cephclient.SetMonStretchTiebreaker(c.context, c.ClusterInfo, c.arbiterMon, failureDomain); err != nil {
 		return errors.Wrap(err, "failed to set mon tiebreaker")
@@ -406,14 +394,6 @@ func (c *Cluster) ConfigureArbiter() error {
 	return nil
 }
 
-func (c *Cluster) builtinMgrPoolExists() bool {
-	_, err := cephclient.GetPoolDetails(c.context, c.ClusterInfo, ".mgr")
-
-	// If the call fails, either the pool does not exist or else there is some Ceph error.
-	// Either way, we want to wait for confirmation of the pool existing.
-	return err == nil
-}
-
 func (c *Cluster) readyToConfigureArbiter(checkOSDPods bool) (bool, error) {
 	failureDomain := c.getFailureDomainName()
 
diff --git a/pkg/operator/ceph/cluster/osd/osd.go b/pkg/operator/ceph/cluster/osd/osd.go
index 188659da9..04d91ca8e 100644
--- a/pkg/operator/ceph/cluster/osd/osd.go
+++ b/pkg/operator/ceph/cluster/osd/osd.go
@@ -88,7 +88,6 @@ type Cluster struct {
 	deviceSets     []deviceSet
 	migrateOSD     *OSDInfo
 	deprecatedOSDs map[string][]int
-	nodeConfigmaps map[string]struct{}
 }
 
 // New creates an instance of the OSD manager
@@ -207,9 +206,6 @@ func (c *Cluster) Start() error {
 	if err := c.validateOSDSettings(); err != nil {
 		return err
 	}
-	if err := c.initializeNodeConfigmaps(); err != nil {
-		return err
-	}
 	logger.Infof("start running osds in namespace %q", namespace)
 
 	if !c.spec.Storage.UseAllNodes && len(c.spec.Storage.Nodes) == 0 && len(c.spec.Storage.StorageClassDeviceSets) == 0 {
@@ -1057,24 +1053,3 @@ func getOSDLocationFromArgs(args []string) (string, bool) {
 
 	return "", false
 }
-
-func (c *Cluster) initializeNodeConfigmaps() error {
-	// Find existing configmaps matching the label
-	label := "node.config.rook.io/osd"
-	options := metav1.ListOptions{LabelSelector: label}
-	configmaps, err := c.context.Clientset.CoreV1().ConfigMaps(c.clusterInfo.Namespace).List(c.clusterInfo.Context, options)
-	if err != nil {
-		return errors.Wrapf(err, "failed to list configmaps in namespace %q with label %q", c.clusterInfo.Namespace, label)
-	}
-
-	c.nodeConfigmaps = map[string]struct{}{}
-	prefix := k8sutil.ConfigOverrideName + "-"
-	for _, configmap := range configmaps.Items {
-		if strings.HasPrefix(configmap.Name, prefix) {
-			nodeName := strings.TrimPrefix(configmap.Name, prefix)
-			logger.Infof("found node %q configmap, will mount it for alternate ceph conf overrides for osds on this node", nodeName)
-			c.nodeConfigmaps[nodeName] = struct{}{}
-		}
-	}
-	return nil
-}
diff --git a/pkg/operator/ceph/cluster/osd/provision_spec.go b/pkg/operator/ceph/cluster/osd/provision_spec.go
index 81166ca95..01b70b62e 100644
--- a/pkg/operator/ceph/cluster/osd/provision_spec.go
+++ b/pkg/operator/ceph/cluster/osd/provision_spec.go
@@ -104,7 +104,6 @@ func (c *Cluster) provisionPodTemplateSpec(osdProps osdProperties, restart v1.Re
 	// ceph-volume is currently set up to use /etc/ceph/ceph.conf; this means no user config
 	// overrides will apply to ceph-volume, but this is unnecessary anyway
 	volumes := append(controller.PodVolumes(provisionConfig.DataPathMap, c.spec.DataDirHostPath, c.spec.DataDirHostPath, true), copyBinariesVolume)
-	volumes = c.updateCephConfigVolume(volumes, osdProps.crushHostname)
 
 	// create a volume on /dev so the pod can access devices on the host
 	devVolume := v1.Volume{Name: "devices", VolumeSource: v1.VolumeSource{HostPath: &v1.HostPathVolumeSource{Path: "/dev"}}}
diff --git a/pkg/operator/ceph/cluster/osd/spec.go b/pkg/operator/ceph/cluster/osd/spec.go
index 37c56fb9b..ac4aeaeb9 100644
--- a/pkg/operator/ceph/cluster/osd/spec.go
+++ b/pkg/operator/ceph/cluster/osd/spec.go
@@ -328,24 +328,6 @@ func deploymentName(osdID int) string {
 	return fmt.Sprintf(osdAppNameFmt, osdID)
 }
 
-func (c *Cluster) updateCephConfigVolume(volumes []v1.Volume, nodeName string) []v1.Volume {
-	if _, ok := c.nodeConfigmaps[nodeName]; !ok {
-		logger.Debugf("no configmap to override for node %q", nodeName)
-		return volumes
-	}
-
-	updatedVolumes := []v1.Volume{}
-	for _, volume := range volumes {
-		if volume.Name == k8sutil.ConfigOverrideName {
-			logger.Debugf("found configmap volume to override for node %q", nodeName)
-			name := fmt.Sprintf("%s-%s", k8sutil.ConfigOverrideName, nodeName)
-			volume.VolumeSource.Projected.Sources[0].ConfigMap.LocalObjectReference.Name = name
-		}
-		updatedVolumes = append(updatedVolumes, volume)
-	}
-	return updatedVolumes
-}
-
 func (c *Cluster) makeDeployment(osdProps osdProperties, osd *OSDInfo, provisionConfig *provisionConfig) (*apps.Deployment, error) {
 	deploymentName := deploymentName(osd.ID)
 	replicaCount := int32(1)
@@ -357,8 +339,6 @@ func (c *Cluster) makeDeployment(osdProps osdProperties, osd *OSDInfo, provision
 		dataDirHostPath = ""
 	}
 	volumes := controller.PodVolumes(provisionConfig.DataPathMap, dataDirHostPath, c.spec.DataDirHostPath, false)
-	volumes = c.updateCephConfigVolume(volumes, osdProps.crushHostname)
-
 	failureDomainValue := osdProps.crushHostname
 	doConfigInit := true     // initialize ceph.conf in init container?
 	doBinaryCopyInit := true // copy rook binary in an init container?
diff --git a/pkg/operator/ceph/cluster/watcher.go b/pkg/operator/ceph/cluster/watcher.go
index c5ee33184..57363bfa2 100644
--- a/pkg/operator/ceph/cluster/watcher.go
+++ b/pkg/operator/ceph/cluster/watcher.go
@@ -159,21 +159,27 @@ func (c *clientCluster) onK8sNode(ctx context.Context, object runtime.Object, op
 }
 
 func (c *clientCluster) handleNodeFailure(ctx context.Context, cluster *cephv1.CephCluster, node *corev1.Node, opNamespace string) error {
-	watchForNodeLoss := k8sutil.GetOperatorSetting("ROOK_WATCH_FOR_NODE_FAILURE", "true")
+	watchForNodeLoss, err := k8sutil.GetOperatorSetting(ctx, c.context.Clientset, opcontroller.OperatorSettingConfigMapName, "ROOK_WATCH_FOR_NODE_FAILURE", "true")
+	if err != nil {
+		return pkgerror.Wrapf(err, "failed to get configmap value `ROOK_WATCH_FOR_NODE_FAILURE`.")
+	}
 
 	if strings.ToLower(watchForNodeLoss) != "true" {
 		logger.Debugf("not watching for node failures since `ROOK_WATCH_FOR_NODE_FAILURE` is set to %q", watchForNodeLoss)
 		return nil
 	}
 
-	disabledCSI := k8sutil.GetOperatorSetting("ROOK_CSI_DISABLE_DRIVER", "false")
+	disabledCSI, err := k8sutil.GetOperatorSetting(ctx, c.context.Clientset, opcontroller.OperatorSettingConfigMapName, "ROOK_CSI_DISABLE_DRIVER", "false")
+	if err != nil {
+		return pkgerror.Wrapf(err, "failed to get configmap value `ROOK_CSI_DISABLE_DRIVER`.")
+	}
 
 	if strings.ToLower(disabledCSI) != "false" {
 		logger.Debugf("not watching for node failures since `ROOK_CSI_DISABLE_DRIVER` is set to %q, skip creating networkFence", disabledCSI)
 		return nil
 	}
 
-	_, err := c.context.ApiExtensionsClient.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, "networkfences.csiaddons.openshift.io", metav1.GetOptions{})
+	_, err = c.context.ApiExtensionsClient.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, "networkfences.csiaddons.openshift.io", metav1.GetOptions{})
 	if err != nil {
 		if errors.IsNotFound(err) {
 			logger.Debug("networkfences.csiaddons.openshift.io CRD not found, skip creating networkFence")
diff --git a/pkg/operator/ceph/controller.go b/pkg/operator/ceph/controller.go
index 5aa86f336..c674854a5 100644
--- a/pkg/operator/ceph/controller.go
+++ b/pkg/operator/ceph/controller.go
@@ -33,6 +33,7 @@ import (
 	"github.com/rook/rook/pkg/operator/k8sutil"
 	"github.com/rook/rook/pkg/util"
 	v1 "k8s.io/api/core/v1"
+	kerrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
@@ -102,43 +103,51 @@ func (r *ReconcileConfig) Reconcile(context context.Context, request reconcile.R
 
 func (r *ReconcileConfig) reconcile(request reconcile.Request) (reconcile.Result, error) {
 	// Fetch the operator's configmap
+	opConfig := &v1.ConfigMap{}
 	logger.Debugf("reconciling %s", request.NamespacedName)
-	if request.Name == opcontroller.OperatorSettingConfigMapName {
-		if err := k8sutil.ApplyOperatorSettingsConfigmap(r.opManagerContext, r.context.Clientset); err != nil {
-			return opcontroller.ImmediateRetryResult, errors.Wrap(err, "failed to get operator setting configmap")
+	err := r.client.Get(r.opManagerContext, request.NamespacedName, opConfig)
+	if err != nil {
+		if kerrors.IsNotFound(err) {
+			logger.Debug("operator's configmap resource not found. will use default value or env var.")
+		} else {
+			// Error reading the object - requeue the request.
+			return opcontroller.ImmediateRetryResult, errors.Wrap(err, "failed to get operator's configmap")
 		}
+	} else {
+		// Populate the operator's config
+		r.config.Parameters = opConfig.Data
 	}
+
 	// Reconcile Ceph CLI timeout, since the clusterd context is passed to by pointer to all CRD
 	// controllers they will receive the update
-	opcontroller.SetCephCommandsTimeout()
+	opcontroller.SetCephCommandsTimeout(r.config.Parameters)
 
 	// Reconcile Operator's logging level
-	reconcileOperatorLogLevel()
+	reconcileOperatorLogLevel(opConfig.Data)
 
 	// Reconcile discovery daemon
-	err := r.reconcileDiscoveryDaemon()
+	err = r.reconcileDiscoveryDaemon(opConfig.Data)
 	if err != nil {
 		return opcontroller.ImmediateRetryResult, err
 	}
 
-	opcontroller.SetAllowLoopDevices()
-	opcontroller.SetEnforceHostNetwork()
-	opcontroller.SetRevisionHistoryLimit()
-	opcontroller.SetObcAllowAdditionalConfigFields()
+	opcontroller.SetAllowLoopDevices(r.config.Parameters)
+	opcontroller.SetEnforceHostNetwork(r.config.Parameters)
+	opcontroller.SetRevisionHistoryLimit(r.config.Parameters)
 
 	logger.Infof("%s done reconciling", controllerName)
 	return reconcile.Result{}, nil
 }
 
-func reconcileOperatorLogLevel() {
-	rookLogLevel := k8sutil.GetOperatorSetting("ROOK_LOG_LEVEL", util.DefaultLogLevel.String())
+func reconcileOperatorLogLevel(data map[string]string) {
+	rookLogLevel := k8sutil.GetValue(data, "ROOK_LOG_LEVEL", util.DefaultLogLevel.String())
 	util.SetGlobalLogLevel(rookLogLevel, logger)
 }
 
-func (r *ReconcileConfig) reconcileDiscoveryDaemon() error {
+func (r *ReconcileConfig) reconcileDiscoveryDaemon(data map[string]string) error {
 	rookDiscover := discover.New(r.context.Clientset)
-	if opcontroller.DiscoveryDaemonEnabled() {
-		if err := rookDiscover.Start(r.opManagerContext, r.config.OperatorNamespace, r.config.Image, r.config.ServiceAccount, true); err != nil {
+	if opcontroller.DiscoveryDaemonEnabled(r.config.Parameters) {
+		if err := rookDiscover.Start(r.opManagerContext, r.config.OperatorNamespace, r.config.Image, r.config.ServiceAccount, data, true); err != nil {
 			return errors.Wrap(err, "failed to start device discovery daemonset")
 		}
 	} else {
diff --git a/pkg/operator/ceph/controller/controller_utils.go b/pkg/operator/ceph/controller/controller_utils.go
index 01ca10e56..acdf7920b 100644
--- a/pkg/operator/ceph/controller/controller_utils.go
+++ b/pkg/operator/ceph/controller/controller_utils.go
@@ -20,7 +20,6 @@ import (
 	"context"
 	"fmt"
 	"reflect"
-	"slices"
 	"strconv"
 	"strings"
 	"time"
@@ -40,6 +39,7 @@ type OperatorConfig struct {
 	Image             string
 	ServiceAccount    string
 	NamespaceToWatch  string
+	Parameters        map[string]string
 }
 
 // ClusterHealth is passed to the various monitoring go routines to stop them when the context is cancelled
@@ -54,9 +54,6 @@ const (
 	enforceHostNetworkSettingName  string = "ROOK_ENFORCE_HOST_NETWORK"
 	enforceHostNetworkDefaultValue string = "false"
 
-	obcAllowAdditionalConfigFieldsSettingName  string = "ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS"
-	obcAllowAdditionalConfigFieldsDefaultValue string = "maxObjects,maxSize"
-
 	revisionHistoryLimitSettingName string = "ROOK_REVISION_HISTORY_LIMIT"
 
 	// UninitializedCephConfigError refers to the error message printed by the Ceph CLI when there is no ceph configuration file
@@ -93,18 +90,15 @@ var (
 	// loopDevicesAllowed indicates whether loop devices are allowed to be used
 	loopDevicesAllowed          = false
 	revisionHistoryLimit *int32 = nil
-
-	// allowed OBC additional config fields
-	obcAllowAdditionalConfigFields = strings.Split(obcAllowAdditionalConfigFieldsDefaultValue, ",")
 )
 
-func DiscoveryDaemonEnabled() bool {
-	return k8sutil.GetOperatorSetting("ROOK_ENABLE_DISCOVERY_DAEMON", "false") == "true"
+func DiscoveryDaemonEnabled(data map[string]string) bool {
+	return k8sutil.GetValue(data, "ROOK_ENABLE_DISCOVERY_DAEMON", "false") == "true"
 }
 
 // SetCephCommandsTimeout sets the timeout value of Ceph commands which are executed from Rook
-func SetCephCommandsTimeout() {
-	strTimeoutSeconds := k8sutil.GetOperatorSetting("ROOK_CEPH_COMMANDS_TIMEOUT_SECONDS", "15")
+func SetCephCommandsTimeout(data map[string]string) {
+	strTimeoutSeconds := k8sutil.GetValue(data, "ROOK_CEPH_COMMANDS_TIMEOUT_SECONDS", "15")
 	timeoutSeconds, err := strconv.Atoi(strTimeoutSeconds)
 	if err != nil || timeoutSeconds < 1 {
 		logger.Warningf("ROOK_CEPH_COMMANDS_TIMEOUT is %q but it should be >= 1, set the default value 15", strTimeoutSeconds)
@@ -113,8 +107,8 @@ func SetCephCommandsTimeout() {
 	exec.CephCommandsTimeout = time.Duration(timeoutSeconds) * time.Second
 }
 
-func SetAllowLoopDevices() {
-	strLoopDevicesAllowed := k8sutil.GetOperatorSetting("ROOK_CEPH_ALLOW_LOOP_DEVICES", "false")
+func SetAllowLoopDevices(data map[string]string) {
+	strLoopDevicesAllowed := k8sutil.GetValue(data, "ROOK_CEPH_ALLOW_LOOP_DEVICES", "false")
 	var err error
 	loopDevicesAllowed, err = strconv.ParseBool(strLoopDevicesAllowed)
 	if err != nil {
@@ -127,8 +121,8 @@ func LoopDevicesAllowed() bool {
 	return loopDevicesAllowed
 }
 
-func SetEnforceHostNetwork() {
-	strval := k8sutil.GetOperatorSetting(enforceHostNetworkSettingName, enforceHostNetworkDefaultValue)
+func SetEnforceHostNetwork(data map[string]string) {
+	strval := k8sutil.GetValue(data, enforceHostNetworkSettingName, enforceHostNetworkDefaultValue)
 	val, err := strconv.ParseBool(strval)
 	if err != nil {
 		logger.Warningf("failed to parse value %q for %q. assuming false value", strval, enforceHostNetworkSettingName)
@@ -142,8 +136,8 @@ func EnforceHostNetwork() bool {
 	return cephv1.EnforceHostNetwork()
 }
 
-func SetRevisionHistoryLimit() {
-	strval := k8sutil.GetOperatorSetting(revisionHistoryLimitSettingName, "")
+func SetRevisionHistoryLimit(data map[string]string) {
+	strval := k8sutil.GetValue(data, revisionHistoryLimitSettingName, "")
 	var limit int32
 	if strval == "" {
 		logger.Debugf("not parsing empty string to int for %q. assuming default value.", revisionHistoryLimitSettingName)
@@ -165,15 +159,6 @@ func RevisionHistoryLimit() *int32 {
 	return revisionHistoryLimit
 }
 
-func SetObcAllowAdditionalConfigFields() {
-	strval := k8sutil.GetOperatorSetting(obcAllowAdditionalConfigFieldsSettingName, obcAllowAdditionalConfigFieldsDefaultValue)
-	obcAllowAdditionalConfigFields = strings.Split(strval, ",")
-}
-
-func ObcAdditionalConfigKeyIsAllowed(configField string) bool {
-	return slices.Contains(obcAllowAdditionalConfigFields, configField)
-}
-
 // canIgnoreHealthErrStatusInReconcile determines whether a status of HEALTH_ERR in the CephCluster can be ignored safely.
 func canIgnoreHealthErrStatusInReconcile(cephCluster cephv1.CephCluster, controllerName string) bool {
 	// Get a list of all the keys causing the HEALTH_ERR status.
@@ -184,30 +169,13 @@ func canIgnoreHealthErrStatusInReconcile(cephCluster cephv1.CephCluster, control
 		}
 	}
 
-	// If there are no errors, the caller actually expects false to be returned so the absence
-	// of an error doesn't cause the health status to be ignored. In production, if there are no
-	// errors, we would anyway expect the health status to be ok or warning. False in this case
-	// will cover if the health status is blank.
-	if len(healthErrKeys) == 0 {
-		return false
-	}
-
-	allowedErrStatus := map[string]struct{}{
-		"MDS_ALL_DOWN":     {},
-		"MGR_MODULE_ERROR": {},
-	}
-	allCanBeIgnored := true
-	for _, healthErrKey := range healthErrKeys {
-		if _, ok := allowedErrStatus[healthErrKey]; !ok {
-			allCanBeIgnored = false
-			break
-		}
-	}
-	if allCanBeIgnored {
-		logger.Debugf("%q: ignoring ceph error status (full status is %+v)", controllerName, cephCluster.Status.CephStatus)
-		return true
+	// If there is only one cause for HEALTH_ERR and it's on the allowed list of errors, ignore it.
+	var allowedErrStatus = []string{"MDS_ALL_DOWN"}
+	var ignoreHealthErr = len(healthErrKeys) == 1 && contains(allowedErrStatus, healthErrKeys[0])
+	if ignoreHealthErr {
+		logger.Debugf("%q: ignoring ceph status %q because only cause is %q (full status is %+v)", controllerName, cephCluster.Status.CephStatus.Health, healthErrKeys[0], cephCluster.Status.CephStatus)
 	}
-	return false
+	return ignoreHealthErr
 }
 
 // IsReadyToReconcile determines if a controller is ready to reconcile or not
diff --git a/pkg/operator/ceph/cr_manager.go b/pkg/operator/ceph/cr_manager.go
index 21d9e4860..b3398d681 100644
--- a/pkg/operator/ceph/cr_manager.go
+++ b/pkg/operator/ceph/cr_manager.go
@@ -143,7 +143,11 @@ func (o *Operator) startCRDManager(context context.Context, mgrErrorCh chan erro
 		}
 	}
 
-	metricsBindAddress := k8sutil.GetOperatorSetting("ROOK_OPERATOR_METRICS_BIND_ADDRESS", "0")
+	metricsBindAddress, err := k8sutil.GetOperatorSetting(context, o.context.Clientset, opcontroller.OperatorSettingConfigMapName, "ROOK_OPERATOR_METRICS_BIND_ADDRESS", "0")
+	if err != nil {
+		mgrErrorCh <- errors.Wrap(err, "failed to get configmap value `ROOK_OPERATOR_METRICS_BIND_ADDRESS`.")
+		return
+	}
 	skipNameValidation := true
 	// Set up a manager
 	mgrOpts := manager.Options{
diff --git a/pkg/operator/ceph/csi/controller.go b/pkg/operator/ceph/csi/controller.go
index 809b2da4d..b9c6c5034 100644
--- a/pkg/operator/ceph/csi/controller.go
+++ b/pkg/operator/ceph/csi/controller.go
@@ -42,6 +42,7 @@ import (
 	kerrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/types"
 )
 
 const (
@@ -157,17 +158,32 @@ func (r *ReconcileCSI) reconcile(request reconcile.Request) (reconcile.Result, e
 		return opcontroller.ImmediateRetryResult, errors.Wrap(err, "failed creating csi config map")
 	}
 
-	if err := k8sutil.ApplyOperatorSettingsConfigmap(r.opManagerContext, r.context.Clientset); err != nil {
-		return opcontroller.ImmediateRetryResult, errors.Wrap(err, "failed to apply operator settings configmap")
+	// Fetch the operator's configmap. We force the NamespaceName to the operator since the request
+	// could be a CephCluster. If so the NamespaceName will be the one from the cluster and thus the
+	// CM won't be found
+	opNamespaceName := types.NamespacedName{Name: opcontroller.OperatorSettingConfigMapName, Namespace: r.opConfig.OperatorNamespace}
+	opConfig := &v1.ConfigMap{}
+	err = r.client.Get(r.opManagerContext, opNamespaceName, opConfig)
+	if err != nil {
+		if kerrors.IsNotFound(err) {
+			logger.Debug("operator's configmap resource not found. will use default value or env var.")
+			r.opConfig.Parameters = make(map[string]string)
+		} else {
+			// Error reading the object - requeue the request.
+			return opcontroller.ImmediateRetryResult, errors.Wrap(err, "failed to get operator's configmap")
+		}
+	} else {
+		// Populate the operator's config
+		r.opConfig.Parameters = opConfig.Data
 	}
 
-	enableCSIOperator, err = strconv.ParseBool(k8sutil.GetOperatorSetting("ROOK_USE_CSI_OPERATOR", "false"))
+	enableCSIOperator, err = strconv.ParseBool(k8sutil.GetValue(r.opConfig.Parameters, "ROOK_USE_CSI_OPERATOR", "false"))
 	if err != nil {
 		return reconcileResult, errors.Wrap(err, "unable to parse value for 'ROOK_USE_CSI_OPERATOR'")
 	}
 
 	// do not recocnile if csi driver is disabled
-	disableCSI, err := strconv.ParseBool(k8sutil.GetOperatorSetting("ROOK_CSI_DISABLE_DRIVER", "false"))
+	disableCSI, err := strconv.ParseBool(k8sutil.GetValue(r.opConfig.Parameters, "ROOK_CSI_DISABLE_DRIVER", "false"))
 	if err != nil {
 		return reconcile.Result{}, errors.Wrap(err, "unable to parse value for 'ROOK_CSI_DISABLE_DRIVER")
 	} else if disableCSI {
diff --git a/pkg/operator/ceph/csi/csi.go b/pkg/operator/ceph/csi/csi.go
index 6919eaf54..9769a519f 100644
--- a/pkg/operator/ceph/csi/csi.go
+++ b/pkg/operator/ceph/csi/csi.go
@@ -56,31 +56,31 @@ func (r *ReconcileCSI) validateAndConfigureDrivers(ownerInfo *k8sutil.OwnerInfo)
 func (r *ReconcileCSI) setParams() error {
 	var err error
 
-	if EnableRBD, err = strconv.ParseBool(k8sutil.GetOperatorSetting("ROOK_CSI_ENABLE_RBD", "true")); err != nil {
+	if EnableRBD, err = strconv.ParseBool(k8sutil.GetValue(r.opConfig.Parameters, "ROOK_CSI_ENABLE_RBD", "true")); err != nil {
 		return errors.Wrap(err, "unable to parse value for 'ROOK_CSI_ENABLE_RBD'")
 	}
 
-	if EnableCephFS, err = strconv.ParseBool(k8sutil.GetOperatorSetting("ROOK_CSI_ENABLE_CEPHFS", "true")); err != nil {
+	if EnableCephFS, err = strconv.ParseBool(k8sutil.GetValue(r.opConfig.Parameters, "ROOK_CSI_ENABLE_CEPHFS", "true")); err != nil {
 		return errors.Wrap(err, "unable to parse value for 'ROOK_CSI_ENABLE_CEPHFS'")
 	}
 
-	if EnableNFS, err = strconv.ParseBool(k8sutil.GetOperatorSetting("ROOK_CSI_ENABLE_NFS", "false")); err != nil {
+	if EnableNFS, err = strconv.ParseBool(k8sutil.GetValue(r.opConfig.Parameters, "ROOK_CSI_ENABLE_NFS", "false")); err != nil {
 		return errors.Wrap(err, "unable to parse value for 'ROOK_CSI_ENABLE_NFS'")
 	}
 
-	if CSIParam.EnableCSIHostNetwork, err = strconv.ParseBool(k8sutil.GetOperatorSetting("CSI_ENABLE_HOST_NETWORK", "true")); err != nil {
+	if CSIParam.EnableCSIHostNetwork, err = strconv.ParseBool(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_HOST_NETWORK", "true")); err != nil {
 		return errors.Wrap(err, "failed to parse value for 'CSI_ENABLE_HOST_NETWORK'")
 	}
 
 	// If not set or set to anything but "false", the kernel client will be enabled
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_FORCE_CEPHFS_KERNEL_CLIENT", "true"), "false") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_FORCE_CEPHFS_KERNEL_CLIENT", "true"), "false") {
 		CSIParam.ForceCephFSKernelClient = "false"
 	} else {
 		CSIParam.ForceCephFSKernelClient = "true"
 	}
 
 	// parse RPC timeout
-	timeout := k8sutil.GetOperatorSetting(grpcTimeout, "150")
+	timeout := k8sutil.GetValue(r.opConfig.Parameters, grpcTimeout, "150")
 	timeoutSeconds, err := strconv.Atoi(timeout)
 	if err != nil {
 		logger.Errorf("failed to parse %q. Defaulting to %d. %v", grpcTimeout, defaultGRPCTimeout, err)
@@ -93,32 +93,20 @@ func (r *ReconcileCSI) setParams() error {
 	CSIParam.GRPCTimeout = time.Duration(timeoutSeconds) * time.Second
 
 	// parse Liveness port
-	CSIParam.CephFSLivenessMetricsPort, err = getPortFromConfig("CSI_CEPHFS_LIVENESS_METRICS_PORT", DefaultCephFSLivenessMerticsPort)
+	CSIParam.CephFSLivenessMetricsPort, err = getPortFromConfig(r.opConfig.Parameters, "CSI_CEPHFS_LIVENESS_METRICS_PORT", DefaultCephFSLivenessMerticsPort)
 	if err != nil {
 		return errors.Wrap(err, "error getting CSI CephFS liveness metrics port.")
 	}
-
-	CSIParam.CSIAddonsPort, err = getPortFromConfig("CSIADDONS_PORT", DefaultCSIAddonsPort)
+	CSIParam.CSIAddonsPort, err = getPortFromConfig(r.opConfig.Parameters, "CSIADDONS_PORT", DefaultCSIAddonsPort)
 	if err != nil {
 		return errors.Wrap(err, "failed to get CSI Addons port")
 	}
-
-	CSIParam.CSIAddonsRBDProvisionerPort, err = getPortFromConfig("CSIADDONS_RBD_PROVISIONER_PORT", DefaultCSIAddonsRBDProvisionerPort)
-	if err != nil {
-		return errors.Wrap(err, "failed to get CSI Addons port for RBD provisioner")
-	}
-
-	CSIParam.CSIAddonsCephFSProvisionerPort, err = getPortFromConfig("CSIADDONS_CEPHFS_PROVISIONER_PORT", DefaultCSIAddonsCephFSProvisionerPort)
-	if err != nil {
-		return errors.Wrap(err, "failed to get CSI Addons port for Ceph FS provisioner")
-	}
-
-	CSIParam.RBDLivenessMetricsPort, err = getPortFromConfig("CSI_RBD_LIVENESS_METRICS_PORT", DefaultRBDLivenessMerticsPort)
+	CSIParam.RBDLivenessMetricsPort, err = getPortFromConfig(r.opConfig.Parameters, "CSI_RBD_LIVENESS_METRICS_PORT", DefaultRBDLivenessMerticsPort)
 	if err != nil {
 		return errors.Wrap(err, "error getting CSI RBD liveness metrics port.")
 	}
 
-	CSIParam.EnableLiveness, err = strconv.ParseBool(k8sutil.GetOperatorSetting("CSI_ENABLE_LIVENESS", "false"))
+	CSIParam.EnableLiveness, err = strconv.ParseBool(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_LIVENESS", "false"))
 	if err != nil {
 		return errors.Wrap(err, "failed to parse value for 'CSI_ENABLE_LIVENESS'")
 	}
@@ -126,31 +114,31 @@ func (r *ReconcileCSI) setParams() error {
 	CSIParam.Privileged = controller.HostPathRequiresPrivileged()
 
 	// default value `system-node-critical` is the highest available priority
-	CSIParam.PluginPriorityClassName = k8sutil.GetOperatorSetting("CSI_PLUGIN_PRIORITY_CLASSNAME", "")
+	CSIParam.PluginPriorityClassName = k8sutil.GetValue(r.opConfig.Parameters, "CSI_PLUGIN_PRIORITY_CLASSNAME", "")
 
 	// default value `system-cluster-critical` is applied for some
 	// critical pods in cluster but less priority than plugin pods
-	CSIParam.ProvisionerPriorityClassName = k8sutil.GetOperatorSetting("CSI_PROVISIONER_PRIORITY_CLASSNAME", "")
+	CSIParam.ProvisionerPriorityClassName = k8sutil.GetValue(r.opConfig.Parameters, "CSI_PROVISIONER_PRIORITY_CLASSNAME", "")
 
 	CSIParam.EnableOMAPGenerator = false
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_ENABLE_OMAP_GENERATOR", "false"), "true") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_OMAP_GENERATOR", "false"), "true") {
 		CSIParam.EnableOMAPGenerator = true
 	}
 
 	CSIParam.EnableCSIDriverSeLinuxMount = true
 
 	CSIParam.EnableRBDSnapshotter = true
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_ENABLE_RBD_SNAPSHOTTER", "true"), "false") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_RBD_SNAPSHOTTER", "true"), "false") {
 		CSIParam.EnableRBDSnapshotter = false
 	}
 
 	CSIParam.EnableCephFSSnapshotter = true
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_ENABLE_CEPHFS_SNAPSHOTTER", "true"), "false") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_CEPHFS_SNAPSHOTTER", "true"), "false") {
 		CSIParam.EnableCephFSSnapshotter = false
 	}
 
 	CSIParam.EnableNFSSnapshotter = true
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_ENABLE_NFS_SNAPSHOTTER", "true"), "false") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_NFS_SNAPSHOTTER", "true"), "false") {
 		CSIParam.EnableNFSSnapshotter = false
 	}
 
@@ -159,36 +147,36 @@ func (r *ReconcileCSI) setParams() error {
 	if err == nil {
 		CSIParam.EnableCSIAddonsSideCar = true
 	}
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_ENABLE_CSIADDONS", ""), "false") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_CSIADDONS", ""), "false") {
 		CSIParam.EnableCSIAddonsSideCar = false
 	}
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_ENABLE_CSIADDONS", ""), "true") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_CSIADDONS", ""), "true") {
 		CSIParam.EnableCSIAddonsSideCar = true
 	}
 
 	CSIParam.EnableCSITopology = false
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_ENABLE_TOPOLOGY", "false"), "true") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_TOPOLOGY", "false"), "true") {
 		CSIParam.EnableCSITopology = true
 	}
 
 	CSIParam.EnableCSIEncryption = false
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_ENABLE_ENCRYPTION", "false"), "true") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_ENCRYPTION", "false"), "true") {
 		CSIParam.EnableCSIEncryption = true
 	}
 
 	CSIParam.CSIEnableMetadata = false
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_ENABLE_METADATA", "false"), "true") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_METADATA", "false"), "true") {
 		CSIParam.CSIEnableMetadata = true
 	}
 
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_CEPHFS_PLUGIN_UPDATE_STRATEGY", rollingUpdate), onDelete) {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_CEPHFS_PLUGIN_UPDATE_STRATEGY", rollingUpdate), onDelete) {
 		CSIParam.CephFSPluginUpdateStrategy = onDelete
 	} else {
 		CSIParam.CephFSPluginUpdateStrategy = rollingUpdate
-		CSIParam.CephFSPluginUpdateStrategyMaxUnavailable = k8sutil.GetOperatorSetting("CSI_CEPHFS_PLUGIN_UPDATE_STRATEGY_MAX_UNAVAILABLE", "1")
+		CSIParam.CephFSPluginUpdateStrategyMaxUnavailable = k8sutil.GetValue(r.opConfig.Parameters, "CSI_CEPHFS_PLUGIN_UPDATE_STRATEGY_MAX_UNAVAILABLE", "1")
 	}
 
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_NFS_PLUGIN_UPDATE_STRATEGY", rollingUpdate), onDelete) {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_NFS_PLUGIN_UPDATE_STRATEGY", rollingUpdate), onDelete) {
 		CSIParam.NFSPluginUpdateStrategy = onDelete
 	} else {
 		CSIParam.NFSPluginUpdateStrategy = rollingUpdate
@@ -196,19 +184,19 @@ func (r *ReconcileCSI) setParams() error {
 
 	// Default values are based on Kubernetes official documentation.
 	// https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#daemonset-update-strategy
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_RBD_PLUGIN_UPDATE_STRATEGY", rollingUpdate), onDelete) {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_RBD_PLUGIN_UPDATE_STRATEGY", rollingUpdate), onDelete) {
 		CSIParam.RBDPluginUpdateStrategy = onDelete
 	} else {
 		CSIParam.RBDPluginUpdateStrategy = rollingUpdate
-		CSIParam.RBDPluginUpdateStrategyMaxUnavailable = k8sutil.GetOperatorSetting("CSI_RBD_PLUGIN_UPDATE_STRATEGY_MAX_UNAVAILABLE", "1")
+		CSIParam.RBDPluginUpdateStrategyMaxUnavailable = k8sutil.GetValue(r.opConfig.Parameters, "CSI_RBD_PLUGIN_UPDATE_STRATEGY_MAX_UNAVAILABLE", "1")
 	}
 
 	CSIParam.EnablePluginSelinuxHostMount = false
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_PLUGIN_ENABLE_SELINUX_HOST_MOUNT", "false"), "true") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_PLUGIN_ENABLE_SELINUX_HOST_MOUNT", "false"), "true") {
 		CSIParam.EnablePluginSelinuxHostMount = true
 	}
 
-	logLevel := k8sutil.GetOperatorSetting("CSI_LOG_LEVEL", "")
+	logLevel := k8sutil.GetValue(r.opConfig.Parameters, "CSI_LOG_LEVEL", "")
 	CSIParam.LogLevel = defaultLogLevel
 	if logLevel != "" {
 		l, err := strconv.ParseUint(logLevel, 10, 8)
@@ -219,7 +207,7 @@ func (r *ReconcileCSI) setParams() error {
 		}
 	}
 
-	sidecarLogLevel := k8sutil.GetOperatorSetting("CSI_SIDECAR_LOG_LEVEL", "")
+	sidecarLogLevel := k8sutil.GetValue(r.opConfig.Parameters, "CSI_SIDECAR_LOG_LEVEL", "")
 	CSIParam.SidecarLogLevel = defaultSidecarLogLevel
 	if sidecarLogLevel != "" {
 		l, err := strconv.ParseUint(sidecarLogLevel, 10, 8)
@@ -230,7 +218,7 @@ func (r *ReconcileCSI) setParams() error {
 		}
 	}
 
-	leaderElectionLeaseDuration := k8sutil.GetOperatorSetting("CSI_LEADER_ELECTION_LEASE_DURATION", "")
+	leaderElectionLeaseDuration := k8sutil.GetValue(r.opConfig.Parameters, "CSI_LEADER_ELECTION_LEASE_DURATION", "")
 	CSIParam.LeaderElectionLeaseDuration = defaultLeaderElectionLeaseDuration
 	if leaderElectionLeaseDuration != "" {
 		d, err := time.ParseDuration(leaderElectionLeaseDuration)
@@ -241,7 +229,7 @@ func (r *ReconcileCSI) setParams() error {
 		}
 	}
 
-	leaderElectionRenewDeadline := k8sutil.GetOperatorSetting("CSI_LEADER_ELECTION_RENEW_DEADLINE", "")
+	leaderElectionRenewDeadline := k8sutil.GetValue(r.opConfig.Parameters, "CSI_LEADER_ELECTION_RENEW_DEADLINE", "")
 	CSIParam.LeaderElectionRenewDeadline = defaultLeaderElectionRenewDeadline
 	if leaderElectionRenewDeadline != "" {
 		d, err := time.ParseDuration(leaderElectionRenewDeadline)
@@ -252,7 +240,7 @@ func (r *ReconcileCSI) setParams() error {
 		}
 	}
 
-	leaderElectionRetryPeriod := k8sutil.GetOperatorSetting("CSI_LEADER_ELECTION_RETRY_PERIOD", "")
+	leaderElectionRetryPeriod := k8sutil.GetValue(r.opConfig.Parameters, "CSI_LEADER_ELECTION_RETRY_PERIOD", "")
 	CSIParam.LeaderElectionRetryPeriod = defaultLeaderElectionRetryPeriod
 	if leaderElectionRetryPeriod != "" {
 		d, err := time.ParseDuration(leaderElectionRetryPeriod)
@@ -269,7 +257,7 @@ func (r *ReconcileCSI) setParams() error {
 		if len(nodes.Items) == 1 {
 			CSIParam.ProvisionerReplicas = 1
 		} else {
-			replicaStr := k8sutil.GetOperatorSetting("CSI_PROVISIONER_REPLICAS", "2")
+			replicaStr := k8sutil.GetValue(r.opConfig.Parameters, "CSI_PROVISIONER_REPLICAS", "2")
 			replicas, err := strconv.ParseInt(replicaStr, 10, 32)
 			if err != nil {
 				logger.Errorf("failed to parse CSI_PROVISIONER_REPLICAS. Defaulting to %d. %v", defaultProvisionerReplicas, err)
@@ -281,39 +269,39 @@ func (r *ReconcileCSI) setParams() error {
 		logger.Errorf("failed to get nodes. Defaulting the number of replicas of provisioner pods to %d. %v", CSIParam.ProvisionerReplicas, err)
 	}
 
-	CSIParam.CSIPluginImage = getImage("ROOK_CSI_CEPH_IMAGE", DefaultCSIPluginImage)
-	CSIParam.RegistrarImage = getImage("ROOK_CSI_REGISTRAR_IMAGE", DefaultRegistrarImage)
-	CSIParam.ProvisionerImage = getImage("ROOK_CSI_PROVISIONER_IMAGE", DefaultProvisionerImage)
-	CSIParam.AttacherImage = getImage("ROOK_CSI_ATTACHER_IMAGE", DefaultAttacherImage)
-	CSIParam.SnapshotterImage = getImage("ROOK_CSI_SNAPSHOTTER_IMAGE", DefaultSnapshotterImage)
-	CSIParam.ResizerImage = getImage("ROOK_CSI_RESIZER_IMAGE", DefaultResizerImage)
-	CSIParam.KubeletDirPath = k8sutil.GetOperatorSetting("ROOK_CSI_KUBELET_DIR_PATH", DefaultKubeletDirPath)
-	CSIParam.CSIAddonsImage = getImage("ROOK_CSIADDONS_IMAGE", DefaultCSIAddonsImage)
-	CSIParam.CSIDomainLabels = k8sutil.GetOperatorSetting("CSI_TOPOLOGY_DOMAIN_LABELS", "")
-	csiCephFSPodLabels := k8sutil.GetOperatorSetting("ROOK_CSI_CEPHFS_POD_LABELS", "")
+	CSIParam.CSIPluginImage = getImage(r.opConfig.Parameters, "ROOK_CSI_CEPH_IMAGE", DefaultCSIPluginImage)
+	CSIParam.RegistrarImage = getImage(r.opConfig.Parameters, "ROOK_CSI_REGISTRAR_IMAGE", DefaultRegistrarImage)
+	CSIParam.ProvisionerImage = getImage(r.opConfig.Parameters, "ROOK_CSI_PROVISIONER_IMAGE", DefaultProvisionerImage)
+	CSIParam.AttacherImage = getImage(r.opConfig.Parameters, "ROOK_CSI_ATTACHER_IMAGE", DefaultAttacherImage)
+	CSIParam.SnapshotterImage = getImage(r.opConfig.Parameters, "ROOK_CSI_SNAPSHOTTER_IMAGE", DefaultSnapshotterImage)
+	CSIParam.ResizerImage = getImage(r.opConfig.Parameters, "ROOK_CSI_RESIZER_IMAGE", DefaultResizerImage)
+	CSIParam.KubeletDirPath = k8sutil.GetValue(r.opConfig.Parameters, "ROOK_CSI_KUBELET_DIR_PATH", DefaultKubeletDirPath)
+	CSIParam.CSIAddonsImage = getImage(r.opConfig.Parameters, "ROOK_CSIADDONS_IMAGE", DefaultCSIAddonsImage)
+	CSIParam.CSIDomainLabels = k8sutil.GetValue(r.opConfig.Parameters, "CSI_TOPOLOGY_DOMAIN_LABELS", "")
+	csiCephFSPodLabels := k8sutil.GetValue(r.opConfig.Parameters, "ROOK_CSI_CEPHFS_POD_LABELS", "")
 	CSIParam.CSICephFSPodLabels = k8sutil.ParseStringToLabels(csiCephFSPodLabels)
-	csiNFSPodLabels := k8sutil.GetOperatorSetting("ROOK_CSI_NFS_POD_LABELS", "")
+	csiNFSPodLabels := k8sutil.GetValue(r.opConfig.Parameters, "ROOK_CSI_NFS_POD_LABELS", "")
 	CSIParam.CSINFSPodLabels = k8sutil.ParseStringToLabels(csiNFSPodLabels)
-	csiRBDPodLabels := k8sutil.GetOperatorSetting("ROOK_CSI_RBD_POD_LABELS", "")
+	csiRBDPodLabels := k8sutil.GetValue(r.opConfig.Parameters, "ROOK_CSI_RBD_POD_LABELS", "")
 	CSIParam.CSIRBDPodLabels = k8sutil.ParseStringToLabels(csiRBDPodLabels)
-	CSIParam.CSIClusterName = k8sutil.GetOperatorSetting("CSI_CLUSTER_NAME", "")
-	CSIParam.ImagePullPolicy = k8sutil.GetOperatorSetting("ROOK_CSI_IMAGE_PULL_POLICY", DefaultCSIImagePullPolicy)
-	CSIParam.CephFSKernelMountOptions = k8sutil.GetOperatorSetting("CSI_CEPHFS_KERNEL_MOUNT_OPTIONS", "")
+	CSIParam.CSIClusterName = k8sutil.GetValue(r.opConfig.Parameters, "CSI_CLUSTER_NAME", "")
+	CSIParam.ImagePullPolicy = k8sutil.GetValue(r.opConfig.Parameters, "ROOK_CSI_IMAGE_PULL_POLICY", DefaultCSIImagePullPolicy)
+	CSIParam.CephFSKernelMountOptions = k8sutil.GetValue(r.opConfig.Parameters, "CSI_CEPHFS_KERNEL_MOUNT_OPTIONS", "")
 
 	CSIParam.CephFSAttachRequired = true
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_CEPHFS_ATTACH_REQUIRED", "true"), "false") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_CEPHFS_ATTACH_REQUIRED", "true"), "false") {
 		CSIParam.CephFSAttachRequired = false
 	}
 	CSIParam.RBDAttachRequired = true
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_RBD_ATTACH_REQUIRED", "true"), "false") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_RBD_ATTACH_REQUIRED", "true"), "false") {
 		CSIParam.RBDAttachRequired = false
 	}
 	CSIParam.NFSAttachRequired = true
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_NFS_ATTACH_REQUIRED", "true"), "false") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_NFS_ATTACH_REQUIRED", "true"), "false") {
 		CSIParam.NFSAttachRequired = false
 	}
 
-	CSIParam.DriverNamePrefix = k8sutil.GetOperatorSetting("CSI_DRIVER_NAME_PREFIX", r.opConfig.OperatorNamespace)
+	CSIParam.DriverNamePrefix = k8sutil.GetValue(r.opConfig.Parameters, "CSI_DRIVER_NAME_PREFIX", r.opConfig.OperatorNamespace)
 
 	crd, err := r.context.ApiExtensionsClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), "volumegroupsnapshotclasses.groupsnapshot.storage.k8s.io", metav1.GetOptions{})
 	if err != nil && !kerrors.IsNotFound(err) {
@@ -324,7 +312,7 @@ func (r *ReconcileCSI) setParams() error {
 	if err == nil && len(crd.Spec.Versions) > 0 {
 		ver := crd.Spec.Versions[0]
 		// Determine if VolumeGroupSnapshot feature should be disabled
-		disableVGS := strings.EqualFold(k8sutil.GetOperatorSetting("CSI_ENABLE_VOLUME_GROUP_SNAPSHOT", "true"), "false")
+		disableVGS := strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_VOLUME_GROUP_SNAPSHOT", "true"), "false")
 		const (
 			enableVolumeGroupSnapshotFlag = "--enable-volume-group-snapshots="
 			featureGateFlag               = "--feature-gates=CSIVolumeGroupSnapshot="
@@ -346,7 +334,7 @@ func (r *ReconcileCSI) setParams() error {
 		}
 	}
 
-	kubeApiBurst := k8sutil.GetOperatorSetting("CSI_KUBE_API_BURST", "")
+	kubeApiBurst := k8sutil.GetValue(r.opConfig.Parameters, "CSI_KUBE_API_BURST", "")
 	CSIParam.KubeApiBurst = 0
 	if kubeApiBurst != "" {
 		k, err := strconv.ParseUint(kubeApiBurst, 10, 16)
@@ -357,7 +345,7 @@ func (r *ReconcileCSI) setParams() error {
 		}
 	}
 
-	kubeApiQPS := k8sutil.GetOperatorSetting("CSI_KUBE_API_QPS", "")
+	kubeApiQPS := k8sutil.GetValue(r.opConfig.Parameters, "CSI_KUBE_API_QPS", "")
 	CSIParam.KubeApiQPS = 0
 	if kubeApiQPS != "" {
 		k, err := strconv.ParseFloat(kubeApiQPS, 32)
diff --git a/pkg/operator/ceph/csi/operator_config.go b/pkg/operator/ceph/csi/operator_config.go
index 7e013245c..638e512b2 100644
--- a/pkg/operator/ceph/csi/operator_config.go
+++ b/pkg/operator/ceph/csi/operator_config.go
@@ -94,9 +94,9 @@ func (r *ReconcileCSI) generateCSIOpConfigSpec(cluster cephv1.CephCluster, opCon
 				PodCommonSpec: csiopv1a1.PodCommonSpec{
 					PrioritylClassName: &CSIParam.ProvisionerPriorityClassName,
 					Affinity: &v1.Affinity{
-						NodeAffinity: getNodeAffinity(pluginNodeAffinityEnv, &v1.NodeAffinity{}),
+						NodeAffinity: getNodeAffinity(r.opConfig.Parameters, pluginNodeAffinityEnv, &v1.NodeAffinity{}),
 					},
-					Tolerations: getToleration(pluginTolerationsEnv, []v1.Toleration{}),
+					Tolerations: getToleration(r.opConfig.Parameters, pluginTolerationsEnv, []v1.Toleration{}),
 				},
 				Resources:              csiopv1a1.NodePluginResourcesSpec{},
 				KubeletDirPath:         CSIParam.KubeletDirPath,
@@ -106,9 +106,9 @@ func (r *ReconcileCSI) generateCSIOpConfigSpec(cluster cephv1.CephCluster, opCon
 				PodCommonSpec: csiopv1a1.PodCommonSpec{
 					PrioritylClassName: &CSIParam.PluginPriorityClassName,
 					Affinity: &v1.Affinity{
-						NodeAffinity: getNodeAffinity(provisionerNodeAffinityEnv, &v1.NodeAffinity{}),
+						NodeAffinity: getNodeAffinity(r.opConfig.Parameters, provisionerNodeAffinityEnv, &v1.NodeAffinity{}),
 					},
-					Tolerations: getToleration(provisionerTolerationsEnv, []v1.Toleration{}),
+					Tolerations: getToleration(r.opConfig.Parameters, provisionerTolerationsEnv, []v1.Toleration{}),
 				},
 				Replicas:  &CSIParam.ProvisionerReplicas,
 				Resources: csiopv1a1.ControllerPluginResourcesSpec{},
diff --git a/pkg/operator/ceph/csi/operator_driver.go b/pkg/operator/ceph/csi/operator_driver.go
index 99c845a2b..f5457be45 100644
--- a/pkg/operator/ceph/csi/operator_driver.go
+++ b/pkg/operator/ceph/csi/operator_driver.go
@@ -92,11 +92,11 @@ func (r *ReconcileCSI) createOrUpdateRBDDriverResource(cluster cephv1.CephCluste
 		Spec: spec,
 	}
 
-	rbdDriver.Spec.ControllerPlugin.Resources = createDriverControllerPluginResources(rbdPluginResource)
+	rbdDriver.Spec.ControllerPlugin.Resources = createDriverControllerPluginResources(r.opConfig.Parameters, rbdPluginResource)
 	rbdDriver.Spec.Liveness = &csiopv1a1.LivenessSpec{
 		MetricsPort: int(CSIParam.RBDLivenessMetricsPort),
 	}
-	rbdDriver.Spec.NodePlugin.Resources = createDriverNodePluginResouces(rbdProvisionerResource)
+	rbdDriver.Spec.NodePlugin.Resources = createDriverNodePluginResouces(r.opConfig.Parameters, rbdProvisionerResource)
 	rbdDriver.Spec.NodePlugin.UpdateStrategy = &v1.DaemonSetUpdateStrategy{
 		Type: v1.RollingUpdateDaemonSetStrategyType,
 	}
@@ -143,12 +143,12 @@ func (r *ReconcileCSI) createOrUpdateCephFSDriverResource(cluster cephv1.CephClu
 		cephFsDriver.Spec.SnapshotPolicy = csiopv1a1.VolumeGroupSnapshotPolicy
 	}
 
-	cephFsDriver.Spec.ControllerPlugin.Resources = createDriverControllerPluginResources(cephFSPluginResource)
+	cephFsDriver.Spec.ControllerPlugin.Resources = createDriverControllerPluginResources(r.opConfig.Parameters, cephFSPluginResource)
 	cephFsDriver.Spec.Liveness = &csiopv1a1.LivenessSpec{
 		MetricsPort: int(CSIParam.CephFSLivenessMetricsPort),
 	}
 
-	cephFsDriver.Spec.NodePlugin.Resources = createDriverNodePluginResouces(cephFSProvisionerResource)
+	cephFsDriver.Spec.NodePlugin.Resources = createDriverNodePluginResouces(r.opConfig.Parameters, cephFSProvisionerResource)
 	cephFsDriver.Spec.NodePlugin.UpdateStrategy = &v1.DaemonSetUpdateStrategy{
 		Type: v1.RollingUpdateDaemonSetStrategyType,
 	}
@@ -190,9 +190,9 @@ func (r *ReconcileCSI) createOrUpdateNFSDriverResource(cluster cephv1.CephCluste
 		Spec: spec,
 	}
 
-	NFSDriver.Spec.ControllerPlugin.Resources = createDriverControllerPluginResources(nfsPluginResource)
+	NFSDriver.Spec.ControllerPlugin.Resources = createDriverControllerPluginResources(r.opConfig.Parameters, nfsPluginResource)
 
-	NFSDriver.Spec.NodePlugin.Resources = createDriverNodePluginResouces(nfsProvisionerResource)
+	NFSDriver.Spec.NodePlugin.Resources = createDriverNodePluginResouces(r.opConfig.Parameters, nfsProvisionerResource)
 	NFSDriver.Spec.NodePlugin.UpdateStrategy = &v1.DaemonSetUpdateStrategy{
 		Type: v1.RollingUpdateDaemonSetStrategyType,
 	}
@@ -270,9 +270,9 @@ func (r *ReconcileCSI) generateDriverSpec(clusterName string) (csiopv1a1.DriverS
 			PodCommonSpec: csiopv1a1.PodCommonSpec{
 				PrioritylClassName: &CSIParam.ProvisionerPriorityClassName,
 				Affinity: &corev1.Affinity{
-					NodeAffinity: getNodeAffinity(pluginNodeAffinityEnv, &corev1.NodeAffinity{}),
+					NodeAffinity: getNodeAffinity(r.opConfig.Parameters, pluginNodeAffinityEnv, &corev1.NodeAffinity{}),
 				},
-				Tolerations: getToleration(pluginTolerationsEnv, []corev1.Toleration{}),
+				Tolerations: getToleration(r.opConfig.Parameters, pluginTolerationsEnv, []corev1.Toleration{}),
 			},
 			Resources:              csiopv1a1.NodePluginResourcesSpec{},
 			KubeletDirPath:         CSIParam.KubeletDirPath,
@@ -282,9 +282,9 @@ func (r *ReconcileCSI) generateDriverSpec(clusterName string) (csiopv1a1.DriverS
 			PodCommonSpec: csiopv1a1.PodCommonSpec{
 				PrioritylClassName: &CSIParam.PluginPriorityClassName,
 				Affinity: &corev1.Affinity{
-					NodeAffinity: getNodeAffinity(provisionerNodeAffinityEnv, &corev1.NodeAffinity{}),
+					NodeAffinity: getNodeAffinity(r.opConfig.Parameters, provisionerNodeAffinityEnv, &corev1.NodeAffinity{}),
 				},
-				Tolerations: getToleration(provisionerTolerationsEnv, []corev1.Toleration{}),
+				Tolerations: getToleration(r.opConfig.Parameters, provisionerTolerationsEnv, []corev1.Toleration{}),
 			},
 			Replicas:  &CSIParam.ProvisionerReplicas,
 			Resources: csiopv1a1.ControllerPluginResourcesSpec{},
@@ -294,9 +294,9 @@ func (r *ReconcileCSI) generateDriverSpec(clusterName string) (csiopv1a1.DriverS
 	}, nil
 }
 
-func createDriverControllerPluginResources(key string) csiopv1a1.ControllerPluginResourcesSpec {
+func createDriverControllerPluginResources(opConfig map[string]string, key string) csiopv1a1.ControllerPluginResourcesSpec {
 	controllerPluginResources := csiopv1a1.ControllerPluginResourcesSpec{}
-	resource := getComputeResource(key)
+	resource := getComputeResource(opConfig, key)
 
 	for _, r := range resource {
 		if !reflect.DeepEqual(r.Resource, corev1.ResourceRequirements{}) {
@@ -347,9 +347,9 @@ func createDriverControllerPluginResources(key string) csiopv1a1.ControllerPlugi
 	return controllerPluginResources
 }
 
-func createDriverNodePluginResouces(key string) csiopv1a1.NodePluginResourcesSpec {
+func createDriverNodePluginResouces(opConfig map[string]string, key string) csiopv1a1.NodePluginResourcesSpec {
 	nodePluginResources := csiopv1a1.NodePluginResourcesSpec{}
-	resource := getComputeResource(key)
+	resource := getComputeResource(opConfig, key)
 
 	for _, r := range resource {
 		if !reflect.DeepEqual(r.Resource, corev1.ResourceRequirements{}) {
diff --git a/pkg/operator/ceph/csi/spec.go b/pkg/operator/ceph/csi/spec.go
index 5b968453f..9381619cc 100644
--- a/pkg/operator/ceph/csi/spec.go
+++ b/pkg/operator/ceph/csi/spec.go
@@ -83,8 +83,6 @@ type Param struct {
 	SidecarLogLevel                          uint8
 	CephFSLivenessMetricsPort                uint16
 	CSIAddonsPort                            uint16
-	CSIAddonsRBDProvisionerPort              uint16
-	CSIAddonsCephFSProvisionerPort           uint16
 	RBDLivenessMetricsPort                   uint16
 	KubeApiBurst                             uint16
 	KubeApiQPS                               float32
@@ -221,14 +219,12 @@ const (
 	// kubelet directory path
 	DefaultKubeletDirPath = "/var/lib/kubelet"
 
-	// gRPC metrics and liveness port for CephFS and RBD
-	DefaultCephFSGRPCMerticsPort          uint16 = 9091
-	DefaultCephFSLivenessMerticsPort      uint16 = 9081
-	DefaultRBDGRPCMerticsPort             uint16 = 9090
-	DefaultRBDLivenessMerticsPort         uint16 = 9080
-	DefaultCSIAddonsPort                  uint16 = 9070
-	DefaultCSIAddonsRBDProvisionerPort    uint16 = 9070
-	DefaultCSIAddonsCephFSProvisionerPort uint16 = 9070
+	// grpc metrics and liveness port for cephfs  and rbd
+	DefaultCephFSGRPCMerticsPort     uint16 = 9091
+	DefaultCephFSLivenessMerticsPort uint16 = 9081
+	DefaultRBDGRPCMerticsPort        uint16 = 9090
+	DefaultRBDLivenessMerticsPort    uint16 = 9080
+	DefaultCSIAddonsPort             uint16 = 9070
 
 	// default log level for csi containers
 	defaultLogLevel        uint8 = 0
@@ -426,25 +422,25 @@ func (r *ReconcileCSI) startDrivers(ownerInfo *k8sutil.OwnerInfo) error {
 	}
 
 	// get common provisioner tolerations and node affinity
-	provisionerTolerations := getToleration(provisionerTolerationsEnv, []corev1.Toleration{})
-	provisionerNodeAffinity := getNodeAffinity(provisionerNodeAffinityEnv, &corev1.NodeAffinity{})
+	provisionerTolerations := getToleration(r.opConfig.Parameters, provisionerTolerationsEnv, []corev1.Toleration{})
+	provisionerNodeAffinity := getNodeAffinity(r.opConfig.Parameters, provisionerNodeAffinityEnv, &corev1.NodeAffinity{})
 
 	// get common plugin tolerations and node affinity
-	pluginTolerations := getToleration(pluginTolerationsEnv, []corev1.Toleration{})
-	pluginNodeAffinity := getNodeAffinity(pluginNodeAffinityEnv, &corev1.NodeAffinity{})
+	pluginTolerations := getToleration(r.opConfig.Parameters, pluginTolerationsEnv, []corev1.Toleration{})
+	pluginNodeAffinity := getNodeAffinity(r.opConfig.Parameters, pluginNodeAffinityEnv, &corev1.NodeAffinity{})
 
 	if rbdPlugin != nil {
 		// get RBD plugin tolerations and node affinity, defaults to common tolerations and node affinity if not specified
-		rbdPluginTolerations := getToleration(rbdPluginTolerationsEnv, pluginTolerations)
-		rbdPluginNodeAffinity := getNodeAffinity(rbdPluginNodeAffinityEnv, pluginNodeAffinity)
+		rbdPluginTolerations := getToleration(r.opConfig.Parameters, rbdPluginTolerationsEnv, pluginTolerations)
+		rbdPluginNodeAffinity := getNodeAffinity(r.opConfig.Parameters, rbdPluginNodeAffinityEnv, pluginNodeAffinity)
 		// apply RBD plugin tolerations and node affinity
 		applyToPodSpec(&rbdPlugin.Spec.Template.Spec, rbdPluginNodeAffinity, rbdPluginTolerations)
 		// apply resource request and limit to rbdplugin containers
-		applyResourcesToContainers(rbdPluginResource, &rbdPlugin.Spec.Template.Spec)
+		applyResourcesToContainers(r.opConfig.Parameters, rbdPluginResource, &rbdPlugin.Spec.Template.Spec)
 		// apply custom mounts to volumes
-		applyVolumeToPodSpec(rbdPluginVolume, &rbdPlugin.Spec.Template.Spec)
+		applyVolumeToPodSpec(r.opConfig.Parameters, rbdPluginVolume, &rbdPlugin.Spec.Template.Spec)
 		// apply custom mounts to volume mounts
-		applyVolumeMountToContainer(rbdPluginVolumeMount, "csi-rbdplugin", &rbdPlugin.Spec.Template.Spec)
+		applyVolumeMountToContainer(r.opConfig.Parameters, rbdPluginVolumeMount, "csi-rbdplugin", &rbdPlugin.Spec.Template.Spec)
 		err = ownerInfo.SetControllerReference(rbdPlugin)
 		if err != nil {
 			return errors.Wrapf(err, "failed to set owner reference to rbd plugin daemonset %q", rbdPlugin.Name)
@@ -462,12 +458,12 @@ func (r *ReconcileCSI) startDrivers(ownerInfo *k8sutil.OwnerInfo) error {
 
 	if rbdProvisionerDeployment != nil {
 		// get RBD provisioner tolerations and node affinity, defaults to common tolerations and node affinity if not specified
-		rbdProvisionerTolerations := getToleration(rbdProvisionerTolerationsEnv, provisionerTolerations)
-		rbdProvisionerNodeAffinity := getNodeAffinity(rbdProvisionerNodeAffinityEnv, provisionerNodeAffinity)
+		rbdProvisionerTolerations := getToleration(r.opConfig.Parameters, rbdProvisionerTolerationsEnv, provisionerTolerations)
+		rbdProvisionerNodeAffinity := getNodeAffinity(r.opConfig.Parameters, rbdProvisionerNodeAffinityEnv, provisionerNodeAffinity)
 		// apply RBD provisioner tolerations and node affinity
 		applyToPodSpec(&rbdProvisionerDeployment.Spec.Template.Spec, rbdProvisionerNodeAffinity, rbdProvisionerTolerations)
 		// apply resource request and limit to rbd provisioner containers
-		applyResourcesToContainers(rbdProvisionerResource, &rbdProvisionerDeployment.Spec.Template.Spec)
+		applyResourcesToContainers(r.opConfig.Parameters, rbdProvisionerResource, &rbdProvisionerDeployment.Spec.Template.Spec)
 		err = ownerInfo.SetControllerReference(rbdProvisionerDeployment)
 		if err != nil {
 			return errors.Wrapf(err, "failed to set owner reference to rbd provisioner deployment %q", rbdProvisionerDeployment.Name)
@@ -504,16 +500,16 @@ func (r *ReconcileCSI) startDrivers(ownerInfo *k8sutil.OwnerInfo) error {
 
 	if cephfsPlugin != nil {
 		// get CephFS plugin tolerations and node affinity, defaults to common tolerations and node affinity if not specified
-		cephFSPluginTolerations := getToleration(cephFSPluginTolerationsEnv, pluginTolerations)
-		cephFSPluginNodeAffinity := getNodeAffinity(cephFSPluginNodeAffinityEnv, pluginNodeAffinity)
+		cephFSPluginTolerations := getToleration(r.opConfig.Parameters, cephFSPluginTolerationsEnv, pluginTolerations)
+		cephFSPluginNodeAffinity := getNodeAffinity(r.opConfig.Parameters, cephFSPluginNodeAffinityEnv, pluginNodeAffinity)
 		// apply CephFS plugin tolerations and node affinity
 		applyToPodSpec(&cephfsPlugin.Spec.Template.Spec, cephFSPluginNodeAffinity, cephFSPluginTolerations)
 		// apply resource request and limit to cephfs plugin containers
-		applyResourcesToContainers(cephFSPluginResource, &cephfsPlugin.Spec.Template.Spec)
+		applyResourcesToContainers(r.opConfig.Parameters, cephFSPluginResource, &cephfsPlugin.Spec.Template.Spec)
 		// apply custom mounts to volumes
-		applyVolumeToPodSpec(cephFSPluginVolume, &cephfsPlugin.Spec.Template.Spec)
+		applyVolumeToPodSpec(r.opConfig.Parameters, cephFSPluginVolume, &cephfsPlugin.Spec.Template.Spec)
 		// apply custom mounts to volume mounts
-		applyVolumeMountToContainer(cephFSPluginVolumeMount, "csi-cephfsplugin", &cephfsPlugin.Spec.Template.Spec)
+		applyVolumeMountToContainer(r.opConfig.Parameters, cephFSPluginVolumeMount, "csi-cephfsplugin", &cephfsPlugin.Spec.Template.Spec)
 		err = ownerInfo.SetControllerReference(cephfsPlugin)
 		if err != nil {
 			return errors.Wrapf(err, "failed to set owner reference to cephfs plugin daemonset %q", cephfsPlugin.Name)
@@ -532,13 +528,13 @@ func (r *ReconcileCSI) startDrivers(ownerInfo *k8sutil.OwnerInfo) error {
 
 	if cephfsProvisionerDeployment != nil {
 		// get CephFS provisioner tolerations and node affinity, defaults to common tolerations and node affinity if not specified
-		cephFSProvisionerTolerations := getToleration(cephFSProvisionerTolerationsEnv, provisionerTolerations)
-		cephFSProvisionerNodeAffinity := getNodeAffinity(cephFSProvisionerNodeAffinityEnv, provisionerNodeAffinity)
+		cephFSProvisionerTolerations := getToleration(r.opConfig.Parameters, cephFSProvisionerTolerationsEnv, provisionerTolerations)
+		cephFSProvisionerNodeAffinity := getNodeAffinity(r.opConfig.Parameters, cephFSProvisionerNodeAffinityEnv, provisionerNodeAffinity)
 		// apply CephFS provisioner tolerations and node affinity
 		applyToPodSpec(&cephfsProvisionerDeployment.Spec.Template.Spec, cephFSProvisionerNodeAffinity, cephFSProvisionerTolerations)
 		// get resource details for cephfs provisioner
 		// apply resource request and limit to cephfs provisioner containers
-		applyResourcesToContainers(cephFSProvisionerResource, &cephfsProvisionerDeployment.Spec.Template.Spec)
+		applyResourcesToContainers(r.opConfig.Parameters, cephFSProvisionerResource, &cephfsProvisionerDeployment.Spec.Template.Spec)
 		err = ownerInfo.SetControllerReference(cephfsProvisionerDeployment)
 		if err != nil {
 			return errors.Wrapf(err, "failed to set owner reference to cephfs provisioner deployment %q", cephfsProvisionerDeployment.Name)
@@ -574,16 +570,16 @@ func (r *ReconcileCSI) startDrivers(ownerInfo *k8sutil.OwnerInfo) error {
 
 	if nfsPlugin != nil {
 		// get NFS plugin tolerations and node affinity, defaults to common tolerations and node affinity if not specified
-		nfsPluginTolerations := getToleration(nfsPluginTolerationsEnv, pluginTolerations)
-		nfsPluginNodeAffinity := getNodeAffinity(nfsPluginNodeAffinityEnv, pluginNodeAffinity)
+		nfsPluginTolerations := getToleration(r.opConfig.Parameters, nfsPluginTolerationsEnv, pluginTolerations)
+		nfsPluginNodeAffinity := getNodeAffinity(r.opConfig.Parameters, nfsPluginNodeAffinityEnv, pluginNodeAffinity)
 		// apply NFS plugin tolerations and node affinity
 		applyToPodSpec(&nfsPlugin.Spec.Template.Spec, nfsPluginNodeAffinity, nfsPluginTolerations)
 		// apply resource request and limit to nfs plugin containers
-		applyResourcesToContainers(nfsPluginResource, &nfsPlugin.Spec.Template.Spec)
+		applyResourcesToContainers(r.opConfig.Parameters, nfsPluginResource, &nfsPlugin.Spec.Template.Spec)
 		// apply custom mounts to volumes
-		applyVolumeToPodSpec(nfsPluginVolume, &nfsPlugin.Spec.Template.Spec)
+		applyVolumeToPodSpec(r.opConfig.Parameters, nfsPluginVolume, &nfsPlugin.Spec.Template.Spec)
 		// apply custom mounts to volume mounts
-		applyVolumeMountToContainer(nfsPluginVolumeMount, "csi-nfsplugin", &nfsPlugin.Spec.Template.Spec)
+		applyVolumeMountToContainer(r.opConfig.Parameters, nfsPluginVolumeMount, "csi-nfsplugin", &nfsPlugin.Spec.Template.Spec)
 		err = ownerInfo.SetControllerReference(nfsPlugin)
 		if err != nil {
 			return errors.Wrapf(err, "failed to set owner reference to nfs plugin daemonset %q", nfsPlugin.Name)
@@ -602,13 +598,13 @@ func (r *ReconcileCSI) startDrivers(ownerInfo *k8sutil.OwnerInfo) error {
 
 	if nfsProvisionerDeployment != nil {
 		// get NFS provisioner tolerations and node affinity, defaults to common tolerations and node affinity if not specified
-		nfsProvisionerTolerations := getToleration(nfsProvisionerTolerationsEnv, provisionerTolerations)
-		nfsProvisionerNodeAffinity := getNodeAffinity(nfsProvisionerNodeAffinityEnv, provisionerNodeAffinity)
+		nfsProvisionerTolerations := getToleration(r.opConfig.Parameters, nfsProvisionerTolerationsEnv, provisionerTolerations)
+		nfsProvisionerNodeAffinity := getNodeAffinity(r.opConfig.Parameters, nfsProvisionerNodeAffinityEnv, provisionerNodeAffinity)
 		// apply NFS provisioner tolerations and node affinity
 		applyToPodSpec(&nfsProvisionerDeployment.Spec.Template.Spec, nfsProvisionerNodeAffinity, nfsProvisionerTolerations)
 		// get resource details for nfs provisioner
 		// apply resource request and limit to nfs provisioner containers
-		applyResourcesToContainers(nfsProvisionerResource, &nfsProvisionerDeployment.Spec.Template.Spec)
+		applyResourcesToContainers(r.opConfig.Parameters, nfsProvisionerResource, &nfsProvisionerDeployment.Spec.Template.Spec)
 		err = ownerInfo.SetControllerReference(nfsProvisionerDeployment)
 		if err != nil {
 			return errors.Wrapf(err, "failed to set owner reference to nfs provisioner deployment %q", nfsProvisionerDeployment.Name)
@@ -634,7 +630,7 @@ func (r *ReconcileCSI) startDrivers(ownerInfo *k8sutil.OwnerInfo) error {
 	if EnableRBD {
 		err = csiDriverobj.createCSIDriverInfo(
 			r.opManagerContext, r.context.Clientset,
-			RBDDriverName, k8sutil.GetOperatorSetting("CSI_RBD_FSGROUPPOLICY", string(k8scsi.FileFSGroupPolicy)),
+			RBDDriverName, k8sutil.GetValue(r.opConfig.Parameters, "CSI_RBD_FSGROUPPOLICY", string(k8scsi.FileFSGroupPolicy)),
 			tp.Param.RBDAttachRequired, CSIParam.EnableCSIDriverSeLinuxMount)
 		if err != nil {
 			return errors.Wrapf(err, "failed to create CSI driver object for %q", RBDDriverName)
@@ -643,7 +639,7 @@ func (r *ReconcileCSI) startDrivers(ownerInfo *k8sutil.OwnerInfo) error {
 	if EnableCephFS {
 		err = csiDriverobj.createCSIDriverInfo(
 			r.opManagerContext, r.context.Clientset,
-			CephFSDriverName, k8sutil.GetOperatorSetting("CSI_CEPHFS_FSGROUPPOLICY", string(k8scsi.FileFSGroupPolicy)),
+			CephFSDriverName, k8sutil.GetValue(r.opConfig.Parameters, "CSI_CEPHFS_FSGROUPPOLICY", string(k8scsi.FileFSGroupPolicy)),
 			tp.Param.CephFSAttachRequired, CSIParam.EnableCSIDriverSeLinuxMount)
 		if err != nil {
 			return errors.Wrapf(err, "failed to create CSI driver object for %q", CephFSDriverName)
@@ -651,7 +647,7 @@ func (r *ReconcileCSI) startDrivers(ownerInfo *k8sutil.OwnerInfo) error {
 	}
 	if EnableNFS {
 		err = csiDriverobj.createCSIDriverInfo(r.opManagerContext, r.context.Clientset,
-			NFSDriverName, k8sutil.GetOperatorSetting("CSI_NFS_FSGROUPPOLICY", string(k8scsi.FileFSGroupPolicy)),
+			NFSDriverName, k8sutil.GetValue(r.opConfig.Parameters, "CSI_NFS_FSGROUPPOLICY", string(k8scsi.FileFSGroupPolicy)),
 			tp.Param.NFSAttachRequired, CSIParam.EnableCSIDriverSeLinuxMount)
 		if err != nil {
 			return errors.Wrapf(err, "failed to create CSI driver object for %q", NFSDriverName)
diff --git a/pkg/operator/ceph/csi/template/cephfs/csi-cephfsplugin-provisioner-dep.yaml b/pkg/operator/ceph/csi/template/cephfs/csi-cephfsplugin-provisioner-dep.yaml
index ff9554ad7..5ed29f704 100644
--- a/pkg/operator/ceph/csi/template/cephfs/csi-cephfsplugin-provisioner-dep.yaml
+++ b/pkg/operator/ceph/csi/template/cephfs/csi-cephfsplugin-provisioner-dep.yaml
@@ -216,7 +216,7 @@ spec:
             - "--node-id=$(NODE_ID)"
             - "--v={{ .LogLevel }}"
             - "--csi-addons-address=$(CSIADDONS_ENDPOINT)"
-            - "--controller-port={{ .CSIAddonsCephFSProvisionerPort }}"
+            - "--controller-port={{ .CSIAddonsPort }}"
             - "--pod=$(POD_NAME)"
             - "--namespace=$(POD_NAMESPACE)"
             - "--pod-uid=$(POD_UID)"
@@ -231,7 +231,7 @@ spec:
             - "--log_file={{ .CsiLogRootPath }}/log/{{ .CsiComponentName }}/csi-addons.log"
             {{ end }}
           ports:
-            - containerPort: {{ .CSIAddonsCephFSProvisionerPort }}
+            - containerPort: {{ .CSIAddonsPort }}
           env:
             - name: NODE_ID
               valueFrom:
diff --git a/pkg/operator/ceph/csi/template/rbd/csi-rbdplugin-provisioner-dep.yaml b/pkg/operator/ceph/csi/template/rbd/csi-rbdplugin-provisioner-dep.yaml
index bacd76682..135338322 100644
--- a/pkg/operator/ceph/csi/template/rbd/csi-rbdplugin-provisioner-dep.yaml
+++ b/pkg/operator/ceph/csi/template/rbd/csi-rbdplugin-provisioner-dep.yaml
@@ -169,7 +169,7 @@ spec:
             - "--node-id=$(NODE_ID)"
             - "--v={{ .LogLevel }}"
             - "--csi-addons-address=$(CSIADDONS_ENDPOINT)"
-            - "--controller-port={{ .CSIAddonsRBDProvisionerPort }}"
+            - "--controller-port={{ .CSIAddonsPort }}"
             - "--pod=$(POD_NAME)"
             - "--namespace=$(POD_NAMESPACE)"
             - "--pod-uid=$(POD_UID)"
@@ -184,7 +184,7 @@ spec:
             - "--log_file={{ .CsiLogRootPath }}/log/{{ .CsiComponentName }}/csi-addons.log"
             {{ end }}
           ports:
-            - containerPort: {{ .CSIAddonsRBDProvisionerPort }}
+            - containerPort: {{ .CSIAddonsPort }}
           env:
             - name: NODE_ID
               valueFrom:
diff --git a/pkg/operator/ceph/csi/util.go b/pkg/operator/ceph/csi/util.go
index 60f549bb1..4c3e70827 100644
--- a/pkg/operator/ceph/csi/util.go
+++ b/pkg/operator/ceph/csi/util.go
@@ -98,8 +98,8 @@ func applyLogrotateSidecar(specTemplate *corev1.PodTemplateSpec, name, templateD
 	specTemplate.Spec.Containers = append(specTemplate.Spec.Containers, logrotateSidecarContainer)
 }
 
-func applyResourcesToContainers(key string, podspec *corev1.PodSpec) {
-	resource := getComputeResource(key)
+func applyResourcesToContainers(opConfig map[string]string, key string, podspec *corev1.PodSpec) {
+	resource := getComputeResource(opConfig, key)
 
 	for _, r := range resource {
 		for i, c := range podspec.Containers {
@@ -110,12 +110,12 @@ func applyResourcesToContainers(key string, podspec *corev1.PodSpec) {
 	}
 }
 
-func getComputeResource(key string) []k8sutil.ContainerResource {
+func getComputeResource(opConfig map[string]string, key string) []k8sutil.ContainerResource {
 	// Add Resource list if any
 	resource := []k8sutil.ContainerResource{}
 	var err error
 
-	if resourceRaw := k8sutil.GetOperatorSetting(key, ""); resourceRaw != "" {
+	if resourceRaw := k8sutil.GetValue(opConfig, key, ""); resourceRaw != "" {
 		resource, err = k8sutil.YamlToContainerResourceArray(resourceRaw)
 		if err != nil {
 			logger.Warningf("failed to parse %q. %v", resourceRaw, err)
@@ -124,9 +124,9 @@ func getComputeResource(key string) []k8sutil.ContainerResource {
 	return resource
 }
 
-func getToleration(tolerationsName string, defaultTolerations []corev1.Toleration) []corev1.Toleration {
+func getToleration(opConfig map[string]string, tolerationsName string, defaultTolerations []corev1.Toleration) []corev1.Toleration {
 	// Add toleration if any, otherwise return defaultTolerations
-	tolerationsRaw := k8sutil.GetOperatorSetting(tolerationsName, "")
+	tolerationsRaw := k8sutil.GetValue(opConfig, tolerationsName, "")
 	if tolerationsRaw == "" {
 		return defaultTolerations
 	}
@@ -147,9 +147,9 @@ func getToleration(tolerationsName string, defaultTolerations []corev1.Toleratio
 	return tolerations
 }
 
-func getNodeAffinity(nodeAffinityName string, defaultNodeAffinity *corev1.NodeAffinity) *corev1.NodeAffinity {
+func getNodeAffinity(opConfig map[string]string, nodeAffinityName string, defaultNodeAffinity *corev1.NodeAffinity) *corev1.NodeAffinity {
 	// Add NodeAffinity if any, otherwise return defaultNodeAffinity
-	nodeAffinity := k8sutil.GetOperatorSetting(nodeAffinityName, "")
+	nodeAffinity := k8sutil.GetValue(opConfig, nodeAffinityName, "")
 	if nodeAffinity == "" {
 		return defaultNodeAffinity
 	}
@@ -168,9 +168,9 @@ func applyToPodSpec(pod *corev1.PodSpec, n *corev1.NodeAffinity, t []corev1.Tole
 	}
 }
 
-func getPortFromConfig(env string, defaultPort uint16) (uint16, error) {
-	port := k8sutil.GetOperatorSetting(env, strconv.Itoa(int(defaultPort)))
-	if strings.TrimSpace(k8sutil.GetOperatorSetting(env, strconv.Itoa(int(defaultPort)))) == "" {
+func getPortFromConfig(data map[string]string, env string, defaultPort uint16) (uint16, error) {
+	port := k8sutil.GetValue(data, env, strconv.Itoa(int(defaultPort)))
+	if strings.TrimSpace(k8sutil.GetValue(data, env, strconv.Itoa(int(defaultPort)))) == "" {
 		return defaultPort, nil
 	}
 	p, err := strconv.ParseUint(port, 10, 64)
@@ -203,8 +203,8 @@ func GetPodAntiAffinity(key, value string) corev1.PodAntiAffinity {
 	}
 }
 
-func applyVolumeToPodSpec(configName string, podspec *corev1.PodSpec) {
-	volumesRaw := k8sutil.GetOperatorSetting(configName, "")
+func applyVolumeToPodSpec(opConfig map[string]string, configName string, podspec *corev1.PodSpec) {
+	volumesRaw := k8sutil.GetValue(opConfig, configName, "")
 	if volumesRaw == "" {
 		return
 	}
@@ -230,8 +230,8 @@ func applyVolumeToPodSpec(configName string, podspec *corev1.PodSpec) {
 	}
 }
 
-func applyVolumeMountToContainer(configName, containerName string, podspec *corev1.PodSpec) {
-	volumeMountsRaw := k8sutil.GetOperatorSetting(configName, "")
+func applyVolumeMountToContainer(opConfig map[string]string, configName, containerName string, podspec *corev1.PodSpec) {
+	volumeMountsRaw := k8sutil.GetValue(opConfig, configName, "")
 	if volumeMountsRaw == "" {
 		return
 	}
@@ -267,8 +267,8 @@ func applyVolumeMountToContainer(configName, containerName string, podspec *core
 
 // getImage returns the image for the given setting name. If the image does not contain version,
 // the default version is appended from the default image.
-func getImage(settingName, defaultImage string) string {
-	image := k8sutil.GetOperatorSetting(settingName, defaultImage)
+func getImage(data map[string]string, settingName, defaultImage string) string {
+	image := k8sutil.GetValue(data, settingName, defaultImage)
 	if !strings.Contains(image, ":") {
 		version := strings.SplitN(defaultImage, ":", 2)[1]
 		image = fmt.Sprintf("%s:%s", image, version)
diff --git a/pkg/operator/ceph/file/mds/livenessprobe.sh b/pkg/operator/ceph/file/mds/livenessprobe.sh
index 667bb8e4a..5cddfac15 100644
--- a/pkg/operator/ceph/file/mds/livenessprobe.sh
+++ b/pkg/operator/ceph/file/mds/livenessprobe.sh
@@ -7,8 +7,7 @@ FILESYSTEM_NAME="{{ .FilesystemName }}"
 KEYRING="{{ .Keyring }}"
 CMD_TIMEOUT="{{ .CmdTimeout }}"
 
-export CEPH_ARGS="--keyring $KEYRING" # workaround for https://tracker.ceph.com/issues/70167
-outp="$(ceph fs dump --mon-host="$ROOK_CEPH_MON_HOST" --mon-initial-members="$ROOK_CEPH_MON_INITIAL_MEMBERS" --name="mds.$MDS_ID" --connect-timeout="$CMD_TIMEOUT" --format json)"
+outp="$(ceph fs dump --mon-host="$ROOK_CEPH_MON_HOST" --mon-initial-members="$ROOK_CEPH_MON_INITIAL_MEMBERS" --keyring "$KEYRING" --connect-timeout="$CMD_TIMEOUT" --format json)"
 rc=$?
 if [ $rc -ne 0 ]; then
     echo "ceph MDS dump check failed with the following output:"
diff --git a/pkg/operator/ceph/object/bucket/controller.go b/pkg/operator/ceph/object/bucket/controller.go
index b9a4007f7..eed00adc9 100644
--- a/pkg/operator/ceph/object/bucket/controller.go
+++ b/pkg/operator/ceph/object/bucket/controller.go
@@ -36,6 +36,7 @@ import (
 	v1 "k8s.io/api/core/v1"
 	kerrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/types"
 )
 
 const (
@@ -139,6 +140,25 @@ func (r *ReconcileBucket) reconcile(request reconcile.Request) (reconcile.Result
 		return reconcile.Result{}, nil
 	}
 
+	// Fetch the operator's configmap. We force the NamespaceName to the operator since the request
+	// could be a CephCluster. If so the NamespaceName will be the one from the cluster and thus the
+	// CM won't be found
+	opNamespaceName := types.NamespacedName{Name: opcontroller.OperatorSettingConfigMapName, Namespace: r.opConfig.OperatorNamespace}
+	opConfig := &v1.ConfigMap{}
+	err = r.client.Get(r.opManagerContext, opNamespaceName, opConfig)
+	if err != nil {
+		if kerrors.IsNotFound(err) {
+			logger.Debug("operator's configmap resource not found. will use default value or env var.")
+			r.opConfig.Parameters = make(map[string]string)
+		} else {
+			// Error reading the object - requeue the request.
+			return opcontroller.ImmediateRetryResult, errors.Wrap(err, "failed to get operator's configmap")
+		}
+	} else {
+		// Populate the operator's config
+		r.opConfig.Parameters = opConfig.Data
+	}
+
 	// Populate clusterInfo during each reconcile
 	clusterInfo, _, _, err := opcontroller.LoadClusterInfo(r.context, r.opManagerContext, cephCluster.Namespace, &cephCluster.Spec)
 	if err != nil {
@@ -157,7 +177,7 @@ func (r *ReconcileBucket) reconcile(request reconcile.Request) (reconcile.Result
 
 	// note: the error return below is ignored and is expected to be removed from the
 	//   bucket library's `NewProvisioner` function
-	bucketController, _ := NewBucketController(r.context.KubeConfig, bucketProvisioner)
+	bucketController, _ := NewBucketController(r.context.KubeConfig, bucketProvisioner, r.opConfig.Parameters)
 
 	// We must run this in a go routine since RunWithContext() blocks and waits for the context to
 	// be Done. However, since it has a context, the go routine will exit on reload with SIGHUP
diff --git a/pkg/operator/ceph/object/bucket/util.go b/pkg/operator/ceph/object/bucket/util.go
index 7e8df47ea..28953500c 100644
--- a/pkg/operator/ceph/object/bucket/util.go
+++ b/pkg/operator/ceph/object/bucket/util.go
@@ -24,7 +24,6 @@ import (
 	"github.com/kube-object-storage/lib-bucket-provisioner/pkg/provisioner"
 	"github.com/pkg/errors"
 	cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1"
-	opcontroller "github.com/rook/rook/pkg/operator/ceph/controller"
 	cephObject "github.com/rook/rook/pkg/operator/ceph/object"
 	storagev1 "k8s.io/api/storage/v1"
 	kerrors "k8s.io/apimachinery/pkg/api/errors"
@@ -43,9 +42,9 @@ const (
 	objectStoreEndpoint  = "endpoint"
 )
 
-func NewBucketController(cfg *rest.Config, p *Provisioner) (*provisioner.Provisioner, error) {
+func NewBucketController(cfg *rest.Config, p *Provisioner, data map[string]string) (*provisioner.Provisioner, error) {
 	const allNamespaces = ""
-	provName, err := cephObject.GetObjectBucketProvisioner(p.clusterInfo.Namespace)
+	provName, err := cephObject.GetObjectBucketProvisioner(data, p.clusterInfo.Namespace)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to get provisioner name")
 	}
@@ -94,9 +93,6 @@ func additionalConfigSpecFromMap(config map[string]string) (*additionalConfigSpe
 	spec := additionalConfigSpec{}
 
 	if _, ok := config["maxObjects"]; ok {
-		if !opcontroller.ObcAdditionalConfigKeyIsAllowed("maxObjects") {
-			return nil, errors.Errorf("OBC config %q is not allowed", "maxObjects")
-		}
 		spec.maxObjects, err = quanityToInt64(config["maxObjects"])
 		if err != nil {
 			return nil, errors.Wrapf(err, "failed to parse maxObjects quota")
@@ -104,9 +100,6 @@ func additionalConfigSpecFromMap(config map[string]string) (*additionalConfigSpe
 	}
 
 	if _, ok := config["maxSize"]; ok {
-		if !opcontroller.ObcAdditionalConfigKeyIsAllowed("maxSize") {
-			return nil, errors.Errorf("OBC config %q is not allowed", "maxSize")
-		}
 		spec.maxSize, err = quanityToInt64(config["maxSize"])
 		if err != nil {
 			return nil, errors.Wrapf(err, "failed to parse maxSize quota")
@@ -114,9 +107,6 @@ func additionalConfigSpecFromMap(config map[string]string) (*additionalConfigSpe
 	}
 
 	if _, ok := config["bucketMaxObjects"]; ok {
-		if !opcontroller.ObcAdditionalConfigKeyIsAllowed("bucketMaxObjects") {
-			return nil, errors.Errorf("OBC config %q is not allowed", "bucketMaxObjects")
-		}
 		spec.bucketMaxObjects, err = quanityToInt64(config["bucketMaxObjects"])
 		if err != nil {
 			return nil, errors.Wrapf(err, "failed to parse bucketMaxObjects quota")
@@ -124,9 +114,6 @@ func additionalConfigSpecFromMap(config map[string]string) (*additionalConfigSpe
 	}
 
 	if _, ok := config["bucketMaxSize"]; ok {
-		if !opcontroller.ObcAdditionalConfigKeyIsAllowed("bucketMaxSize") {
-			return nil, errors.Errorf("OBC config %q is not allowed", "bucketMaxSize")
-		}
 		spec.bucketMaxSize, err = quanityToInt64(config["bucketMaxSize"])
 		if err != nil {
 			return nil, errors.Wrapf(err, "failed to parse bucketMaxSize quota")
@@ -134,25 +121,16 @@ func additionalConfigSpecFromMap(config map[string]string) (*additionalConfigSpe
 	}
 
 	if _, ok := config["bucketPolicy"]; ok {
-		if !opcontroller.ObcAdditionalConfigKeyIsAllowed("bucketPolicy") {
-			return nil, errors.Errorf("OBC config %q is not allowed", "bucketPolicy")
-		}
 		policy := config["bucketPolicy"]
 		spec.bucketPolicy = &policy
 	}
 
 	if _, ok := config["bucketLifecycle"]; ok {
-		if !opcontroller.ObcAdditionalConfigKeyIsAllowed("bucketLifecycle") {
-			return nil, errors.Errorf("OBC config %q is not allowed", "bucketLifecycle")
-		}
 		lifecycle := config["bucketLifecycle"]
 		spec.bucketLifecycle = &lifecycle
 	}
 
 	if _, ok := config["bucketOwner"]; ok {
-		if !opcontroller.ObcAdditionalConfigKeyIsAllowed("bucketOwner") {
-			return nil, errors.Errorf("OBC config %q is not allowed", "bucketOwner")
-		}
 		bucketOwner := config["bucketOwner"]
 		spec.bucketOwner = &bucketOwner
 	}
diff --git a/pkg/operator/ceph/object/objectstore.go b/pkg/operator/ceph/object/objectstore.go
index 67a41626a..080367731 100644
--- a/pkg/operator/ceph/object/objectstore.go
+++ b/pkg/operator/ceph/object/objectstore.go
@@ -1137,10 +1137,10 @@ func poolName(poolPrefix, poolName string) string {
 }
 
 // GetObjectBucketProvisioner returns the bucket provisioner name appended with operator namespace if OBC is watching on it
-func GetObjectBucketProvisioner(namespace string) (string, error) {
+func GetObjectBucketProvisioner(data map[string]string, namespace string) (string, error) {
 	provName := bucketProvisionerName
-	obcWatchOnNamespace := k8sutil.GetOperatorSetting("ROOK_OBC_WATCH_OPERATOR_NAMESPACE", "false")
-	obcProvisionerNamePrefix := k8sutil.GetOperatorSetting("ROOK_OBC_PROVISIONER_NAME_PREFIX", "")
+	obcWatchOnNamespace := k8sutil.GetValue(data, "ROOK_OBC_WATCH_OPERATOR_NAMESPACE", "false")
+	obcProvisionerNamePrefix := k8sutil.GetValue(data, "ROOK_OBC_PROVISIONER_NAME_PREFIX", "")
 	if obcProvisionerNamePrefix != "" {
 		errList := validation.IsDNS1123Label(obcProvisionerNamePrefix)
 		if len(errList) > 0 {
diff --git a/pkg/operator/ceph/operator.go b/pkg/operator/ceph/operator.go
index 2674c41ab..e43ba8c78 100644
--- a/pkg/operator/ceph/operator.go
+++ b/pkg/operator/ceph/operator.go
@@ -129,11 +129,6 @@ func (o *Operator) runCRDManager() {
 	// Create the context and the cancellation function
 	opManagerContext, opManagerStop = context.WithCancel(context.Background())
 
-	// Initialize the operator settings from the configmap
-	if err := k8sutil.ApplyOperatorSettingsConfigmap(opManagerContext, o.context.Clientset); err != nil {
-		panic("failed to load operator settings configmap. " + err.Error())
-	}
-
 	// The operator config manager is also watching for changes here so if the operator config map
 	// content changes for ROOK_CURRENT_NAMESPACE_ONLY we must reload the operator CRD manager
 	o.namespaceToWatch()
@@ -154,7 +149,7 @@ func (o *Operator) runCRDManager() {
 }
 
 func (o *Operator) namespaceToWatch() {
-	currentNamespaceOnly := k8sutil.GetOperatorSetting("ROOK_CURRENT_NAMESPACE_ONLY", "true")
+	currentNamespaceOnly, _ := k8sutil.GetOperatorSetting(opManagerContext, o.context.Clientset, opcontroller.OperatorSettingConfigMapName, "ROOK_CURRENT_NAMESPACE_ONLY", "true")
 	if currentNamespaceOnly == "true" {
 		o.config.NamespaceToWatch = o.config.OperatorNamespace
 		logger.Infof("watching the current namespace %q for Ceph CRs", o.config.OperatorNamespace)
diff --git a/pkg/operator/discover/discover.go b/pkg/operator/discover/discover.go
index dca033d57..5b73b8cc0 100644
--- a/pkg/operator/discover/discover.go
+++ b/pkg/operator/discover/discover.go
@@ -73,16 +73,16 @@ func New(clientset kubernetes.Interface) *Discover {
 }
 
 // Start the discover
-func (d *Discover) Start(ctx context.Context, namespace, discoverImage, securityAccount string, useCephVolume bool) error {
-	err := d.createDiscoverDaemonSet(ctx, namespace, discoverImage, securityAccount, useCephVolume)
+func (d *Discover) Start(ctx context.Context, namespace, discoverImage, securityAccount string, data map[string]string, useCephVolume bool) error {
+	err := d.createDiscoverDaemonSet(ctx, namespace, discoverImage, securityAccount, data, useCephVolume)
 	if err != nil {
 		return fmt.Errorf("failed to start discover daemonset. %v", err)
 	}
 	return nil
 }
 
-func (d *Discover) createDiscoverDaemonSet(ctx context.Context, namespace, discoverImage, securityAccount string, useCephVolume bool) error {
-	discoveryInterval := k8sutil.GetOperatorSetting(discoverIntervalEnv, defaultDiscoverInterval)
+func (d *Discover) createDiscoverDaemonSet(ctx context.Context, namespace, discoverImage, securityAccount string, data map[string]string, useCephVolume bool) error {
+	discoveryInterval := k8sutil.GetValue(data, discoverIntervalEnv, defaultDiscoverInterval)
 
 	discoveryParameters := []string{"discover",
 		"--discover-interval", discoveryInterval}
@@ -90,7 +90,7 @@ func (d *Discover) createDiscoverDaemonSet(ctx context.Context, namespace, disco
 		discoveryParameters = append(discoveryParameters, "--use-ceph-volume")
 	}
 
-	discoverDaemonResourcesRaw := k8sutil.GetOperatorSetting(discoverDaemonResourcesEnv, "")
+	discoverDaemonResourcesRaw := k8sutil.GetValue(data, discoverDaemonResourcesEnv, "")
 	discoverDaemonResources, err := k8sutil.YamlToContainerResource(discoverDaemonResourcesRaw)
 	if err != nil {
 		logger.Warningf("failed to parse.%s %v", discoverDaemonResourcesRaw, err)
@@ -176,7 +176,7 @@ func (d *Discover) createDiscoverDaemonSet(ctx context.Context, namespace, disco
 						},
 					},
 					HostNetwork:       opcontroller.EnforceHostNetwork(),
-					PriorityClassName: k8sutil.GetOperatorSetting(discoverDaemonsetPriorityClassNameEnv, ""),
+					PriorityClassName: k8sutil.GetValue(data, discoverDaemonsetPriorityClassNameEnv, ""),
 					SecurityContext:   &v1.PodSecurityContext{},
 				},
 			},
@@ -191,18 +191,18 @@ func (d *Discover) createDiscoverDaemonSet(ctx context.Context, namespace, disco
 	}
 
 	// Add toleration if any
-	tolerationValue := k8sutil.GetOperatorSetting(discoverDaemonsetTolerationEnv, "")
+	tolerationValue := k8sutil.GetValue(data, discoverDaemonsetTolerationEnv, "")
 	if tolerationValue != "" {
 		ds.Spec.Template.Spec.Tolerations = []v1.Toleration{
 			{
 				Effect:   v1.TaintEffect(tolerationValue),
 				Operator: v1.TolerationOpExists,
-				Key:      k8sutil.GetOperatorSetting(discoverDaemonsetTolerationKeyEnv, ""),
+				Key:      k8sutil.GetValue(data, discoverDaemonsetTolerationKeyEnv, ""),
 			},
 		}
 	}
 
-	tolerationsRaw := k8sutil.GetOperatorSetting(discoverDaemonsetTolerationsEnv, "")
+	tolerationsRaw := k8sutil.GetValue(data, discoverDaemonsetTolerationsEnv, "")
 	tolerations, err := k8sutil.YamlToTolerations(tolerationsRaw)
 	if err != nil {
 		logger.Warningf("failed to parse %s. %+v", tolerationsRaw, err)
@@ -211,7 +211,7 @@ func (d *Discover) createDiscoverDaemonSet(ctx context.Context, namespace, disco
 	ds.Spec.Template.Spec.Tolerations = append(ds.Spec.Template.Spec.Tolerations, tolerations...)
 
 	// Add NodeAffinity if any
-	nodeAffinity := k8sutil.GetOperatorSetting(discoverDaemonSetNodeAffinityEnv, "")
+	nodeAffinity := k8sutil.GetValue(data, discoverDaemonSetNodeAffinityEnv, "")
 	if nodeAffinity != "" {
 		v1NodeAffinity, err := k8sutil.GenerateNodeAffinity(nodeAffinity)
 		if err != nil {
@@ -224,7 +224,7 @@ func (d *Discover) createDiscoverDaemonSet(ctx context.Context, namespace, disco
 		logger.Infof("nodeAffinity: %s", v1NodeAffinity)
 	}
 
-	podLabels := k8sutil.GetOperatorSetting(discoverDaemonSetPodLabelsEnv, "")
+	podLabels := k8sutil.GetValue(data, discoverDaemonSetPodLabelsEnv, "")
 	if podLabels != "" {
 		podLabels := k8sutil.ParseStringToLabels(podLabels)
 		// Override / Set the app label even if set by the user as
diff --git a/pkg/operator/k8sutil/configmap.go b/pkg/operator/k8sutil/configmap.go
index 7e57bb7d7..08b932430 100644
--- a/pkg/operator/k8sutil/configmap.go
+++ b/pkg/operator/k8sutil/configmap.go
@@ -20,17 +20,17 @@ import (
 	"context"
 	"fmt"
 	"os"
+	"sync"
 	"time"
 
 	"github.com/pkg/errors"
 	v1 "k8s.io/api/core/v1"
 	kerrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/client-go/kubernetes"
 )
 
-var loadedOperatorSettings bool
+var loggedOperatorSettings = sync.Map{}
 
 // DeleteConfigMap deletes a ConfigMap.
 func DeleteConfigMap(ctx context.Context, clientset kubernetes.Interface, cmName, namespace string, opts *DeleteOptions) error {
@@ -70,43 +70,50 @@ func CreateOrUpdateConfigMap(ctx context.Context, clientset kubernetes.Interface
 	return existingCm, nil
 }
 
-// GetOperatorSetting gets the operator setting from Env Var merged with ConfigMap
+// GetOperatorSetting gets the operator setting from ConfigMap or Env Var
 // returns defaultValue if setting is not found
-func GetOperatorSetting(settingName, defaultValue string) string {
-	if !loadedOperatorSettings {
-		logger.Warningf("WARNING: attempting to load operator setting %q before configmap is loaded", settingName)
-	}
-	if settingValue, ok := os.LookupEnv(settingName); ok {
-		return settingValue
+func GetOperatorSetting(context context.Context, clientset kubernetes.Interface, configMapName, settingName, defaultValue string) (string, error) {
+	// config must be in operator pod namespace
+	namespace := os.Getenv(PodNamespaceEnvVar)
+	cm, err := clientset.CoreV1().ConfigMaps(namespace).Get(context, configMapName, metav1.GetOptions{})
+	if err != nil {
+		if kerrors.IsNotFound(err) {
+			if settingValue, ok := os.LookupEnv(settingName); ok {
+				logger.Infof("%s=%q (env var)", settingName, settingValue)
+				return settingValue, nil
+			}
+			logger.Infof("%s=%q (default)", settingName, defaultValue)
+			return defaultValue, nil
+		}
+		return defaultValue, fmt.Errorf("error reading ConfigMap %q. %v", configMapName, err)
 	}
-	return defaultValue
+
+	return GetValue(cm.Data, settingName, defaultValue), nil
 }
 
-func ApplyOperatorSettingsConfigmap(ctx context.Context, clientset kubernetes.Interface) error {
-	namespacedName := types.NamespacedName{Namespace: os.Getenv(PodNamespaceEnvVar), Name: "rook-ceph-operator-config"}
-	logger.Debugf("loading operator settings configmap from %v", namespacedName)
-	opConfig, err := clientset.CoreV1().ConfigMaps(namespacedName.Namespace).Get(ctx, namespacedName.Name, metav1.GetOptions{})
+func GetValue(data map[string]string, settingName, defaultValue string) string {
+	settingValue := defaultValue
+	settingSource := "default"
 
-	if err != nil {
-		if kerrors.IsNotFound(err) {
-			loadedOperatorSettings = true
-			logger.Debug("operator's configmap resource not found. will use default value or env var.")
-			return nil
-		}
-		return err
+	if val, ok := data[settingName]; ok {
+		settingValue = val
+		settingSource = "configmap"
+	} else if val, ok := os.LookupEnv(settingName); ok {
+		settingValue = val
+		settingSource = "env var"
 	}
 
-	for key, value := range opConfig.Data {
-		currentValue := os.Getenv(key)
-		if currentValue == value {
-			continue
-		}
-		logger.Infof("operator setting %q = %q", key, value)
-		if err := os.Setenv(key, value); err != nil {
-			logger.Errorf("failed to set env var %q = %q. %v", key, value, err)
-		}
+	if logChangedSettings(settingName, settingValue) {
+		logger.Infof("%s=%q (%s)", settingName, settingValue, settingSource)
+	}
+
+	return settingValue
+}
+
+func logChangedSettings(settingName, settingValue string) bool {
+	if val, ok := loggedOperatorSettings.Load(settingName); !ok || val != settingValue {
+		loggedOperatorSettings.Store(settingName, settingValue)
+		return true
 	}
-	loadedOperatorSettings = true
-	logger.Debug("done loading operator settings configmap")
-	return nil
+	return false
 }


Test patch:
diff --git a/.github/workflows/canary-integration-test.yml b/.github/workflows/canary-integration-test.yml
index dd31d10fc..5e0ab7f7f 100644
--- a/.github/workflows/canary-integration-test.yml
+++ b/.github/workflows/canary-integration-test.yml
@@ -1644,6 +1644,7 @@ jobs:
         uses: ./.github/workflows/collect-logs
         with:
           name: ${{ github.job }}-${{ matrix.ceph-image }}
+          additional-namespace: rook-ceph-secondary
 
   encryption-pvc-kms-ibm-kp:
     runs-on: ubuntu-22.04
diff --git a/.github/workflows/integration-test-helm-suite.yaml b/.github/workflows/integration-test-helm-suite.yaml
index f5ec52506..5a847d84c 100644
--- a/.github/workflows/integration-test-helm-suite.yaml
+++ b/.github/workflows/integration-test-helm-suite.yaml
@@ -65,7 +65,7 @@ jobs:
           tests/scripts/collect-logs.sh
 
       - name: Artifact
-        uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         if: failure()
         with:
           name: ceph-helm-suite-artifact-${{ matrix.kubernetes-versions }}
diff --git a/.github/workflows/integration-test-keystone-auth-suite.yaml b/.github/workflows/integration-test-keystone-auth-suite.yaml
index 34c2ed6f6..dd7aa3ebb 100644
--- a/.github/workflows/integration-test-keystone-auth-suite.yaml
+++ b/.github/workflows/integration-test-keystone-auth-suite.yaml
@@ -24,7 +24,7 @@ concurrency:
 jobs:
   TestCephKeystoneAuthSuite:
     if: ${{ github.event_name == 'pull_request' && github.ref != 'refs/heads/master' && !contains(github.event.pull_request.labels.*.name, 'skip-ci') }}
-    runs-on: ubuntu-22.04
+    runs-on: ubuntu-20.04
     strategy:
       fail-fast: false
       matrix:
@@ -63,7 +63,7 @@ jobs:
           tests/scripts/collect-logs.sh
 
       - name: Artifact
-        uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         if: failure()
         with:
           name: ceph-keystone-auth-suite-artifact-${{ matrix.kubernetes-versions }}
diff --git a/.github/workflows/integration-test-mgr-suite.yaml b/.github/workflows/integration-test-mgr-suite.yaml
index 1eda6481e..2b2f2de38 100644
--- a/.github/workflows/integration-test-mgr-suite.yaml
+++ b/.github/workflows/integration-test-mgr-suite.yaml
@@ -60,7 +60,7 @@ jobs:
           tests/scripts/collect-logs.sh
 
       - name: Artifact
-        uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         if: failure()
         with:
           name: ceph-mgr-suite-artifact-${{ matrix.kubernetes-versions }}
diff --git a/.github/workflows/integration-test-multi-cluster-suite.yaml b/.github/workflows/integration-test-multi-cluster-suite.yaml
index f2643dfd3..3604195dd 100644
--- a/.github/workflows/integration-test-multi-cluster-suite.yaml
+++ b/.github/workflows/integration-test-multi-cluster-suite.yaml
@@ -64,7 +64,7 @@ jobs:
           CLUSTER_NAMESPACE="multi-external" tests/scripts/collect-logs.sh
 
       - name: Artifact
-        uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         if: failure()
         with:
           name: ceph-multi-cluster-deploy-suite-artifact-${{ matrix.kubernetes-versions }}
diff --git a/.github/workflows/integration-test-object-suite.yaml b/.github/workflows/integration-test-object-suite.yaml
index 92127a4e1..98c7b5ada 100644
--- a/.github/workflows/integration-test-object-suite.yaml
+++ b/.github/workflows/integration-test-object-suite.yaml
@@ -61,7 +61,7 @@ jobs:
           tests/scripts/collect-logs.sh
 
       - name: Artifact
-        uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         if: failure()
         with:
           name: ceph-object-suite-artifact-${{ matrix.kubernetes-versions }}
diff --git a/.github/workflows/integration-test-smoke-suite.yaml b/.github/workflows/integration-test-smoke-suite.yaml
index b865f04d5..d7822dd13 100644
--- a/.github/workflows/integration-test-smoke-suite.yaml
+++ b/.github/workflows/integration-test-smoke-suite.yaml
@@ -61,7 +61,7 @@ jobs:
           tests/scripts/collect-logs.sh
 
       - name: Artifact
-        uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         if: failure()
         with:
           name: ceph-smoke-suite-artifact-${{ matrix.kubernetes-versions }}
diff --git a/.github/workflows/integration-test-upgrade-suite.yaml b/.github/workflows/integration-test-upgrade-suite.yaml
index 10ba2772c..16344d8d1 100644
--- a/.github/workflows/integration-test-upgrade-suite.yaml
+++ b/.github/workflows/integration-test-upgrade-suite.yaml
@@ -61,7 +61,7 @@ jobs:
           tests/scripts/collect-logs.sh
 
       - name: Artifact
-        uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         if: failure()
         with:
           name: ceph-upgrade-suite-artifact-${{ matrix.kubernetes-versions }}
@@ -109,7 +109,7 @@ jobs:
           tests/scripts/collect-logs.sh
 
       - name: Artifact
-        uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         if: failure()
         with:
           name: ceph-upgrade-helm-suite-artifact-${{ matrix.kubernetes-versions }}
diff --git a/.github/workflows/integration-tests-on-release.yaml b/.github/workflows/integration-tests-on-release.yaml
index e62cc10bc..2ae2efac9 100644
--- a/.github/workflows/integration-tests-on-release.yaml
+++ b/.github/workflows/integration-tests-on-release.yaml
@@ -50,7 +50,7 @@ jobs:
           tests/scripts/collect-logs.sh
 
       - name: Artifact
-        uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         if: failure()
         with:
           name: ceph-helm-suite-artifact-${{ matrix.kubernetes-versions }}
@@ -91,7 +91,7 @@ jobs:
           CLUSTER_NAMESPACE="multi-external" tests/scripts/collect-logs.sh
 
       - name: Artifact
-        uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         if: failure()
         with:
           name: ceph-multi-cluster-deploy-suite-artifact-${{ matrix.kubernetes-versions }}
@@ -129,7 +129,7 @@ jobs:
           tests/scripts/collect-logs.sh
 
       - name: Artifact
-        uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         if: failure()
         with:
           name: ceph-smoke-suite-artifact-${{ matrix.kubernetes-versions }}
@@ -167,7 +167,7 @@ jobs:
           tests/scripts/collect-logs.sh
 
       - name: Artifact
-        uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         if: failure()
         with:
           name: ceph-upgrade-suite-artifact-${{ matrix.kubernetes-versions }}
@@ -208,7 +208,7 @@ jobs:
           tests/scripts/collect-logs.sh
 
       - name: Artifact
-        uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         if: failure()
         with:
           name: ceph-upgrade-suite-artifact-${{ matrix.kubernetes-versions }}
@@ -246,7 +246,7 @@ jobs:
           tests/scripts/collect-logs.sh
 
       - name: Artifact
-        uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         if: failure()
         with:
           name: ceph-object-suite-artifact-${{ matrix.kubernetes-versions }}
diff --git a/deploy/examples/cluster-test.yaml b/deploy/examples/cluster-test.yaml
index f2012328e..bde8e182e 100644
--- a/deploy/examples/cluster-test.yaml
+++ b/deploy/examples/cluster-test.yaml
@@ -20,8 +20,6 @@ spec:
   mon:
     count: 1
     allowMultiplePerNode: true
-  # test environments can skip ok-to-stop checks during upgrades
-  skipUpgradeChecks: true
   mgr:
     count: 1
     allowMultiplePerNode: true
diff --git a/pkg/daemon/ceph/client/upgrade_test.go b/pkg/daemon/ceph/client/upgrade_test.go
index 2c72b0dae..45329915c 100644
--- a/pkg/daemon/ceph/client/upgrade_test.go
+++ b/pkg/daemon/ceph/client/upgrade_test.go
@@ -323,7 +323,7 @@ func TestOSDUpdateShouldCheckOkToStop(t *testing.T) {
 	t.Run("1 node with 3 OSDs", func(t *testing.T) {
 		lsOutput = fake.OsdLsOutput(3)
 		treeOutput = fake.OsdTreeOutput(1, 3)
-		assert.True(t, OSDUpdateShouldCheckOkToStop(context, clusterInfo))
+		assert.False(t, OSDUpdateShouldCheckOkToStop(context, clusterInfo))
 	})
 
 	t.Run("2 nodes with 1 OSD each", func(t *testing.T) {
@@ -349,6 +349,6 @@ func TestOSDUpdateShouldCheckOkToStop(t *testing.T) {
 	t.Run("0 nodes with down OSDs", func(t *testing.T) {
 		lsOutput = fake.OsdLsOutput(3)
 		treeOutput = fake.OsdTreeOutput(0, 1)
-		assert.True(t, OSDUpdateShouldCheckOkToStop(context, clusterInfo))
+		assert.False(t, OSDUpdateShouldCheckOkToStop(context, clusterInfo))
 	})
 }
diff --git a/pkg/operator/ceph/controller/controller_utils_test.go b/pkg/operator/ceph/controller/controller_utils_test.go
index 6725c6e22..82774c8c7 100644
--- a/pkg/operator/ceph/controller/controller_utils_test.go
+++ b/pkg/operator/ceph/controller/controller_utils_test.go
@@ -18,7 +18,6 @@ package controller
 
 import (
 	ctx "context"
-	"os"
 	"testing"
 	"time"
 
@@ -46,11 +45,7 @@ func TestCanIgnoreHealthErrStatusInReconcile(t *testing.T) {
 	var cluster = CreateTestClusterFromStatusDetails(map[string]cephv1.CephHealthMessage{
 		"MDS_ALL_DOWN": {
 			Severity: "HEALTH_ERR",
-			Message:  "mds all down error",
-		},
-		"MGR_MODULE_ERROR": {
-			Severity: "HEALTH_ERR",
-			Message:  "mgr module error",
+			Message:  "MDS_ALL_DOWN",
 		},
 		"TEST_OTHER": {
 			Severity: "HEALTH_WARN",
@@ -63,22 +58,6 @@ func TestCanIgnoreHealthErrStatusInReconcile(t *testing.T) {
 	})
 	assert.True(t, canIgnoreHealthErrStatusInReconcile(cluster, "controller"))
 
-	cluster = CreateTestClusterFromStatusDetails(map[string]cephv1.CephHealthMessage{
-		"MGR_MODULE_ERROR": {
-			Severity: "HEALTH_ERR",
-			Message:  "mgr module error",
-		},
-	})
-	assert.True(t, canIgnoreHealthErrStatusInReconcile(cluster, "controller"))
-
-	cluster = CreateTestClusterFromStatusDetails(map[string]cephv1.CephHealthMessage{
-		"MGR_MODULE_ERROR_FALSE": {
-			Severity: "HEALTH_ERR",
-			Message:  "mgr module error",
-		},
-	})
-	assert.False(t, canIgnoreHealthErrStatusInReconcile(cluster, "controller"))
-
 	cluster = CreateTestClusterFromStatusDetails(map[string]cephv1.CephHealthMessage{
 		"MDS_ALL_DOWN": {
 			Severity: "HEALTH_ERR",
@@ -98,101 +77,86 @@ func TestCanIgnoreHealthErrStatusInReconcile(t *testing.T) {
 		},
 	})
 	assert.False(t, canIgnoreHealthErrStatusInReconcile(cluster, "controller"))
-
-	// The empty status should return false
-	cluster = CreateTestClusterFromStatusDetails(map[string]cephv1.CephHealthMessage{})
-	assert.False(t, canIgnoreHealthErrStatusInReconcile(cluster, "controller"))
 }
 
 func TestSetCephCommandsTimeout(t *testing.T) {
-	defer os.Unsetenv("ROOK_CEPH_COMMANDS_TIMEOUT_SECONDS")
-
-	SetCephCommandsTimeout()
+	SetCephCommandsTimeout(map[string]string{})
 	assert.Equal(t, 15*time.Second, exec.CephCommandsTimeout)
 
 	exec.CephCommandsTimeout = 0
-	os.Setenv("ROOK_CEPH_COMMANDS_TIMEOUT_SECONDS", "0")
-	SetCephCommandsTimeout()
+	SetCephCommandsTimeout(map[string]string{"ROOK_CEPH_COMMANDS_TIMEOUT_SECONDS": "0"})
 	assert.Equal(t, 15*time.Second, exec.CephCommandsTimeout)
 
 	exec.CephCommandsTimeout = 0
-	os.Setenv("ROOK_CEPH_COMMANDS_TIMEOUT_SECONDS", "1")
-	SetCephCommandsTimeout()
+	SetCephCommandsTimeout(map[string]string{"ROOK_CEPH_COMMANDS_TIMEOUT_SECONDS": "1"})
 	assert.Equal(t, 1*time.Second, exec.CephCommandsTimeout)
 }
 
 func TestSetAllowLoopDevices(t *testing.T) {
-	defer os.Unsetenv("ROOK_CEPH_ALLOW_LOOP_DEVICES")
-
-	SetAllowLoopDevices()
+	SetAllowLoopDevices(map[string]string{})
 	assert.False(t, LoopDevicesAllowed())
 
-	os.Setenv("ROOK_CEPH_ALLOW_LOOP_DEVICES", "foo")
-	SetAllowLoopDevices()
+	SetAllowLoopDevices(map[string]string{"ROOK_CEPH_ALLOW_LOOP_DEVICES": "foo"})
 	assert.False(t, LoopDevicesAllowed())
 
-	os.Setenv("ROOK_CEPH_ALLOW_LOOP_DEVICES", "false")
-	SetAllowLoopDevices()
+	SetAllowLoopDevices(map[string]string{"ROOK_CEPH_ALLOW_LOOP_DEVICES": "false"})
 	assert.False(t, LoopDevicesAllowed())
 
-	os.Setenv("ROOK_CEPH_ALLOW_LOOP_DEVICES", "true")
-	SetAllowLoopDevices()
+	SetAllowLoopDevices(map[string]string{"ROOK_CEPH_ALLOW_LOOP_DEVICES": "true"})
 	assert.True(t, LoopDevicesAllowed())
 }
 
 func TestSetEnforceHostNetwork(t *testing.T) {
-	defer os.Unsetenv(enforceHostNetworkSettingName)
-
 	logger.Infof("testing default value for %v", enforceHostNetworkSettingName)
-	SetEnforceHostNetwork()
+	opConfig := map[string]string{}
+	SetEnforceHostNetwork(opConfig)
 	assert.False(t, EnforceHostNetwork())
 
 	// test invalid setting
 	var value string = "foo"
 	logger.Infof("testing invalid value'%v' for %v", value, enforceHostNetworkSettingName)
-	os.Setenv(enforceHostNetworkSettingName, value)
-	SetEnforceHostNetwork()
+	opConfig[enforceHostNetworkSettingName] = value
+	SetEnforceHostNetwork(opConfig)
 	assert.False(t, EnforceHostNetwork())
 
 	// test valid settings
 	value = "true"
 	logger.Infof("testing valid value'%v' for %v", value, enforceHostNetworkSettingName)
-	os.Setenv(enforceHostNetworkSettingName, value)
-	SetEnforceHostNetwork()
+	opConfig[enforceHostNetworkSettingName] = value
+	SetEnforceHostNetwork(opConfig)
 	assert.True(t, EnforceHostNetwork())
 
 	value = "false"
 	logger.Infof("testing valid value'%v' for %v", value, enforceHostNetworkSettingName)
-	os.Setenv(enforceHostNetworkSettingName, value)
-	SetEnforceHostNetwork()
+	opConfig[enforceHostNetworkSettingName] = value
+	SetEnforceHostNetwork(opConfig)
 	assert.False(t, EnforceHostNetwork())
 }
 
 func TestSetRevisionHistoryLimit(t *testing.T) {
-	defer os.Unsetenv(revisionHistoryLimitSettingName)
-
+	opConfig := map[string]string{}
 	t.Run("ROOK_REVISION_HISTORY_LIMIT: test default value", func(t *testing.T) {
-		SetRevisionHistoryLimit()
+		SetRevisionHistoryLimit(opConfig)
 		assert.Nil(t, RevisionHistoryLimit())
 	})
 
 	var value string = "foo"
 	t.Run("ROOK_REVISION_HISTORY_LIMIT: test invalid value 'foo'", func(t *testing.T) {
-		os.Setenv(revisionHistoryLimitSettingName, value)
-		SetRevisionHistoryLimit()
+		opConfig[revisionHistoryLimitSettingName] = value
+		SetRevisionHistoryLimit(opConfig)
 		assert.Nil(t, RevisionHistoryLimit())
 	})
 
 	t.Run("ROOK_REVISION_HISTORY_LIMIT: test empty string value", func(t *testing.T) {
 		value = ""
-		os.Setenv(revisionHistoryLimitSettingName, value)
-		SetRevisionHistoryLimit()
+		opConfig[revisionHistoryLimitSettingName] = value
+		SetRevisionHistoryLimit(opConfig)
 		assert.Nil(t, RevisionHistoryLimit())
 	})
 	t.Run("ROOK_REVISION_HISTORY_LIMIT:  test valig value '10'", func(t *testing.T) {
 		value = "10"
-		os.Setenv(revisionHistoryLimitSettingName, value)
-		SetRevisionHistoryLimit()
+		opConfig[revisionHistoryLimitSettingName] = value
+		SetRevisionHistoryLimit(opConfig)
 		assert.Equal(t, int32(10), *RevisionHistoryLimit())
 	})
 }
@@ -282,48 +246,3 @@ func TestIsReadyToReconcile(t *testing.T) {
 		assert.False(t, clusterExists)
 	})
 }
-
-func TestObcAllowAdditionalConfigFields(t *testing.T) {
-	tests := []struct {
-		name             string
-		additionalConfig string
-		shouldAllow      []string
-		shouldDisallow   []string
-	}{
-		{"not set", "<notset>",
-			[]string{"maxObjects", "maxSize"}, // default allowlist is unlikely to need changing EVER
-			[]string{"bucketMaxObjects", "bucketMaxSize", "bucketPolicy", "bucketLifecycle", "bucketOwner", "random"}},
-		{"set to empty", "",
-			[]string{}, // admin can allow no quota options if desired
-			[]string{"maxObjects", "maxSize", "bucketMaxObjects", "bucketMaxSize", "bucketPolicy", "bucketLifecycle", "bucketOwner", "random"}},
-		{"set to default", "maxObjects,maxSize",
-			[]string{"maxObjects", "maxSize"},
-			[]string{"bucketMaxObjects", "bucketMaxSize", "bucketPolicy", "bucketLifecycle", "bucketOwner", "random"}},
-		{"all quota fields", "maxObjects,maxSize,bucketMaxObjects,bucketMaxSize",
-			[]string{"maxObjects", "maxSize", "bucketMaxObjects", "bucketMaxSize"},
-			[]string{"bucketPolicy", "bucketLifecycle", "bucketOwner", "random"}},
-		{"all fields", "maxObjects,maxSize,bucketMaxObjects,bucketMaxSize,bucketPolicy,bucketLifecycle,bucketOwner",
-			[]string{"maxObjects", "maxSize", "bucketMaxObjects", "bucketMaxSize", "bucketPolicy", "bucketLifecycle", "bucketOwner"},
-			[]string{"random"}},
-		// this mechanism doesn't do any field checking - that isn't it's job - it merely handles
-		// allow-listing essentially arbitrary config keys
-		{"all fields including unknown", "maxObjects,maxSize,bucketMaxObjects,bucketMaxSize,bucketPolicy,bucketLifecycle,bucketOwner,random",
-			[]string{"maxObjects", "maxSize", "bucketMaxObjects", "bucketMaxSize", "bucketPolicy", "bucketLifecycle", "bucketOwner", "random"},
-			[]string{"otherRandom"}},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			if tt.additionalConfig != "<notset>" {
-				os.Setenv("ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS", tt.additionalConfig)
-				defer os.Unsetenv("ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS")
-			}
-			SetObcAllowAdditionalConfigFields()
-			for _, toAllow := range tt.shouldAllow {
-				assert.True(t, ObcAdditionalConfigKeyIsAllowed(toAllow))
-			}
-			for _, toDisallow := range tt.shouldDisallow {
-				assert.False(t, ObcAdditionalConfigKeyIsAllowed(toDisallow))
-			}
-		})
-	}
-}
diff --git a/pkg/operator/ceph/controller_test.go b/pkg/operator/ceph/controller_test.go
index a9f08b672..ed4485611 100644
--- a/pkg/operator/ceph/controller_test.go
+++ b/pkg/operator/ceph/controller_test.go
@@ -23,6 +23,7 @@ import (
 	"time"
 
 	"github.com/coreos/pkg/capnslog"
+	cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1"
 	rookclient "github.com/rook/rook/pkg/client/clientset/versioned/fake"
 	"github.com/rook/rook/pkg/client/clientset/versioned/scheme"
 	"github.com/rook/rook/pkg/clusterd"
@@ -30,7 +31,9 @@ import (
 	"github.com/rook/rook/pkg/operator/test"
 	"github.com/rook/rook/pkg/util/exec"
 	"github.com/stretchr/testify/assert"
+	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/types"
 	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
 	"sigs.k8s.io/controller-runtime/pkg/client/fake"
@@ -61,6 +64,7 @@ func TestOperatorController(t *testing.T) {
 
 		// Register operator types with the runtime scheme.
 		s := scheme.Scheme
+		s.AddKnownTypes(cephv1.SchemeGroupVersion, &v1.ConfigMap{}, &v1.ConfigMapList{})
 
 		// Create a fake client to mock API calls.
 		cl := fake.NewClientBuilder().WithScheme(s).Build()
@@ -91,6 +95,7 @@ func TestOperatorController(t *testing.T) {
 
 		// Register operator types with the runtime scheme.
 		s := scheme.Scheme
+		s.AddKnownTypes(cephv1.SchemeGroupVersion, &v1.ConfigMap{}, &v1.ConfigMapList{})
 
 		// Create a fake client to mock API calls.
 		cl := fake.NewClientBuilder().WithScheme(s).Build()
@@ -123,12 +128,23 @@ func TestOperatorController(t *testing.T) {
 		// Register operator types with the runtime scheme.
 		// s := scheme.Scheme
 		s := clientgoscheme.Scheme
+		// s.AddKnownTypes(cephv1.SchemeGroupVersion, &v1.ConfigMap{})
 
-		os.Setenv("ROOK_CEPH_COMMANDS_TIMEOUT_SECONDS", "20")
-		defer os.Unsetenv("ROOK_CEPH_COMMANDS_TIMEOUT_SECONDS")
+		opConfigCM := &v1.ConfigMap{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      controller.OperatorSettingConfigMapName,
+				Namespace: namespace,
+			},
+			Data: map[string]string{
+				"ROOK_CEPH_COMMANDS_TIMEOUT_SECONDS": "20",
+			},
+		}
 
+		object := []runtime.Object{
+			opConfigCM,
+		}
 		// Create a fake client to mock API calls.
-		cl := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects().Build()
+		cl := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(object...).Build()
 
 		// Create a ReconcileCSI object with the scheme and fake client.
 		r := &ReconcileConfig{
@@ -157,6 +173,7 @@ func TestOperatorController(t *testing.T) {
 
 		// Register operator types with the runtime scheme.
 		s := scheme.Scheme
+		s.AddKnownTypes(cephv1.SchemeGroupVersion, &v1.ConfigMap{}, &v1.ConfigMapList{})
 
 		// Create a fake client to mock API calls.
 		cl := fake.NewClientBuilder().WithScheme(s).Build()
@@ -192,12 +209,23 @@ func TestOperatorController(t *testing.T) {
 		// Register operator types with the runtime scheme.
 		// s := scheme.Scheme
 		s := clientgoscheme.Scheme
+		// s.AddKnownTypes(cephv1.SchemeGroupVersion, &v1.ConfigMap{})
 
-		os.Setenv("ROOK_CEPH_ALLOW_LOOP_DEVICES", "true")
-		defer os.Unsetenv("ROOK_CEPH_ALLOW_LOOP_DEVICES")
+		opConfigCM := &v1.ConfigMap{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      controller.OperatorSettingConfigMapName,
+				Namespace: namespace,
+			},
+			Data: map[string]string{
+				"ROOK_CEPH_ALLOW_LOOP_DEVICES": "true",
+			},
+		}
 
+		object := []runtime.Object{
+			opConfigCM,
+		}
 		// Create a fake client to mock API calls.
-		cl := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects().Build()
+		cl := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(object...).Build()
 
 		// Create a ReconcileCSI object with the scheme and fake client.
 		r := &ReconcileConfig{
diff --git a/pkg/operator/ceph/csi/util_test.go b/pkg/operator/ceph/csi/util_test.go
index 74860696e..29f5cbced 100644
--- a/pkg/operator/ceph/csi/util_test.go
+++ b/pkg/operator/ceph/csi/util_test.go
@@ -47,27 +47,28 @@ func TestDeploymentTemplate(t *testing.T) {
 func TestGetPortFromConfig(t *testing.T) {
 	key := "TEST_CSI_PORT_ENV"
 	var defaultPort uint16 = 8000
+	data := map[string]string{}
 
 	// empty env variable
-	port, err := getPortFromConfig(key, defaultPort)
+	port, err := getPortFromConfig(data, key, defaultPort)
 	assert.Nil(t, err)
 	assert.Equal(t, port, defaultPort)
 
 	// valid port is set in env
 	t.Setenv(key, "9000")
-	port, err = getPortFromConfig(key, defaultPort)
+	port, err = getPortFromConfig(data, key, defaultPort)
 	assert.Nil(t, err)
 	assert.Equal(t, port, uint16(9000))
 
 	// higher port value is set in env
 	t.Setenv(key, "65536")
-	port, err = getPortFromConfig(key, defaultPort)
+	port, err = getPortFromConfig(data, key, defaultPort)
 	assert.Error(t, err)
 	assert.Equal(t, port, defaultPort)
 
 	// negative port is set in env
 	t.Setenv(key, "-1")
-	port, err = getPortFromConfig(key, defaultPort)
+	port, err = getPortFromConfig(data, key, defaultPort)
 	assert.Error(t, err)
 	assert.Equal(t, port, defaultPort)
 
@@ -79,6 +80,7 @@ func TestApplyingResourcesToRBDPlugin(t *testing.T) {
 	tp := templateParam{}
 	rbdPlugin, err := templateToDaemonSet("rbdplugin", RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
+	params := make(map[string]string)
 
 	// need to build using map[string]interface{} because the following resource
 	// doesn't serialise nicely
@@ -101,9 +103,8 @@ func TestApplyingResourcesToRBDPlugin(t *testing.T) {
 
 	resourceRaw, err := yaml.Marshal(resource)
 	assert.Nil(t, err)
-	os.Setenv(rbdPluginResource, string(resourceRaw))
-	defer os.Unsetenv(rbdPluginResource)
-	applyResourcesToContainers(rbdPluginResource, &rbdPlugin.Spec.Template.Spec)
+	params[rbdPluginResource] = string(resourceRaw)
+	applyResourcesToContainers(params, rbdPluginResource, &rbdPlugin.Spec.Template.Spec)
 	assert.Equal(t, rbdPlugin.Spec.Template.Spec.Containers[0].Resources.Requests.Memory().String(), "128Mi")
 	assert.Equal(t, rbdPlugin.Spec.Template.Spec.Containers[0].Resources.Limits.Memory().String(), "256Mi")
 	assert.Equal(t, rbdPlugin.Spec.Template.Spec.Containers[0].Resources.Requests.Cpu().String(), "100m")
@@ -112,6 +113,7 @@ func TestApplyingResourcesToRBDPlugin(t *testing.T) {
 
 func Test_applyVolumeToPodSpec(t *testing.T) {
 	// when no volumes specified
+	config := make(map[string]string)
 	configKey := "TEST_CSI_PLUGIN_VOLUME"
 	dsName := "test-ds"
 	tp := templateParam{
@@ -122,7 +124,7 @@ func Test_applyVolumeToPodSpec(t *testing.T) {
 	defaultVolumes := 11
 	ds, err := templateToDaemonSet(dsName, RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
-	applyVolumeToPodSpec(configKey, &ds.Spec.Template.Spec)
+	applyVolumeToPodSpec(config, configKey, &ds.Spec.Template.Spec)
 
 	assert.Len(t, ds.Spec.Template.Spec.Volumes, defaultVolumes)
 
@@ -130,7 +132,7 @@ func Test_applyVolumeToPodSpec(t *testing.T) {
 	tp.CSILogRotation = true
 	ds, err = templateToDaemonSet(dsName, RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
-	applyVolumeToPodSpec(configKey, &ds.Spec.Template.Spec)
+	applyVolumeToPodSpec(config, configKey, &ds.Spec.Template.Spec)
 	assert.Len(t, ds.Spec.Template.Spec.Volumes, defaultVolumes+2)
 	tp.CSILogRotation = false
 
@@ -143,11 +145,10 @@ func Test_applyVolumeToPodSpec(t *testing.T) {
 	}
 	volumeRaw, err := yaml.Marshal(volumes)
 	assert.Nil(t, err)
-	os.Setenv(configKey, string(volumeRaw))
-	defer os.Unsetenv(configKey)
+	config[configKey] = string(volumeRaw)
 	ds, err = templateToDaemonSet(dsName, RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
-	applyVolumeToPodSpec(configKey, &ds.Spec.Template.Spec)
+	applyVolumeToPodSpec(config, configKey, &ds.Spec.Template.Spec)
 
 	assert.Len(t, ds.Spec.Template.Spec.Volumes, defaultVolumes+1)
 	// add one more volume
@@ -157,10 +158,10 @@ func Test_applyVolumeToPodSpec(t *testing.T) {
 	})
 	volumeRaw, err = yaml.Marshal(volumes)
 	assert.Nil(t, err)
-	os.Setenv(configKey, string(volumeRaw))
+	config[configKey] = string(volumeRaw)
 	ds, err = templateToDaemonSet(dsName, RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
-	applyVolumeToPodSpec(configKey, &ds.Spec.Template.Spec)
+	applyVolumeToPodSpec(config, configKey, &ds.Spec.Template.Spec)
 	assert.Len(t, ds.Spec.Template.Spec.Volumes, defaultVolumes+2)
 	// override existing volume configuration
 	volumes[1].VolumeSource = corev1.VolumeSource{
@@ -168,10 +169,10 @@ func Test_applyVolumeToPodSpec(t *testing.T) {
 	}
 	volumeRaw, err = yaml.Marshal(volumes)
 	assert.Nil(t, err)
-	os.Setenv(configKey, string(volumeRaw))
+	config[configKey] = string(volumeRaw)
 	ds, err = templateToDaemonSet(dsName, RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
-	applyVolumeToPodSpec(configKey, &ds.Spec.Template.Spec)
+	applyVolumeToPodSpec(config, configKey, &ds.Spec.Template.Spec)
 	assert.Len(t, ds.Spec.Template.Spec.Volumes, defaultVolumes+2)
 	// remove existing volume configuration
 	volumes = []corev1.Volume{
@@ -182,15 +183,16 @@ func Test_applyVolumeToPodSpec(t *testing.T) {
 	}
 	volumeRaw, err = yaml.Marshal(volumes)
 	assert.Nil(t, err)
-	os.Setenv(configKey, string(volumeRaw))
+	config[configKey] = string(volumeRaw)
 	ds, err = templateToDaemonSet(dsName, RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
-	applyVolumeToPodSpec(configKey, &ds.Spec.Template.Spec)
+	applyVolumeToPodSpec(config, configKey, &ds.Spec.Template.Spec)
 	assert.Len(t, ds.Spec.Template.Spec.Volumes, defaultVolumes+1)
 }
 
 func Test_applyVolumeMountToContainer(t *testing.T) {
 	// when no volumes specified
+	config := make(map[string]string)
 	configKey := "TEST_CSI_PLUGIN_VOLUME_MOUNT"
 	dsName := "test-ds"
 	rbdContainerName := "csi-rbdplugin"
@@ -202,7 +204,7 @@ func Test_applyVolumeMountToContainer(t *testing.T) {
 	defaultVolumes := 10
 	ds, err := templateToDaemonSet(dsName, RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
-	applyVolumeMountToContainer(configKey, rbdContainerName, &ds.Spec.Template.Spec)
+	applyVolumeMountToContainer(config, configKey, rbdContainerName, &ds.Spec.Template.Spec)
 
 	assert.Len(t, ds.Spec.Template.Spec.Containers[1].VolumeMounts, defaultVolumes)
 
@@ -210,7 +212,7 @@ func Test_applyVolumeMountToContainer(t *testing.T) {
 	tp.CSILogRotation = true
 	ds, err = templateToDaemonSet(dsName, RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
-	applyVolumeMountToContainer(configKey, rbdContainerName, &ds.Spec.Template.Spec)
+	applyVolumeMountToContainer(config, configKey, rbdContainerName, &ds.Spec.Template.Spec)
 	assert.Len(t, ds.Spec.Template.Spec.Containers[1].VolumeMounts, defaultVolumes+1)
 	tp.CSILogRotation = false
 
@@ -223,11 +225,10 @@ func Test_applyVolumeMountToContainer(t *testing.T) {
 	}
 	volumeMountsRaw, err := yaml.Marshal(volumeMounts)
 	assert.Nil(t, err)
-	os.Setenv(configKey, string(volumeMountsRaw))
-	defer os.Unsetenv(configKey)
+	config[configKey] = string(volumeMountsRaw)
 	ds, err = templateToDaemonSet(dsName, RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
-	applyVolumeMountToContainer(configKey, rbdContainerName, &ds.Spec.Template.Spec)
+	applyVolumeMountToContainer(config, configKey, rbdContainerName, &ds.Spec.Template.Spec)
 
 	assert.Len(t, ds.Spec.Template.Spec.Containers[1].VolumeMounts, defaultVolumes+1)
 	// add one more volumemount
@@ -237,19 +238,19 @@ func Test_applyVolumeMountToContainer(t *testing.T) {
 	})
 	volumeMountsRaw, err = yaml.Marshal(volumeMounts)
 	assert.Nil(t, err)
-	os.Setenv(configKey, string(volumeMountsRaw))
+	config[configKey] = string(volumeMountsRaw)
 	ds, err = templateToDaemonSet(dsName, RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
-	applyVolumeMountToContainer(configKey, rbdContainerName, &ds.Spec.Template.Spec)
+	applyVolumeMountToContainer(config, configKey, rbdContainerName, &ds.Spec.Template.Spec)
 	assert.Len(t, ds.Spec.Template.Spec.Containers[1].VolumeMounts, defaultVolumes+2)
 	// override existing volume configuration
 	volumeMounts[1].MountPath = "/run/test/run"
 	volumeMountsRaw, err = yaml.Marshal(volumeMounts)
 	assert.Nil(t, err)
-	os.Setenv(configKey, string(volumeMountsRaw))
+	config[configKey] = string(volumeMountsRaw)
 	ds, err = templateToDaemonSet(dsName, RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
-	applyVolumeMountToContainer(configKey, rbdContainerName, &ds.Spec.Template.Spec)
+	applyVolumeMountToContainer(config, configKey, rbdContainerName, &ds.Spec.Template.Spec)
 	assert.Len(t, ds.Spec.Template.Spec.Containers[1].VolumeMounts, defaultVolumes+2)
 	// remove existing volume configuration
 	volumeMounts = []corev1.VolumeMount{
@@ -260,16 +261,17 @@ func Test_applyVolumeMountToContainer(t *testing.T) {
 	}
 	volumeMountsRaw, err = yaml.Marshal(volumeMounts)
 	assert.Nil(t, err)
-	os.Setenv(configKey, string(volumeMountsRaw))
+	config[configKey] = string(volumeMountsRaw)
 	ds, err = templateToDaemonSet(dsName, RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
-	applyVolumeMountToContainer(configKey, rbdContainerName, &ds.Spec.Template.Spec)
+	applyVolumeMountToContainer(config, configKey, rbdContainerName, &ds.Spec.Template.Spec)
 	assert.Len(t, ds.Spec.Template.Spec.Containers[1].VolumeMounts, defaultVolumes+1)
 }
 
 func Test_getImage(t *testing.T) {
 	type args struct {
-		userImage    string
+		data         map[string]string
+		settingName  string
 		defaultImage string
 	}
 	tests := []struct {
@@ -280,6 +282,8 @@ func Test_getImage(t *testing.T) {
 		{
 			name: "test with default image",
 			args: args{
+				data:         map[string]string{},
+				settingName:  "ROOK_CSI_CEPH_IMAGE",
 				defaultImage: "quay.io/cephcsi/cephcsi:v3.13.0",
 			},
 			want: DefaultCSIPluginImage,
@@ -287,7 +291,10 @@ func Test_getImage(t *testing.T) {
 		{
 			name: "test with user image",
 			args: args{
-				userImage:    "registry.io/private/cephcsi:v8",
+				data: map[string]string{
+					"ROOK_CSI_CEPH_IMAGE": "registry.io/private/cephcsi:v8",
+				},
+				settingName:  "ROOK_CSI_CEPH_IMAGE",
 				defaultImage: "quay.io/cephcsi/cephcsi:v3.11.0",
 			},
 			want: "registry.io/private/cephcsi:v8",
@@ -295,7 +302,10 @@ func Test_getImage(t *testing.T) {
 		{
 			name: "test with user image without version",
 			args: args{
-				userImage:    "registry.io/private/cephcsi",
+				data: map[string]string{
+					"ROOK_CSI_CEPH_IMAGE": "registry.io/private/cephcsi",
+				},
+				settingName:  "ROOK_CSI_CEPH_IMAGE",
 				defaultImage: "quay.io/cephcsi/cephcsi:v3.11.0",
 			},
 			want: "registry.io/private/cephcsi:v3.11.0",
@@ -303,11 +313,7 @@ func Test_getImage(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			if tt.args.userImage != "" {
-				os.Setenv("ROOK_CSI_CEPH_IMAGE", tt.args.userImage)
-				defer os.Unsetenv("ROOK_CSI_CEPH_IMAGE")
-			}
-			got := getImage("ROOK_CSI_CEPH_IMAGE", tt.args.defaultImage)
+			got := getImage(tt.args.data, tt.args.settingName, tt.args.defaultImage)
 			assert.Equal(t, tt.want, got)
 		})
 	}
diff --git a/pkg/operator/ceph/file/controller_test.go b/pkg/operator/ceph/file/controller_test.go
index 8f1700b35..293d370ab 100644
--- a/pkg/operator/ceph/file/controller_test.go
+++ b/pkg/operator/ceph/file/controller_test.go
@@ -254,7 +254,6 @@ func TestCephFilesystemController(t *testing.T) {
 			recorder:         record.NewFakeRecorder(5),
 			scheme:           s,
 			context:          c,
-			fsContexts:       make(map[string]*fsHealth),
 			opManagerContext: context.TODO(),
 		}
 		res, err := r.Reconcile(ctx, req)
diff --git a/pkg/operator/ceph/object/bucket/provisioner_test.go b/pkg/operator/ceph/object/bucket/provisioner_test.go
index 0e44132a4..5aad6a940 100644
--- a/pkg/operator/ceph/object/bucket/provisioner_test.go
+++ b/pkg/operator/ceph/object/bucket/provisioner_test.go
@@ -22,7 +22,6 @@ import (
 	"fmt"
 	"io"
 	"net/http"
-	"os"
 	"strings"
 	"testing"
 
@@ -32,7 +31,6 @@ import (
 	rookclient "github.com/rook/rook/pkg/client/clientset/versioned/fake"
 	"github.com/rook/rook/pkg/clusterd"
 	"github.com/rook/rook/pkg/daemon/ceph/client"
-	opcontroller "github.com/rook/rook/pkg/operator/ceph/controller"
 	"github.com/rook/rook/pkg/operator/ceph/object"
 	"github.com/rook/rook/pkg/operator/test"
 	"github.com/stretchr/testify/assert"
@@ -502,8 +500,6 @@ func TestProvisioner_additionalConfigSpecFromMap(t *testing.T) {
 	})
 
 	t.Run("maxObjects field should be set", func(t *testing.T) {
-		opcontroller.SetObcAllowAdditionalConfigFields()
-
 		spec, err := additionalConfigSpecFromMap(map[string]string{"maxObjects": "2"})
 		assert.NoError(t, err)
 		var i int64 = 2
@@ -511,8 +507,6 @@ func TestProvisioner_additionalConfigSpecFromMap(t *testing.T) {
 	})
 
 	t.Run("maxSize field should be set", func(t *testing.T) {
-		opcontroller.SetObcAllowAdditionalConfigFields()
-
 		spec, err := additionalConfigSpecFromMap(map[string]string{"maxSize": "3"})
 		assert.NoError(t, err)
 		var i int64 = 3
@@ -520,76 +514,34 @@ func TestProvisioner_additionalConfigSpecFromMap(t *testing.T) {
 	})
 
 	t.Run("bucketMaxObjects field should be set", func(t *testing.T) {
-		os.Setenv("ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS", "bucketMaxObjects")
-		defer os.Unsetenv("ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS")
-		opcontroller.SetObcAllowAdditionalConfigFields()
-		defer opcontroller.SetObcAllowAdditionalConfigFields()
-
 		spec, err := additionalConfigSpecFromMap(map[string]string{"bucketMaxObjects": "4"})
 		assert.NoError(t, err)
 		assert.Equal(t, additionalConfigSpec{bucketMaxObjects: &(&struct{ i int64 }{4}).i}, *spec)
 	})
 
 	t.Run("bucketMaxSize field should be set", func(t *testing.T) {
-		os.Setenv("ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS", "bucketMaxSize")
-		defer os.Unsetenv("ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS")
-		opcontroller.SetObcAllowAdditionalConfigFields()
-		defer opcontroller.SetObcAllowAdditionalConfigFields()
-
 		spec, err := additionalConfigSpecFromMap(map[string]string{"bucketMaxSize": "5"})
 		assert.NoError(t, err)
 		assert.Equal(t, additionalConfigSpec{bucketMaxSize: &(&struct{ i int64 }{5}).i}, *spec)
 	})
 
 	t.Run("bucketPolicy field should be set", func(t *testing.T) {
-		os.Setenv("ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS", "bucketPolicy")
-		defer os.Unsetenv("ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS")
-		opcontroller.SetObcAllowAdditionalConfigFields()
-		defer opcontroller.SetObcAllowAdditionalConfigFields()
-
 		spec, err := additionalConfigSpecFromMap(map[string]string{"bucketPolicy": "foo"})
 		assert.NoError(t, err)
 		assert.Equal(t, additionalConfigSpec{bucketPolicy: &(&struct{ s string }{"foo"}).s}, *spec)
 	})
 
 	t.Run("bucketLifecycle field should be set", func(t *testing.T) {
-		os.Setenv("ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS", "bucketLifecycle")
-		defer os.Unsetenv("ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS")
-		opcontroller.SetObcAllowAdditionalConfigFields()
-		defer opcontroller.SetObcAllowAdditionalConfigFields()
-
 		spec, err := additionalConfigSpecFromMap(map[string]string{"bucketLifecycle": "foo"})
 		assert.NoError(t, err)
 		assert.Equal(t, additionalConfigSpec{bucketLifecycle: &(&struct{ s string }{"foo"}).s}, *spec)
 	})
 
 	t.Run("bucketOwner field should be set", func(t *testing.T) {
-		os.Setenv("ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS", "bucketOwner")
-		defer os.Unsetenv("ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS")
-		opcontroller.SetObcAllowAdditionalConfigFields()
-		defer opcontroller.SetObcAllowAdditionalConfigFields()
-
 		spec, err := additionalConfigSpecFromMap(map[string]string{"bucketOwner": "foo"})
 		assert.NoError(t, err)
 		assert.Equal(t, additionalConfigSpec{bucketOwner: &(&struct{ s string }{"foo"}).s}, *spec)
 	})
-
-	t.Run("fields disallowed by default", func(t *testing.T) {
-		opcontroller.SetObcAllowAdditionalConfigFields()
-
-		for _, configKey := range []string{"bucketMaxObjects", "bucketMaxSize", "bucketPolicy", "bucketLifecycle", "bucketOwner"} {
-			_, err := additionalConfigSpecFromMap(map[string]string{configKey: "foo"})
-			assert.Error(t, err)
-		}
-	})
-
-	t.Run("does not fail on empty map", func(t *testing.T) {
-		opcontroller.SetObcAllowAdditionalConfigFields()
-
-		spec, err := additionalConfigSpecFromMap(map[string]string{})
-		assert.NoError(t, err)
-		assert.Equal(t, additionalConfigSpec{}, *spec)
-	})
 }
 
 func numberOfCallsWithValue(substr string, strs []string) int {
diff --git a/pkg/operator/ceph/object/objectstore_test.go b/pkg/operator/ceph/object/objectstore_test.go
index 8359e9897..f384d6c70 100644
--- a/pkg/operator/ceph/object/objectstore_test.go
+++ b/pkg/operator/ceph/object/objectstore_test.go
@@ -517,43 +517,36 @@ func TestGetObjectBucketProvisioner(t *testing.T) {
 	t.Setenv(k8sutil.PodNamespaceEnvVar, testNamespace)
 
 	t.Run("watch ceph cluster namespace", func(t *testing.T) {
-		os.Setenv("ROOK_OBC_WATCH_OPERATOR_NAMESPACE", "true")
-		defer os.Unsetenv("ROOK_OBC_WATCH_OPERATOR_NAMESPACE")
-		bktprovisioner, err := GetObjectBucketProvisioner(testNamespace)
+		data := map[string]string{"ROOK_OBC_WATCH_OPERATOR_NAMESPACE": "true"}
+		bktprovisioner, err := GetObjectBucketProvisioner(data, testNamespace)
 		assert.Equal(t, fmt.Sprintf("%s.%s", testNamespace, bucketProvisionerName), bktprovisioner)
 		assert.NoError(t, err)
 	})
 
 	t.Run("watch all namespaces", func(t *testing.T) {
-		os.Setenv("ROOK_OBC_WATCH_OPERATOR_NAMESPACE", "false")
-		defer os.Unsetenv("ROOK_OBC_WATCH_OPERATOR_NAMESPACE")
-		bktprovisioner, err := GetObjectBucketProvisioner(testNamespace)
+		data := map[string]string{"ROOK_OBC_WATCH_OPERATOR_NAMESPACE": "false"}
+		bktprovisioner, err := GetObjectBucketProvisioner(data, testNamespace)
 		assert.Equal(t, bucketProvisionerName, bktprovisioner)
 		assert.NoError(t, err)
 	})
 
 	t.Run("prefix object provisioner", func(t *testing.T) {
-		os.Setenv("ROOK_OBC_PROVISIONER_NAME_PREFIX", "my-prefix")
-		defer os.Unsetenv("ROOK_OBC_PROVISIONER_NAME_PREFIX")
-		bktprovisioner, err := GetObjectBucketProvisioner(testNamespace)
+		data := map[string]string{"ROOK_OBC_PROVISIONER_NAME_PREFIX": "my-prefix"}
+		bktprovisioner, err := GetObjectBucketProvisioner(data, testNamespace)
 		assert.Equal(t, "my-prefix."+bucketProvisionerName, bktprovisioner)
 		assert.NoError(t, err)
 	})
 
 	t.Run("watch ceph cluster namespace and prefix object provisioner", func(t *testing.T) {
-		os.Setenv("ROOK_OBC_WATCH_OPERATOR_NAMESPACE", "true")
-		os.Setenv("ROOK_OBC_PROVISIONER_NAME_PREFIX", "my-prefix")
-		defer os.Unsetenv("ROOK_OBC_WATCH_OPERATOR_NAMESPACE")
-		defer os.Unsetenv("ROOK_OBC_PROVISIONER_NAME_PREFIX")
-		bktprovisioner, err := GetObjectBucketProvisioner(testNamespace)
+		data := map[string]string{"ROOK_OBC_WATCH_OPERATOR_NAMESPACE": "true", "ROOK_OBC_PROVISIONER_NAME_PREFIX": "my-prefix"}
+		bktprovisioner, err := GetObjectBucketProvisioner(data, testNamespace)
 		assert.Equal(t, "my-prefix."+bucketProvisionerName, bktprovisioner)
 		assert.NoError(t, err)
 	})
 
 	t.Run("invalid prefix value for object provisioner", func(t *testing.T) {
-		os.Setenv("ROOK_OBC_PROVISIONER_NAME_PREFIX", "my-prefix.")
-		defer os.Unsetenv("ROOK_OBC_PROVISIONER_NAME_PREFIX")
-		_, err := GetObjectBucketProvisioner(testNamespace)
+		data := map[string]string{"ROOK_OBC_PROVISIONER_NAME_PREFIX": "my-prefix."}
+		_, err := GetObjectBucketProvisioner(data, testNamespace)
 		assert.Error(t, err)
 	})
 
diff --git a/pkg/operator/discover/discover_test.go b/pkg/operator/discover/discover_test.go
index ec19cc16e..667e88bdc 100644
--- a/pkg/operator/discover/discover_test.go
+++ b/pkg/operator/discover/discover_test.go
@@ -19,12 +19,12 @@ package discover
 
 import (
 	"context"
-	"os"
 	"testing"
 
 	cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1"
 	"github.com/rook/rook/pkg/clusterd"
 	discoverDaemon "github.com/rook/rook/pkg/daemon/discover"
+	"github.com/rook/rook/pkg/operator/ceph/controller"
 	"github.com/rook/rook/pkg/operator/k8sutil"
 	"github.com/rook/rook/pkg/operator/test"
 
@@ -63,7 +63,7 @@ func TestStartDiscoveryDaemonset(t *testing.T) {
 	_, err := clientset.CoreV1().Pods("rook-system").Create(ctx, &pod, metav1.CreateOptions{})
 	assert.NoError(t, err)
 	// start a basic cluster
-	err = a.Start(ctx, namespace, "rook/rook:myversion", "mysa", false)
+	err = a.Start(ctx, namespace, "rook/rook:myversion", "mysa", map[string]string{}, false)
 	assert.Nil(t, err)
 
 	// check daemonset parameters
@@ -86,11 +86,21 @@ func TestStartDiscoveryDaemonset(t *testing.T) {
 	assert.Nil(t, agentDS.Spec.Template.Spec.Tolerations)
 
 	// Test with rook override configmap setting
-	os.Setenv("DISCOVER_TOLERATIONS", "- effect: NoSchedule\n  key: node-role.kubernetes.io/control-plane\n  operator: Exists\n- effect: NoExecute\n  key: node-role.kubernetes.io/etcd\n  operator: Exists")
-	defer os.Unsetenv("DISCOVER_TOLERATIONS")
+	opConfigCM := &v1.ConfigMap{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      controller.OperatorSettingConfigMapName,
+			Namespace: namespace,
+		},
+		Data: map[string]string{
+			"DISCOVER_TOLERATIONS": "- effect: NoSchedule\n  key: node-role.kubernetes.io/control-plane\n  operator: Exists\n- effect: NoExecute\n  key: node-role.kubernetes.io/etcd\n  operator: Exists",
+		},
+	}
+
+	cm, err := clientset.CoreV1().ConfigMaps(namespace).Create(ctx, opConfigCM, metav1.CreateOptions{})
+	assert.NoError(t, err)
 
 	// start a basic cluster
-	err = a.Start(ctx, namespace, "rook/rook:myversion", "mysa", false)
+	err = a.Start(ctx, namespace, "rook/rook:myversion", "mysa", cm.Data, false)
 	assert.Nil(t, err)
 
 	agentDS, err = clientset.AppsV1().DaemonSets(namespace).Get(ctx, "rook-discover", metav1.GetOptions{})
diff --git a/pkg/operator/k8sutil/configmap_test.go b/pkg/operator/k8sutil/configmap_test.go
index d4577b0fc..cc03f1fcc 100644
--- a/pkg/operator/k8sutil/configmap_test.go
+++ b/pkg/operator/k8sutil/configmap_test.go
@@ -63,7 +63,7 @@ func TestGetOperatorSetting(t *testing.T) {
 	k8s := fake.NewSimpleClientset()
 	ctx := context.TODO()
 
-	operatorSettingConfigMapName := "rook-ceph-operator-config"
+	operatorSettingConfigMapName := "rook-operator-config"
 	testNamespace := "test-namespace"
 	cm := &v1.ConfigMap{
 		ObjectMeta: metav1.ObjectMeta{
@@ -81,41 +81,63 @@ func TestGetOperatorSetting(t *testing.T) {
 	cmSettingValue := "storage=rook, worker"
 	defaultValue := ""
 
-	// ConfigMap is not yet applied so we expect the default value
-	setting := GetOperatorSetting(nodeAffinity, defaultValue)
-	assert.Equal(t, defaultValue, setting)
+	// ConfigMap is not found
+	setting, err := GetOperatorSetting(ctx, k8s, operatorSettingConfigMapName, nodeAffinity, defaultValue)
+	assert.NoError(t, err)
 
+	// Env Var doesn't exist
+	assert.Equal(t, defaultValue, setting)
 	// Env Var exists
 	t.Setenv(nodeAffinity, envSettingValue)
-	setting = GetOperatorSetting(nodeAffinity, defaultValue)
+	setting, err = GetOperatorSetting(ctx, k8s, operatorSettingConfigMapName, nodeAffinity, defaultValue)
+	assert.NoError(t, err)
 	assert.Equal(t, envSettingValue, setting)
 
 	// ConfigMap is found
 	t.Setenv("POD_NAMESPACE", testNamespace)
-	_, err := k8s.CoreV1().ConfigMaps(testNamespace).Create(ctx, cm, metav1.CreateOptions{})
-	assert.NoError(t, err)
-	err = ApplyOperatorSettingsConfigmap(ctx, k8s)
+	_, err = k8s.CoreV1().ConfigMaps(testNamespace).Create(ctx, cm, metav1.CreateOptions{})
 	assert.NoError(t, err)
 
 	// Setting exists in ConfigMap
-	setting = GetOperatorSetting(nodeAffinity, defaultValue)
+	setting, err = GetOperatorSetting(ctx, k8s, operatorSettingConfigMapName, nodeAffinity, defaultValue)
+	assert.NoError(t, err)
+	// Env Var exists
 	assert.Equal(t, cmSettingValue, setting)
-
 	// Env Var doesn't exist
 	err = os.Unsetenv(nodeAffinity)
 	assert.NoError(t, err)
 	assert.Equal(t, cmSettingValue, setting)
 
-	// Setting doesn't exist in ConfigMap, expect the default value
-	setting = GetOperatorSetting(podAffinity, defaultValue)
+	// Setting doesn't exist in ConfigMap
+	setting, err = GetOperatorSetting(ctx, k8s, operatorSettingConfigMapName, podAffinity, defaultValue)
+	assert.NoError(t, err)
+	// Env Var doesn't exist
 	assert.Equal(t, defaultValue, setting)
 	// Env Var exists
 	t.Setenv(podAffinity, envSettingValue)
-	setting = GetOperatorSetting(podAffinity, defaultValue)
+	setting, err = GetOperatorSetting(ctx, k8s, operatorSettingConfigMapName, podAffinity, defaultValue)
 	assert.NoError(t, err)
 	assert.Equal(t, envSettingValue, setting)
 }
 
+func TestLogChangedSettings(t *testing.T) {
+	key := "Node_Affinity"
+	value := "storage=rook, worker"
+
+	// When the key is not in the map, it should be added and return true for logging
+	ok := logChangedSettings(key, value)
+	assert.True(t, true, ok)
+
+	// When the key is already present in the map and value is same, it should return false for logging
+	ok = logChangedSettings(key, value)
+	assert.False(t, false, ok)
+
+	// When the key is already present in the map and value is updated, it should return true for logging
+	value = "storage=rook, test"
+	ok = logChangedSettings(key, value)
+	assert.True(t, true, ok)
+}
+
 func TestCreateOrUpdateConfigMap(t *testing.T) {
 	k8s := fake.NewSimpleClientset()
 	ctx := context.TODO()
diff --git a/tests/framework/installer/ceph_settings.go b/tests/framework/installer/ceph_settings.go
index d207d1a33..3fb0e7cb1 100644
--- a/tests/framework/installer/ceph_settings.go
+++ b/tests/framework/installer/ceph_settings.go
@@ -86,9 +86,6 @@ func (s *TestCephSettings) replaceOperatorSettings(manifest string) string {
 	manifest = strings.ReplaceAll(manifest, `ROOK_ENABLE_DISCOVERY_DAEMON: "false"`, fmt.Sprintf(`ROOK_ENABLE_DISCOVERY_DAEMON: "%t"`, s.EnableDiscovery))
 	manifest = strings.ReplaceAll(manifest, `CSI_ENABLE_VOLUME_REPLICATION: "false"`, fmt.Sprintf(`CSI_ENABLE_VOLUME_REPLICATION: "%t"`, s.EnableVolumeReplication))
 	manifest = strings.ReplaceAll(manifest, `ROOK_CSI_ENABLE_NFS: "false"`, fmt.Sprintf(`ROOK_CSI_ENABLE_NFS: "%t"`, s.TestNFSCSI))
-	manifest = strings.ReplaceAll(manifest,
-		`# ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS: "maxObjects,maxSize" # default allowed configs`,
-		`ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS: "maxObjects,maxSize,bucketMaxObjects,bucketMaxSize,bucketPolicy,bucketLifecycle,bucketOwner"`)
 	return manifest
 }
 
diff --git a/tests/integration/ceph_base_keystone_test.go b/tests/integration/ceph_base_keystone_test.go
index 8b30d6cc0..7fc019fbc 100644
--- a/tests/integration/ceph_base_keystone_test.go
+++ b/tests/integration/ceph_base_keystone_test.go
@@ -982,8 +982,6 @@ func runSwiftE2ETest(t *testing.T, helper *clients.TestClient, k8sh *utils.K8sHe
 }
 
 func testInOpenStackClient(t *testing.T, sh *utils.K8sHelper, namespace string, projectname string, username string, expectNoError bool, command ...string) {
-	err := sh.WaitForLabeledPodsToRun("app=osc-admin-admin", namespace)
-	assert.NoError(t, err)
 
 	commandLine := []string{"exec", "-n", namespace, "deployment/osc-" + projectname + "-" + username, "--"}
 
diff --git a/tests/manifests/test-cluster-on-pvc-encrypted.yaml b/tests/manifests/test-cluster-on-pvc-encrypted.yaml
index 941d9f2fa..2ebdeb6d1 100644
--- a/tests/manifests/test-cluster-on-pvc-encrypted.yaml
+++ b/tests/manifests/test-cluster-on-pvc-encrypted.yaml
@@ -14,7 +14,7 @@ spec:
           requests:
             storage: 5Gi
   cephVersion:
-    image: quay.io/ceph/ceph:v19.2.1
+    image: quay.io/ceph/ceph:v19.2.0
   dashboard:
     enabled: false
   network:
diff --git a/tests/scripts/github-action-helper.sh b/tests/scripts/github-action-helper.sh
index c2a303993..a9f2f4759 100755
--- a/tests/scripts/github-action-helper.sh
+++ b/tests/scripts/github-action-helper.sh
@@ -273,8 +273,8 @@ function deploy_toolbox() {
 }
 
 function replace_ceph_image() {
-  local file="$1"  # parameter 1: the file in which to replace the ceph image
-  local ceph_image="${2?ceph_image is required}"  # parameter 2: the new ceph image to use
+  local file="$1"                                # parameter 1: the file in which to replace the ceph image
+  local ceph_image="${2?ceph_image is required}" # parameter 2: the new ceph image to use
 
   # check for ceph_image being an empty string
   if [ -z "$ceph_image" ]; then
@@ -562,7 +562,7 @@ function get_secret_key() {
 }
 
 function s3cmd() {
-  command timeout 20 s3cmd -v --config=s3cfg --access_key="${S3CMD_ACCESS_KEY}" --secret_key="${S3CMD_SECRET_KEY}" "$@"
+  command timeout 150 s3cmd -v --config=s3cfg --access_key="${S3CMD_ACCESS_KEY}" --secret_key="${S3CMD_SECRET_KEY}" "$@"
 }
 
 function write_object_read_from_replica_cluster() {
@@ -572,6 +572,7 @@ function write_object_read_from_replica_cluster() {
 
   local test_object_name="${test_bucket_name}-1mib-test.dat"
   fallocate -l 1M "$test_object_name"
+
   # ensure that test file has unique data
   echo "$test_object_name" >>"$test_object_name"
 
@@ -781,7 +782,7 @@ function test_object_separate_pools() {
     done
     if [[ "$found" == false ]]; then
       echo "Live pool $l is not an expected pool"
-      errors=$((errors+1))
+      errors=$((errors + 1))
     fi
   done