diff --git a/backend/app.py b/backend/app.py
index e07a39d99e92b62375dae26cef1a1ad13dad8883..b52802995bc37c158e3b0c4ff18b9017b55e2981 100644
--- a/backend/app.py
+++ b/backend/app.py
@@ -141,18 +141,20 @@ def init_routines():
     # We have this single "provisioning worker" so there will be only one
     # provisioning operation at a time. We use an Event to signal a
     # provisioning request.
-    def wait_for_provision_event():
+    def provisioning_loop():
         while True:
-            helpers.threads.provision_event.wait()
-            helpers.threads.provision_event.clear()
+            app.logger.info("Waiting for next provisioning run.")
+            # helpers.threads.provision_event.wait()
+            # helpers.threads.provision_event.clear()
+            helpers.threads.wait_provision()
             app.logger.info("Starting provisioning.")
             with app.app_context():
                 try:
                     provisioner.reconcile()
                 except Exception as e:
-                    app.logger.warn(f"Exception in user provisioning:")
+                    app.logger.warn(f"Exception during user provisioning:")
                     app.logger.warn(traceback.format_exc())
-    threading.Thread(target=wait_for_provision_event).start()
+    threading.Thread(target=provisioning_loop).start()
 
 # `init_routines` should only run once per dashboard instance. To enforce this
 # we have different behaviour for production and development mode:
diff --git a/backend/areas/apps/apps_service.py b/backend/areas/apps/apps_service.py
index dc88deb4171dc5de2673ffaba7202566262914b0..98971d8ce13e2bd0e4ad07a387645470bb9b43bd 100644
--- a/backend/areas/apps/apps_service.py
+++ b/backend/areas/apps/apps_service.py
@@ -13,6 +13,7 @@ from database import db
 from helpers.access_control import user_has_access
 from helpers.kratos_user import KratosUser
 import helpers.kubernetes as k8s
+from helpers.threads import request_provision
 
 class AppsService:
     @staticmethod
@@ -82,3 +83,5 @@ class AppsService:
                     )
                     db.session.add(app_role)
                     db.session.commit()
+            ca.logger.info("Requesting user provisioning.")
+            request_provision()
diff --git a/backend/areas/apps/models.py b/backend/areas/apps/models.py
index fd89c963fc705036e8028148f1cd0c404fe43cf5..e9330ab92bafd1eeb36cd93235f405ce68cceb34 100644
--- a/backend/areas/apps/models.py
+++ b/backend/areas/apps/models.py
@@ -92,7 +92,6 @@ class App(db.Model):
     def install(self):
         """Creates a Kustomization in the Kubernetes cluster that installs this application"""
         # Create add-<app> kustomization
-        print("Creating app kustomization.")
         self.__create_kustomization()
 
     def uninstall(self):
diff --git a/backend/cliapp/cliapp/cli.py b/backend/cliapp/cliapp/cli.py
index ee588e74f36c3c0e40d70f0192a350f1f78f4c70..38d5a20f82ea43cf321c3e9ef3344e59792b1a84 100644
--- a/backend/cliapp/cliapp/cli.py
+++ b/backend/cliapp/cliapp/cli.py
@@ -203,7 +203,7 @@ def install_app(slug):
     if not current_status.installed:
         AppsService.install_app(app)
         current_app.logger.info(
-            f"App {slug} installing... use `status` to see status")
+            f"App {slug} installing...")
     else:
         current_app.logger.error(f"App {slug} is already installed")
 
diff --git a/backend/helpers/threads.py b/backend/helpers/threads.py
index 4ee12c880b31b0ccaca20df2f2f600a52bfafc32..0a4e6ad6c0db9ee9341e9de2704e8903fd9f4829 100644
--- a/backend/helpers/threads.py
+++ b/backend/helpers/threads.py
@@ -1,8 +1,9 @@
 import functools
+from posix_ipc import MessageQueue, O_CREAT, BusyError
 import threading
 
 # Signal to provisioning loop that we want to provision now.
-provision_event = threading.Event()
+provisioning_queue = MessageQueue('/stackspin-dashboard-provision-queue', O_CREAT)
 
 def debounce(timeout: float):
     def decorator(func):
@@ -17,4 +18,11 @@ def debounce(timeout: float):
 
 @debounce(1)
 def request_provision():
-    provision_event.set()
+    try:
+        provisioning_queue.send("provision", timeout=0)
+    except BusyError:
+        # If we can't signal for some reason, silently fail.
+        pass
+
+def wait_provision():
+    provisioning_queue.receive()
diff --git a/backend/requirements.txt b/backend/requirements.txt
index 244e4e8b576dc4e66de300dfda2b6cc84ab89137..b06a97967da972a7109d0132353666c5ed723f5c 100644
--- a/backend/requirements.txt
+++ b/backend/requirements.txt
@@ -28,6 +28,7 @@ ory-kratos-client==1.0.0
 ory-hydra-client==1.11.8
 pathspec==0.9.0
 platformdirs==2.5.1
+posix-ipc==1.1.1
 pycparser==2.21
 PyJWT==2.3.0
 pymysql==1.0.2