diff --git a/CMakeLists.txt b/CMakeLists.txt
index b20fb6d69efe22032e0da05f04dc867356bedfd5..e4d2d6906b29352526f8ef24ba85c80810fd9750 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -301,7 +301,7 @@ if ( ENABLE_CPACK )
       set( CPACK_RPM_PACKAGE_REQUIRES "${CPACK_RPM_PACKAGE_REQUIRES},OCE-draw,OCE-foundation,OCE-modeling,OCE-ocaf,OCE-visualization")
       set ( CPACK_RPM_PACKAGE_REQUIRES "${CPACK_RPM_PACKAGE_REQUIRES},poco-crypto,poco-data,poco-mysql,poco-sqlite,poco-odbc,poco-util,poco-xml,poco-zip,poco-net,poco-netssl,poco-foundation,PyQt4" )
       set ( CPACK_RPM_PACKAGE_REQUIRES "${CPACK_RPM_PACKAGE_REQUIRES},sip >= 4.18" )
-      set ( CPACK_RPM_PACKAGE_REQUIRES "${CPACK_RPM_PACKAGE_REQUIRES},python-six,python-ipython >= 1.1.0,python-ipython-notebook,PyYAML" )
+      set ( CPACK_RPM_PACKAGE_REQUIRES "${CPACK_RPM_PACKAGE_REQUIRES},python-six,python-ipython >= 1.1.0,python-ipython-notebook,PyYAML,python2-psutil" )
       # scipy
       set ( CPACK_RPM_PACKAGE_REQUIRES "${CPACK_RPM_PACKAGE_REQUIRES},scipy" )
       set ( CPACK_RPM_PACKAGE_REQUIRES "${CPACK_RPM_PACKAGE_REQUIRES},mxml,hdf,hdf5,jsoncpp >= 0.7.0" )
@@ -361,7 +361,8 @@ if ( ENABLE_CPACK )
                            "libhdf5-cpp-11,"
                            "python-pycifrw (>= 4.2.1),"
                            "python-yaml,"
-                           "python-qtawesome")
+                           "python-qtawesome,"
+                           "python-psutil")
         set ( PERFTOOLS_DEB_PACKAGE "libgoogle-perftools4 (>= 1.7)" )
         if( "${UNIX_CODENAME}" STREQUAL "bionic")
             list ( APPEND DEPENDS_LIST ",libgsl23,liboce-foundation11,liboce-modeling11,libqscintilla2-qt4-13,jupyter-notebook,libhdf5-cpp-100")
diff --git a/MantidPlot/CMakeLists.txt b/MantidPlot/CMakeLists.txt
index 7ea2943c40f2bdb611a5224926459f37bba02b51..b5b9670e1c502cfef2537ab1deba3d5c29ab58ef 100644
--- a/MantidPlot/CMakeLists.txt
+++ b/MantidPlot/CMakeLists.txt
@@ -91,6 +91,7 @@ set ( QTIPLOT_SRCS src/ApplicationWindow.cpp
                    src/PluginFit.cpp
                    src/PolynomFitDialog.cpp
                    src/PolynomialFit.cpp
+                   src/Process.cpp
                    src/ProjectRecovery.cpp
                    src/ProjectSaveView.cpp
                    src/ProjectSerialiser.cpp
@@ -291,6 +292,7 @@ set ( QTIPLOT_HDRS src/ApplicationWindow.h
                    src/PluginFit.h
                    src/PolynomFitDialog.h
                    src/PolynomialFit.h
+                   src/Process.h
                    src/ProjectRecovery.h
                    src/ProjectSerialiser.h
                    src/ProjectSaveView.h
diff --git a/MantidPlot/src/ApplicationWindow.cpp b/MantidPlot/src/ApplicationWindow.cpp
index 5dc3ed152afe0869a3c01a5c362278b6a9077699..44d7eae1edbf1091bdcc14113d3b4bb56f0568bc 100644
--- a/MantidPlot/src/ApplicationWindow.cpp
+++ b/MantidPlot/src/ApplicationWindow.cpp
@@ -91,6 +91,7 @@
 #include "PlotWizard.h"
 #include "PolynomFitDialog.h"
 #include "PolynomialFit.h"
+#include "Process.h"
 #include "ProjectRecovery.h"
 #include "ProjectSerialiser.h"
 #include "QwtErrorPlotCurve.h"
@@ -9771,10 +9772,12 @@ void ApplicationWindow::closeEvent(QCloseEvent *ce) {
     }
   }
 
-  // Stop background saving thread, so it doesn't try to use a destroyed
-  // resource
-  m_projectRecovery.stopProjectSaving();
-  m_projectRecovery.clearAllCheckpoints();
+  if (m_projectRecoveryRunOnStart) {
+    // Stop background saving thread, so it doesn't try to use a destroyed
+    // resource
+    m_projectRecovery.stopProjectSaving();
+    m_projectRecovery.clearAllCheckpoints();
+  }
 
   // Close the remaining MDI windows. The Python API is required to be active
   // when the MDI window destructor is called so that those references can be
@@ -16637,8 +16640,20 @@ void ApplicationWindow::onAboutToStart() {
   // Make sure we see all of the startup messages
   resultsLog->scrollToTop();
 
-  // Kick off project recovery
-  checkForProjectRecovery();
+  // Kick off project recovery iff we are able to determine if we are the only
+  // instance currently running
+  try {
+    if (!Process::isAnotherInstanceRunning()) {
+      checkForProjectRecovery();
+    } else {
+      g_log.debug("Another MantidPlot process is running. Project recovery is "
+                  "disabled.");
+    }
+  } catch (std::runtime_error &exc) {
+    g_log.warning("Unable to determine if other MantidPlot processes are "
+                  "running. Project recovery is disabled. Error msg: " +
+                  std::string(exc.what()));
+  }
 }
 
 /**
@@ -16768,7 +16783,13 @@ bool ApplicationWindow::saveProjectRecovery(std::string destination) {
   return projectWriter.save(QString::fromStdString(destination));
 }
 
+/**
+  * Checks for any recovery checkpoint and starts project
+  * saving if one doesn't exist. If one does, it prompts
+  * the user whether they would like to recover
+  */
 void ApplicationWindow::checkForProjectRecovery() {
+  m_projectRecoveryRunOnStart = true;
   if (!m_projectRecovery.checkForRecovery()) {
     m_projectRecovery.startProjectSaving();
     return;
diff --git a/MantidPlot/src/ApplicationWindow.h b/MantidPlot/src/ApplicationWindow.h
index ba7830dd663b9b52535dd1cd3b8c111d830922f8..1e0122798c3b7846121c284c68e50acb3d48b8fc 100644
--- a/MantidPlot/src/ApplicationWindow.h
+++ b/MantidPlot/src/ApplicationWindow.h
@@ -1642,6 +1642,8 @@ private:
 
   /// Owns a thread which automatically triggers project recovery for the GUI
   MantidQt::ProjectRecovery m_projectRecovery;
+  /// True if project recovery was started when MantidPlot started
+  bool m_projectRecoveryRunOnStart{false};
 
 #ifdef SHARED_MENUBAR
   QMenuBar *m_sharedMenuBar; ///< Pointer to the shared menubar
diff --git a/MantidPlot/src/Process.cpp b/MantidPlot/src/Process.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..20fc6d3ead9885a397efa48c784093a72ef7f3b9
--- /dev/null
+++ b/MantidPlot/src/Process.cpp
@@ -0,0 +1,125 @@
+// clang-format off
+#include "MantidQtWidgets/Common/PythonThreading.h"
+// clang-format on
+
+#include "Process.h"
+
+#include <iostream>
+#include <stdexcept>
+#include <QCoreApplication>
+
+namespace {
+
+class PyObjectNewReference {
+public:
+  explicit PyObjectNewReference(PyObject *object) : m_object(object) {}
+  ~PyObjectNewReference() { Py_XDECREF(m_object); }
+
+  PyObjectNewReference(const PyObjectNewReference &) = delete;
+  PyObjectNewReference &operator=(const PyObjectNewReference &) = delete;
+
+  PyObjectNewReference(PyObjectNewReference &&o) { *this = std::move(o); }
+
+  PyObjectNewReference &operator=(PyObjectNewReference &&other) {
+    this->m_object = other.m_object;
+    other.m_object = nullptr;
+    return *this;
+  }
+
+  inline PyObject *ptr() const { return m_object; }
+
+private:
+  PyObject *m_object;
+};
+
+/**
+ * @brief Retrieve a named attribute
+ * @param source The source object
+ * @param name The name of the attribute
+ * @return The attribute
+ * @throws std::runtime_error if an error occurs retrieving the attribute
+ */
+PyObjectNewReference attr(PyObject *source, const char *name) {
+  PyObjectNewReference attr(PyObject_GetAttrString(source, name));
+  if (attr.ptr()) {
+    return attr;
+  } else {
+    PyErr_Print();
+    throw std::runtime_error(std::string("Process: No attribute ") + name +
+                             " found");
+  }
+}
+
+/**
+ * @brief Call a named function with an check for errors
+ * @param source The source object
+ * @param name The name of the attribute to call
+ * @return The return value of the function
+ * @throws std::runtime_error if an error occurs retrieving the attribute
+ */
+PyObjectNewReference call(PyObject *source, const char *name) {
+  auto returnedAttr = attr(source, name);
+  auto result = PyObject_CallFunction(returnedAttr.ptr(), nullptr);
+  if (result)
+    return PyObjectNewReference(result);
+  else {
+    PyErr_Print();
+    throw std::runtime_error(std::string("Process: Error calling function ") +
+                             name);
+  }
+}
+
+/**
+ * @return Return a pointer to the psutil module. A new reference is returned.
+ */
+PyObjectNewReference psutil() {
+  if (auto process = PyImport_ImportModule("psutil")) {
+    return PyObjectNewReference(process);
+  } else {
+    PyErr_Clear();
+    throw std::runtime_error("Python module psutil cannot be imported.");
+  }
+}
+} // namespace
+
+namespace Process {
+
+/**
+  * Returns true is another instance of Mantid is running
+  * on this machine
+  * @return True if another instance is running
+  * @throws std::runtime_error if the PID list cannot be determined
+  */
+bool isAnotherInstanceRunning() { return !otherInstancePIDs().empty(); }
+
+/**
+ * @brief Return a list of process IDs for other instances of this process.
+ * @return A list of other processes running. The PID for this process is
+ * removed from the list. An empty list is returned
+ * if no other processes are running.
+ * @throws std::runtime_error if the PID list cannot be determined
+ */
+std::vector<int64_t> otherInstancePIDs() {
+  ScopedPythonGIL lock;
+  const int64_t ourPID(QCoreApplication::applicationPid());
+  const PyObjectNewReference ourName(
+      FROM_CSTRING(QCoreApplication::applicationName().toLatin1().data()));
+  auto psutilModule(psutil());
+  auto processIter(call(psutilModule.ptr(), "process_iter"));
+
+  std::vector<int64_t> otherPIDs;
+  PyObject *item(nullptr);
+  while ((item = PyIter_Next(processIter.ptr()))) {
+    auto name = call(item, "name");
+    if (PyObject_RichCompareBool(name.ptr(), ourName.ptr(), Py_EQ)) {
+      auto pid = PyLong_AsLong(attr(item, "pid").ptr());
+      if (pid != ourPID) {
+        otherPIDs.emplace_back(pid);
+      }
+    }
+    Py_DECREF(item);
+  }
+  return otherPIDs;
+}
+
+} // namespace Process
diff --git a/MantidPlot/src/Process.h b/MantidPlot/src/Process.h
new file mode 100644
index 0000000000000000000000000000000000000000..e7a467a8cd4258741bfc8a94ea1480e0f66ef530
--- /dev/null
+++ b/MantidPlot/src/Process.h
@@ -0,0 +1,39 @@
+#ifndef PROCESS_H_
+#define PROCESS_H_
+/*
+Copyright &copy; 2008-18 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge
+National Laboratory & European Spallation Source
+
+This file is part of Mantid.
+
+Mantid is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+
+Mantid is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+File change history is stored at: <https://github.com/mantidproject/mantid>
+Code Documentation is available at: <http://doxygen.mantidproject.org>
+*/
+#include <cstdint>
+#include <vector>
+
+/*
+ * A minimal wrapper around Python's psutil package to gather information
+ * about processes
+ */
+
+namespace Process {
+
+bool isAnotherInstanceRunning();
+
+std::vector<int64_t> otherInstancePIDs();
+}
+#endif // PROCESS_H_
diff --git a/MantidPlot/src/ProjectRecovery.cpp b/MantidPlot/src/ProjectRecovery.cpp
index 92593535c6c0451df551a51a814e9cc8f0f69742..3e8fb692394adf3f8d699fb49a2d89b5e8c7f4d9 100644
--- a/MantidPlot/src/ProjectRecovery.cpp
+++ b/MantidPlot/src/ProjectRecovery.cpp
@@ -16,8 +16,10 @@
 #include "boost/range/algorithm_ext/erase.hpp"
 
 #include "Poco/DirectoryIterator.h"
+#include "Poco/Environment.h"
 #include "Poco/NObserver.h"
 #include "Poco/Path.h"
+#include "Poco/Process.h"
 
 #include <QMessageBox>
 #include <QMetaObject>
@@ -62,8 +64,11 @@ boost::optional<bool> getConfigBool(const std::string &key) {
 
 /// Returns a string to the current top level recovery folder
 std::string getRecoveryFolder() {
-  static std::string recoverFolder =
-      Mantid::Kernel::ConfigService::Instance().getAppDataDir() + "/recovery/";
+  static std::string appData =
+      Mantid::Kernel::ConfigService::Instance().getAppDataDir();
+  static std::string hostname = Poco::Environment::nodeName();
+
+  static std::string recoverFolder = appData + "/recovery/" + hostname + '/';
   return recoverFolder;
 }