diff --git a/operations/cleanup_script.py b/operations/cleanup_script.py
new file mode 100644
index 0000000000000000000000000000000000000000..b25742e822af5ce90a737910057e164a06f8c6e7
--- /dev/null
+++ b/operations/cleanup_script.py
@@ -0,0 +1,35 @@
+# ensure compatability with Spack v0.18.0
+try:
+    from spack.package_base import PackageStillNeededError
+except:
+    from spack.package import PackageStillNeededError
+
+needed = set()
+# keep packages that are part of an environment
+for e in spack.environment.all_environments():
+    needed.update(e.all_hashes())
+# also keep packages that provide compilers (and their dependencies)
+for c in spack.compilers.all_compiler_specs():
+    pkg_spec = spack.compilers.pkg_spec_for_compiler(c)
+    needed.update([dep.dag_hash() for pkg in spack.store.db.query(pkg_spec) for dep in pkg.traverse()])
+
+installed = {spec.dag_hash(): spec for spec in spack.store.db.query()}
+to_remove = set(installed) - needed
+
+# this will try to uninstall ANY package that is not part of an environment
+# including test dependencies
+# for a list of all the packages that will be uninstalled:
+print('The following packages will be uninstalled:')
+print('\n'.join(sorted([installed[h].short_spec+' ('+h+')' for h in to_remove])))
+
+# iteratively uninstall packages with no dependents
+while to_remove:
+    # print('New iteration')
+    for hash in to_remove.copy():
+        spec = installed[hash]
+        try:
+            spec.package.do_uninstall()
+            to_remove.discard(hash)
+        except PackageStillNeededError:
+            pass
+            # print('Not ready to remove', spec.short_spec, 'in this iteration')