diff --git a/areas/apps/models.py b/areas/apps/models.py
index 0858592447b438e5dfd0e4ac8bc8327241682fe7..23a6249cf02c48e582e42f6e3bb111f45c37150d 100644
--- a/areas/apps/models.py
+++ b/areas/apps/models.py
@@ -58,9 +58,9 @@ class App(db.Model):
         if ks_ready and hr_ready:
             return "App installed and running"
         if not hr_ready:
-            return f"App failed installing: {hr_message}"
+            return f"App HelmRelease status: {hr_message}"
         if not ks_ready:
-            return f"App failed installing: {ks_message}"
+            return f"App Kustomization status: {ks_message}"
         return "App is installing..."
 
 
@@ -71,6 +71,32 @@ class App(db.Model):
         # Create add-<app> kustomization
         self.__create_kustomization()
 
+    def delete(self):
+        """
+        Fully deletes an application
+
+        This includes user roles, all kubernetes objects and also PVCs, so your
+        data will be *gone*
+        """
+        # Delete all roles first
+        for role in self.roles:
+            db.session.delete(role)
+
+        # Delete the kustomization
+        if self.__delete_kustomization():
+
+        # TODO: This is where we might want to poll for status changes in the
+        # app, so that only once the kustomization and all its stuff (other ks,
+        # helmrelease, etc.) is deleted, we continue
+
+        # If the kustomization delete went well, commit DB changes.
+            db.session.commit()
+            # Then delete the app
+            db.session.delete(self)
+            db.session.commit()
+            return True
+        return False
+
     def __generate_secrets(self):
         """Generates passwords for app installation"""
         # Create app variables secret
@@ -90,11 +116,10 @@ class App(db.Model):
                 "add-app-kustomization.yaml.jinja")
         k8s.store_kustomization(kustomization_template_filepath, self.slug)
 
+    def __delete_kustomization(self):
+        """Deletes kustomization for this app"""
+        k8s.delete_kustomization(f"add-{self.slug}")
 
-    @staticmethod
-    def __get_templates_dir():
-        """Returns directory that contains the Jinja templates used to create app secrets."""
-        return os.path.join(os.path.dirname(os.path.realpath(__file__)), "templates")
 
     @property
     def variables_template_filepath(self):
@@ -116,6 +141,20 @@ class App(db.Model):
             return 'stackspin-apps'
         return 'stackspin'
 
+    @property
+    def roles(self):
+        """
+        All roles that are linked to this app
+        """
+        return AppRole.query.filter_by(
+            app_id=self.id
+        ).all()
+
+    @staticmethod
+    def __get_templates_dir():
+        """Returns directory that contains the Jinja templates used to create app secrets."""
+        return os.path.join(os.path.dirname(os.path.realpath(__file__)), "templates")
+
     @staticmethod
     def check_condition(status):
         """
diff --git a/cliapp/cliapp/cli.py b/cliapp/cliapp/cli.py
index e442421190986303510c866ca139cdd16078e172..ba564c139e5ac2d66a091612ad652de2c487e1ef 100644
--- a/cliapp/cliapp/cli.py
+++ b/cliapp/cliapp/cli.py
@@ -88,27 +88,23 @@ def list_app():
 )
 @click.argument("slug")
 def delete_app(slug):
-    """Removes app from database
+    """Removes app from database as well as uninstalls it from the cluster
     :param slug: str Slug of app to remove
     """
     current_app.logger.info(f"Trying to delete app: {slug}")
-    obj = App.query.filter_by(slug=slug).first()
+    app_obj = App.query.filter_by(slug=slug).first()
 
-    if not obj:
+    if not app_obj:
         current_app.logger.info("Not found")
         return
 
-    # Deleting will (probably) fail if there are still roles attached. This is a
-    # PoC implementation only. Actually management of apps and roles will be
-    # done by the backend application
-    db.session.delete(obj)
-    db.session.commit()
-    current_app.logger.info("Success")
+    deleted = app_obj.delete()
+    current_app.logger.info(f"Success: {deleted}")
     return
 
-@app_cli.command("get_status")
+@app_cli.command("status")
 @click.argument("slug")
-def get_status_app(slug):
+def status_app(slug):
     """Gets the current app status from the Kubernetes cluster
     :param slug: str Slug of app to remove
     """
@@ -125,8 +121,8 @@ def get_status_app(slug):
 @app_cli.command("install")
 @click.argument("slug")
 def install_app(slug):
-    """Gets the current app status from the Kubernetes cluster
-    :param slug: str Slug of app to remove
+    """Installs app into Kubernetes cluster
+    :param slug: str Slug of app to install
     """
     current_app.logger.info(f"Installing app: {slug}")
 
@@ -140,11 +136,28 @@ def install_app(slug):
     if current_status == APP_NOT_INSTALLED_STATUS:
         app.install()
         current_app.logger.info(
-            f"App {slug} installing... use `get_status` to see status")
+            f"App {slug} installing... use `status` to see status")
     else:
         current_app.logger.error("App {slug} should have status"
             f" {APP_NOT_INSTALLED_STATUS} but has status: {current_status}")
 
+@app_cli.command("roles")
+@click.argument("slug")
+def roles_app(slug):
+    """Gets a list of roles for this app
+    :param slug: str Slug of app queried
+    """
+    current_app.logger.info(f"Getting roles for app: {slug}")
+
+    app = App.query.filter_by(slug=slug).first()
+
+    if not app:
+        current_app.logger.error(f"App {slug} does not exist")
+        return
+
+    current_app.logger.info("Roles: ")
+    for role in app.roles:
+        current_app.logger.info(role)
 
 
 cli.cli.add_command(app_cli)
diff --git a/docker-compose.yml b/docker-compose.yml
index c730f1386bf9e095315d45f64d95321febfc1c66..e261b4850d680845d2d691f51801d8e79c219460 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -31,7 +31,7 @@ services:
       # ENV variables that are deployment-specific
       - SECRET_KEY=$FLASK_SECRET_KEY
       - HYDRA_CLIENT_SECRET=$HYDRA_CLIENT_SECRET
-      # - OAUTHLIB_INSECURE_TRANSPORT=1
+      - KUBECONFIG=/.kube/config
     ports:
       - "5000:5000"
     user: "${KUBECTL_UID}:${KUBECTL_GID}"
diff --git a/helpers/kubernetes.py b/helpers/kubernetes.py
index d8f9b7ba6ece9716c184e6cbe6b2b8ea2c2f6788..3169c63de83e59bf0dae4c4729b1d69a0767d682 100644
--- a/helpers/kubernetes.py
+++ b/helpers/kubernetes.py
@@ -13,6 +13,8 @@ from kubernetes.client.exceptions import ApiException
 from kubernetes.utils import create_from_yaml
 from kubernetes.utils.create_from_yaml import FailToCreateError
 
+# Load the kube config once
+config.load_kube_config()
 
 def create_variables_secret(app_slug, variables_filepath):
     """Checks if a variables secret for app_name already exists, generates it if necessary.
@@ -117,19 +119,31 @@ def store_kustomization(kustomization_template_filepath, app_slug):
             namespace="flux-system",
             plural="kustomizations",
             body=kustomization_dict)
-
-
-                # create_from_yaml(
-                # api_client_instance,
-                # yaml_objects=[kustomization_dict],
-                # # All kustomizations live in the flux-system namespace
-                # namespace="flux-system"
-                # )
     except FailToCreateError as ex:
         print(f"Could not create {app_slug} Kustomization because of exception {ex}")
         return
     print(f"Kustomization created with api response: {api_response}")
 
+def delete_kustomization(kustomization_name):
+    """Deletes kustomization for an app_slug. Should also result in the
+    deletion of the app's HelmReleases, PVCs, OAuth2Client, etc. Nothing will
+    remain"""
+    api_client_instance = api_client.ApiClient()
+    custom_objects_api = client.CustomObjectsApi(api_client_instance)
+    body = client.V1DeleteOptions()
+    try:
+        api_response = custom_objects_api.delete_namespaced_custom_object(
+            group="kustomize.toolkit.fluxcd.io",
+            version="v1beta2",
+            namespace="flux-system",
+            plural="kustomizations",
+            name=kustomization_name,
+            body=body)
+    except ApiException as ex:
+        print(f"Could not delete {kustomization_name} Kustomization because of exception {ex}")
+        return False
+    print(f"Kustomization deleted with api response: {api_response}")
+
 
 def read_template_to_dict(template_filepath, template_globals):
     """Reads a Jinja2 template that contains yaml and turns it into a dict
@@ -198,7 +212,6 @@ def get_all_kustomizations(namespace='flux-system'):
     :return: Kustomizations as returned by CustomObjectsApi.list_namespaced_custom_object()
     :rtype: object
     """
-    config.load_kube_config()
     api = client.CustomObjectsApi()
     api_response = api.list_namespaced_custom_object(
         group="kustomize.toolkit.fluxcd.io",
@@ -231,7 +244,6 @@ def get_all_helmreleases(namespace='stackspin'):
     :return: Helmreleases as returned by CustomObjectsApi.list_namespaced_custom_object()
     :rtype: object
     """
-    config.load_kube_config()
     api = client.CustomObjectsApi()
     api_response = api.list_namespaced_custom_object(
         group="helm.toolkit.fluxcd.io",
@@ -244,7 +256,6 @@ def get_all_helmreleases(namespace='stackspin'):
 
 def get_kustomization(name, namespace='flux-system'):
     """Returns all info of a Flux kustomization with name 'name'"""
-    config.load_kube_config()
     api = client.CustomObjectsApi()
     try:
         resource = api.get_namespaced_custom_object(
@@ -264,7 +275,6 @@ def get_kustomization(name, namespace='flux-system'):
 
 def get_helmrelease(name, namespace='stackspin-apps'):
     """Returns all info of a Flux helmrelease with name 'name'"""
-    config.load_kube_config()
     api = client.CustomObjectsApi()
     try:
         resource = api.get_namespaced_custom_object(