diff --git a/README.md b/README.md
index 7ccae2ad5636c967f0f92ee2e05904fe252a8315..a4ade6456b9334cedca691e53ec6497c05c60e30 100644
--- a/README.md
+++ b/README.md
@@ -275,8 +275,9 @@ A few things to note; the annotation for the `StorageClass` will apply to all vo
 
 ### Storage classes
 
-If more than one `paths` are specified in the `nodePathMap` the path is chosen randomly. To make the provisioner choose a specific path, use a `storageClass` defined with a parameter called `nodePath`. Note that this path should be defined in the `nodePathMap`
+If more than one `paths` are specified in the `nodePathMap` the path is chosen randomly. To make the provisioner choose a specific path, use a `storageClass` defined with a parameter called `nodePath`. Note that this path should be defined in the `nodePathMap`.
 
+By default the volume subdirectory is named using the template `{{ .PVName }}_{{ .PVC.Namespace }}_{{ .PVC.Name }}` which make the directory specific to the PV instance. The template can be changed using the `pathPattern` parameter which is interpreted as a go template. The template has access to the PV name using the `PVName` variable and the PVC metadata object, including labels and annotations, with the `PVC` variable.
 ```
 apiVersion: storage.k8s.io/v1
 kind: StorageClass
@@ -285,11 +286,12 @@ metadata:
 provisioner: rancher.io/local-path
 parameters:
   nodePath: /data/ssd
+  pathPattern: "{{ .PVC.Namespace }}/{{ .PVC.Name }}"
 volumeBindingMode: WaitForFirstConsumer
 reclaimPolicy: Delete
 ```
 
-Here the provisioner will use the path `/data/ssd` when storage class `ssd-local-path` is used.
+Here the provisioner will use the path `/data/ssd` with a subdirectory per namespace and PVC when storage class `ssd-local-path` is used.
 
 ## Uninstall
 
diff --git a/provisioner.go b/provisioner.go
index e12485325f1b7bd44726bacefb200ffb690ad471..3257972858ee7bfa51fa12d9cd341bbfb129b439 100644
--- a/provisioner.go
+++ b/provisioner.go
@@ -12,6 +12,7 @@ import (
 	"strconv"
 	"strings"
 	"sync"
+	"text/template"
 	"time"
 
 	"github.com/Sirupsen/logrus"
@@ -291,6 +292,31 @@ func (p *LocalPathProvisioner) pickConfig(storageClassName string) (*StorageClas
 	return &cfg, nil
 }
 
+type pvMetadata struct {
+	PVName string
+	PVC    metav1.ObjectMeta
+}
+
+func pathFromPattern(pattern string, opts pvController.ProvisionOptions) (string, error) {
+	metadata := pvMetadata{
+		PVName: opts.PVName,
+		PVC: opts.PVC.ObjectMeta,
+	}
+
+	tpl, err := template.New("pathPattern").Parse(pattern)
+	if err != nil {
+		return "", err
+	}
+
+	buf := new(bytes.Buffer)
+	err = tpl.Execute(buf, metadata)
+	if err != nil {
+		return "", err
+	}
+
+	return buf.String(), nil
+}
+
 func (p *LocalPathProvisioner) Provision(ctx context.Context, opts pvController.ProvisionOptions) (*v1.PersistentVolume, pvController.ProvisioningState, error) {
 	cfg, err := p.pickConfig(opts.StorageClass.Name)
 	if err != nil {
@@ -340,6 +366,15 @@ func (p *LocalPathProvisioner) provisionFor(opts pvController.ProvisionOptions,
 	name := opts.PVName
 	folderName := strings.Join([]string{name, opts.PVC.Namespace, opts.PVC.Name}, "_")
 
+	pathPattern, exists := opts.StorageClass.Parameters["pathPattern"]
+	if exists {
+		folderName, err = pathFromPattern(pathPattern, opts)
+		if err != nil {
+			err = errors.Wrapf(err, "failed to create path from pattern %v", pathPattern)
+			return nil, pvController.ProvisioningFinished, err
+		}
+	}
+
 	path := filepath.Join(basePath, folderName)
 	if nodeName == "" {
 		logrus.Infof("Creating volume %v at %v", name, path)