diff --git a/cmd/cinder-csi-plugin/main.go b/cmd/cinder-csi-plugin/main.go
index 5bb50621ca..e177b46413 100644
--- a/cmd/cinder-csi-plugin/main.go
+++ b/cmd/cinder-csi-plugin/main.go
@@ -40,6 +40,7 @@ var (
 	httpEndpoint             string
 	provideControllerService bool
 	provideNodeService       bool
+	noClient                 bool
 )
 
 func main() {
@@ -75,6 +76,7 @@ func main() {
 
 	cmd.PersistentFlags().BoolVar(&provideControllerService, "provide-controller-service", true, "If set to true then the CSI driver does provide the controller service (default: true)")
 	cmd.PersistentFlags().BoolVar(&provideNodeService, "provide-node-service", true, "If set to true then the CSI driver does provide the node service (default: true)")
+	cmd.PersistentFlags().BoolVar(&noClient, "node-service-no-os-client", false, "If set to true then the CSI driver node service will not use the OpenStack client (default: false)")
 
 	openstack.AddExtraFlags(pflag.CommandLine)
 
@@ -87,21 +89,32 @@ func handle() {
 	d := cinder.NewDriver(&cinder.DriverOpts{Endpoint: endpoint, ClusterID: cluster})
 
 	openstack.InitOpenStackProvider(cloudConfig, httpEndpoint)
-	var err error
-	clouds := make(map[string]openstack.IOpenStack)
-	for _, cloudName := range cloudNames {
-		clouds[cloudName], err = openstack.GetOpenStackProvider(cloudName)
-		if err != nil {
-			klog.Warningf("Failed to GetOpenStackProvider %s: %v", cloudName, err)
-			return
-		}
-	}
 
 	if provideControllerService {
+		var err error
+		clouds := make(map[string]openstack.IOpenStack)
+		for _, cloudName := range cloudNames {
+			clouds[cloudName], err = openstack.GetOpenStackProvider(cloudName, false)
+			if err != nil {
+				klog.Warningf("Failed to GetOpenStackProvider %s: %v", cloudName, err)
+				return
+			}
+		}
+
 		d.SetupControllerService(clouds)
 	}
 
 	if provideNodeService {
+		var err error
+		clouds := make(map[string]openstack.IOpenStack)
+		for _, cloudName := range cloudNames {
+			clouds[cloudName], err = openstack.GetOpenStackProvider(cloudName, noClient)
+			if err != nil {
+				klog.Warningf("Failed to GetOpenStackProvider %s: %v", cloudName, err)
+				return
+			}
+		}
+
 		//Initialize mount
 		mount := mount.GetMountProvider()
 
diff --git a/docs/cinder-csi-plugin/using-cinder-csi-plugin.md b/docs/cinder-csi-plugin/using-cinder-csi-plugin.md
index 210d26a2c4..3fc6867e3b 100644
--- a/docs/cinder-csi-plugin/using-cinder-csi-plugin.md
+++ b/docs/cinder-csi-plugin/using-cinder-csi-plugin.md
@@ -111,6 +111,13 @@ In addition to the standard set of klog flags, `cinder-csi-plugin` accepts the f
 
   The default is to provide the node service.
   </dd>
+
+  <dt>--node-service-no-os-client &lt;disabled&gt;</dt>
+  <dd>
+  If set to true then the CSI driver does not provide the OpenStack client in the node service.
+
+  The default is to provide the OpenStack client in the node service.
+  </dd>
 </dl>
 
 ## Driver Config
diff --git a/pkg/csi/cinder/openstack/noop_openstack.go b/pkg/csi/cinder/openstack/noop_openstack.go
new file mode 100644
index 0000000000..c2f563c0b2
--- /dev/null
+++ b/pkg/csi/cinder/openstack/noop_openstack.go
@@ -0,0 +1,144 @@
+/*
+Copyright 2024 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package openstack
+
+import (
+	"fmt"
+
+	"github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/backups"
+	"github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/snapshots"
+	"github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes"
+	"github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers"
+	"k8s.io/cloud-provider-openstack/pkg/util/metadata"
+)
+
+type NoopOpenStack struct {
+	bsOpts       BlockStorageOpts
+	metadataOpts metadata.Opts
+}
+
+func (os *NoopOpenStack) CreateVolume(name string, size int, vtype, availability string, snapshotID string, sourceVolID string, sourceBackupID string, tags map[string]string) (*volumes.Volume, error) {
+	return nil, fmt.Errorf("CreateVolume is not implemented for ephemeral storage in this configuration")
+}
+
+func (os *NoopOpenStack) ListVolumes(limit int, startingToken string) ([]volumes.Volume, string, error) {
+	return nil, "", nil
+}
+
+func (os *NoopOpenStack) GetVolumesByName(n string) ([]volumes.Volume, error) {
+	return nil, nil
+}
+
+func (os *NoopOpenStack) DeleteVolume(volumeID string) error {
+	return nil
+}
+
+func (os *NoopOpenStack) GetVolume(volumeID string) (*volumes.Volume, error) {
+	return &volumes.Volume{ID: volumeID}, nil
+}
+
+func (os *NoopOpenStack) AttachVolume(instanceID, volumeID string) (string, error) {
+	return volumeID, nil
+}
+
+func (os *NoopOpenStack) WaitDiskAttached(instanceID string, volumeID string) error {
+	return nil
+}
+
+func (os *NoopOpenStack) WaitVolumeTargetStatus(volumeID string, tStatus []string) error {
+	return nil
+}
+
+func (os *NoopOpenStack) DetachVolume(instanceID, volumeID string) error {
+	return nil
+}
+
+func (os *NoopOpenStack) WaitDiskDetached(instanceID string, volumeID string) error {
+	return nil
+}
+
+func (os *NoopOpenStack) GetAttachmentDiskPath(instanceID, volumeID string) (string, error) {
+	return "", nil
+}
+
+func (os *NoopOpenStack) ExpandVolume(volumeID string, status string, newSize int) error {
+	return nil
+}
+
+func (os *NoopOpenStack) GetMaxVolLimit() int64 {
+	if os.bsOpts.NodeVolumeAttachLimit > 0 && os.bsOpts.NodeVolumeAttachLimit <= 256 {
+		return os.bsOpts.NodeVolumeAttachLimit
+	}
+
+	return defaultMaxVolAttachLimit
+}
+
+func (os *NoopOpenStack) GetBlockStorageOpts() BlockStorageOpts {
+	return os.bsOpts
+}
+
+func (os *NoopOpenStack) GetMetadataOpts() metadata.Opts {
+	return os.metadataOpts
+}
+
+func (os *NoopOpenStack) CreateBackup(name, volID, snapshotID, availabilityZone string, tags map[string]string) (*backups.Backup, error) {
+	return &backups.Backup{}, nil
+}
+
+func (os *NoopOpenStack) BackupsAreEnabled() (bool, error) {
+	return false, nil
+}
+
+func (os *NoopOpenStack) DeleteBackup(backupID string) error {
+	return nil
+}
+
+func (os *NoopOpenStack) CreateSnapshot(name, volID string, tags map[string]string) (*snapshots.Snapshot, error) {
+	return &snapshots.Snapshot{}, nil
+}
+
+func (os *NoopOpenStack) DeleteSnapshot(snapID string) error {
+	return nil
+}
+
+func (os *NoopOpenStack) GetSnapshotByID(snapshotID string) (*snapshots.Snapshot, error) {
+	return &snapshots.Snapshot{ID: snapshotID}, nil
+}
+
+func (os *NoopOpenStack) ListSnapshots(filters map[string]string) ([]snapshots.Snapshot, string, error) {
+	return nil, "", nil
+}
+
+func (os *NoopOpenStack) WaitSnapshotReady(snapshotID string) (string, error) {
+	return "", nil
+}
+
+func (os *NoopOpenStack) GetBackupByID(backupID string) (*backups.Backup, error) {
+	return &backups.Backup{ID: backupID}, nil
+}
+
+func (os *NoopOpenStack) ListBackups(filters map[string]string) ([]backups.Backup, error) {
+	return nil, nil
+}
+
+func (os *NoopOpenStack) WaitBackupReady(backupID string, snapshotSize int, backupMaxDurationSecondsPerGB int) (string, error) {
+	return "", nil
+}
+
+func (os *NoopOpenStack) GetInstanceByID(instanceID string) (*servers.Server, error) {
+	return &servers.Server{ID: instanceID}, nil
+}
diff --git a/pkg/csi/cinder/openstack/openstack.go b/pkg/csi/cinder/openstack/openstack.go
index dbf10dd11d..6bcfe43762 100644
--- a/pkg/csi/cinder/openstack/openstack.go
+++ b/pkg/csi/cinder/openstack/openstack.go
@@ -144,10 +144,12 @@ func GetConfigFromFiles(configFilePaths []string) (Config, error) {
 const defaultMaxVolAttachLimit int64 = 256
 
 var OsInstances map[string]IOpenStack
+var NoopInstances map[string]IOpenStack
 var configFiles = []string{"/etc/cloud.conf"}
 
 func InitOpenStackProvider(cfgFiles []string, httpEndpoint string) {
 	OsInstances = make(map[string]IOpenStack)
+	NoopInstances = make(map[string]IOpenStack)
 	metrics.RegisterMetrics("cinder-csi")
 	if httpEndpoint != "" {
 		mux := http.NewServeMux()
@@ -166,7 +168,7 @@ func InitOpenStackProvider(cfgFiles []string, httpEndpoint string) {
 }
 
 // CreateOpenStackProvider creates Openstack Instance with custom Global config param
-func CreateOpenStackProvider(cloudName string) (IOpenStack, error) {
+func CreateOpenStackProvider(cloudName string, noClient bool) (IOpenStack, error) {
 	// Get config from file
 	cfg, err := GetConfigFromFiles(configFiles)
 	if err != nil {
@@ -179,6 +181,21 @@ func CreateOpenStackProvider(cloudName string) (IOpenStack, error) {
 		return nil, fmt.Errorf("GetConfigFromFiles cloud name \"%s\" not found in configuration files: %s", cloudName, configFiles)
 	}
 
+	// if no search order given, use default
+	if len(cfg.Metadata.SearchOrder) == 0 {
+		cfg.Metadata.SearchOrder = fmt.Sprintf("%s,%s", metadata.ConfigDriveID, metadata.MetadataID)
+	}
+
+	if noClient {
+		// Init OpenStack
+		NoopInstances[cloudName] = &NoopOpenStack{
+			bsOpts:       cfg.BlockStorage,
+			metadataOpts: cfg.Metadata,
+		}
+
+		return NoopInstances[cloudName], nil
+	}
+
 	provider, err := client.NewOpenStackClient(cfg.Global[cloudName], "cinder-csi-plugin", userAgentData...)
 	if err != nil {
 		return nil, err
@@ -201,11 +218,6 @@ func CreateOpenStackProvider(cloudName string) (IOpenStack, error) {
 		return nil, err
 	}
 
-	// if no search order given, use default
-	if len(cfg.Metadata.SearchOrder) == 0 {
-		cfg.Metadata.SearchOrder = fmt.Sprintf("%s,%s", metadata.ConfigDriveID, metadata.MetadataID)
-	}
-
 	// Init OpenStack
 	OsInstances[cloudName] = &OpenStack{
 		compute:      computeclient,
@@ -219,12 +231,25 @@ func CreateOpenStackProvider(cloudName string) (IOpenStack, error) {
 }
 
 // GetOpenStackProvider returns Openstack Instance
-func GetOpenStackProvider(cloudName string) (IOpenStack, error) {
+func GetOpenStackProvider(cloudName string, noClient bool) (IOpenStack, error) {
+	if noClient {
+		NoopInstance, NoopInstanceDefined := NoopInstances[cloudName]
+		if NoopInstanceDefined {
+			return NoopInstance, nil
+		}
+		NoopInstance, err := CreateOpenStackProvider(cloudName, noClient)
+		if err != nil {
+			return nil, err
+		}
+
+		return NoopInstance, nil
+	}
+
 	OsInstance, OsInstanceDefined := OsInstances[cloudName]
 	if OsInstanceDefined {
 		return OsInstance, nil
 	}
-	OsInstance, err := CreateOpenStackProvider(cloudName)
+	OsInstance, err := CreateOpenStackProvider(cloudName, noClient)
 	if err != nil {
 		return nil, err
 	}