diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8c470a4a6..f5db2d11c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -376,3 +376,37 @@ Add your fs to the docs - you'll need to pick an icon for it from [fontawesome](
   * `docs/content/about.md` - front page of rclone.org
   * `docs/layouts/chrome/navbar.html` - add it to the website navigation
   * `bin/make_manual.py` - add the page to the `docs` constant
+
+## Writing a plugin ##
+
+New features (backends, commands) can also be added "out-of-tree", through Go plugins.
+Changes will be kept in a dynamically loaded file instead of being compiled into the main binary.
+This is useful if you can't merge your changes upstream or don't want to maintain a fork of rclone.
+
+Usage
+
+ - Naming
+   - Plugins names must have the pattern `librcloneplugin_KIND_NAME.so`.
+   - `KIND` should be one of `backend`, `command` or `bundle`.
+   - Example: A plugin with backend support for PiFS would be called
+     `librcloneplugin_backend_pifs.so`.
+ - Loading
+   - Supported on macOS & Linux as of now. ([Go issue for Windows support](https://github.com/golang/go/issues/19282))
+   - Supported on rclone v1.50 or greater.
+   - All plugins in the folder specified by variable `$RCLONE_PLUGIN_PATH` are loaded.
+   - If this variable doesn't exist, plugin support is disabled.
+   - Plugins must be compiled against the exact version of rclone to work.
+     (The rclone used during building the plugin must be the same as the source of rclone)
+     
+Building
+
+To turn your existing additions into a Go plugin, move them to an external repository
+and change the top-level package name to `main`.
+
+Check `rclone --version` and make sure that the plugin's rclone dependency and host Go version match.
+
+Then, run `go build -buildmode=plugin -o PLUGIN_NAME.so .` to build the plugin.
+
+[Go reference](https://godoc.org/github.com/rclone/rclone/lib/plugin)
+
+[Minimal example](https://gist.github.com/terorie/21b517ee347828e899e1913efc1d684f)
diff --git a/lib/plugin/package.go b/lib/plugin/package.go
new file mode 100644
index 000000000..72598680f
--- /dev/null
+++ b/lib/plugin/package.go
@@ -0,0 +1,16 @@
+// Package plugin implements loading out-of-tree storage backends
+// using https://golang.org/pkg/plugin/ on Linux and macOS.
+//
+// If the $RCLONE_PLUGIN_PATH is present, any Go plugins in that dir
+// named like librcloneplugin_NAME.so will be loaded.
+//
+// To create a plugin, write the backend package like it was in-tree
+// but set the package name to "main". Then, build the plugin with
+//
+//     go build -buildmode=plugin -o librcloneplugin_NAME.so
+//
+// where NAME equals the plugin's fs.RegInfo.Name.
+package plugin
+
+// Build for plugin for unsupported platforms to stop go complaining
+// about "no buildable Go source files".
diff --git a/lib/plugin/plugin.go b/lib/plugin/plugin.go
new file mode 100644
index 000000000..37ac5b94c
--- /dev/null
+++ b/lib/plugin/plugin.go
@@ -0,0 +1,41 @@
+// +build darwin linux
+
+package plugin
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"plugin"
+	"strings"
+)
+
+func init() {
+	dir := os.Getenv("RCLONE_PLUGIN_PATH")
+	if dir == "" {
+		return
+	}
+	// Get file names of plugin dir
+	listing, err := ioutil.ReadDir(dir)
+	if err != nil {
+		fmt.Fprintln(os.Stderr, "Failed to open plugin directory:", err)
+	}
+	// Enumerate file names, load valid plugins
+	for _, file := range listing {
+		// Match name
+		fileName := file.Name()
+		if !strings.HasPrefix(fileName, "librcloneplugin_") {
+			continue
+		}
+		if !strings.HasSuffix(fileName, ".so") {
+			continue
+		}
+		// Try to load plugin
+		_, err := plugin.Open(filepath.Join(dir, fileName))
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "Failed to load plugin %s: %s\n",
+				fileName, err)
+		}
+	}
+}
diff --git a/rclone.go b/rclone.go
index a1e745f4a..74fde16ab 100644
--- a/rclone.go
+++ b/rclone.go
@@ -6,7 +6,8 @@ package main
 import (
 	_ "github.com/rclone/rclone/backend/all" // import all backends
 	"github.com/rclone/rclone/cmd"
-	_ "github.com/rclone/rclone/cmd/all" // import all commands
+	_ "github.com/rclone/rclone/cmd/all"    // import all commands
+	_ "github.com/rclone/rclone/lib/plugin" // import plugins
 )
 
 func main() {