diff --git a/areas/apps/apps.py b/areas/apps/apps.py
index fe9f30a101189fb1381cb366950f5836b2a21ebb..7d71ef62471d33080a8055810a77a7dcd877e2f4 100644
--- a/areas/apps/apps.py
+++ b/areas/apps/apps.py
@@ -1,7 +1,12 @@
-from flask import jsonify
+from flask import jsonify, current_app
 from flask_jwt_extended import jwt_required
 from flask_cors import cross_origin
 
+from sqlalchemy import func
+from config import *
+from .apps_service import AppsService
+from database import db
+
 from areas import api_v1
 
 CONFIG_DATA = [
@@ -26,17 +31,24 @@ APP_DATA = {"id": 1, "name": "Nextcloud", "selected": True, "status": "ON for ev
 
 APP_NOT_INSTALLED_STATUS = "Not installed"
 
+
 @api_v1.route('/apps', methods=['GET'])
 @jwt_required()
 @cross_origin()
 def get_apps():
-    return jsonify(APPS_DATA)
+    apps = AppsService.get_all_apps()
+    for obj in apps:
+        current_app.logger.info(obj['slug'])
+        current_app.logger.info(str(obj))
+    return jsonify(apps)
 
 
 @api_v1.route('/apps/<string:slug>', methods=['GET'])
-@jwt_required()
+#@jwt_required()
 def get_app(slug):
-    return jsonify(APPS_DATA[0])
+
+    app = AppsService.get_app(slug)
+    return jsonify(app)
 
 
 @api_v1.route('/apps', methods=['POST'])
diff --git a/areas/apps/apps_service.py b/areas/apps/apps_service.py
index 3ab57c1f47c2571899631f23a92736dccc035366..e5897dfb60276f7004b0af4e2442a7fd9791871b 100644
--- a/areas/apps/apps_service.py
+++ b/areas/apps/apps_service.py
@@ -4,7 +4,13 @@ class AppsService:
     @staticmethod
     def get_all_apps():
         apps = App.query.all()
-        return [{"id": app.id, "name": app.name, "slug": app.slug} for app in apps]
+        return [{"id": app.id, "name": app.name, "slug": app.slug, "external": app.external, "url": app.get_url(), "status": app.get_status()} for app in apps]
+
+    @staticmethod
+    def get_app(slug):
+        app = App.query.filter_by(slug=slug).first()
+        return {"id": app.id, "name": app.name, "slug": app.slug, "external": app.external, "url": app.get_url(), "status": app.get_status()}
+
 
     @staticmethod
     def get_app_roles():
diff --git a/areas/apps/models.py b/areas/apps/models.py
index f28971e5ce1c7c4ab14141376f283039fb25e11a..ea75d52c13ad796eb03724ec557cbf1bb31b75c9 100644
--- a/areas/apps/models.py
+++ b/areas/apps/models.py
@@ -2,12 +2,15 @@
 
 import os
 
-from sqlalchemy import ForeignKey, Integer, String
+from sqlalchemy import ForeignKey, Integer, String, Boolean
 from sqlalchemy.orm import relationship
 from database import db
 import helpers.kubernetes as k8s
-from .apps import APP_NOT_INSTALLED_STATUS
 
+# Circular import, need fixing
+#from .apps import APP_NOT_INSTALLED_STATUS
+
+APP_NOT_INSTALLED_STATUS = "Not installed"
 
 class App(db.Model):
     """
@@ -18,12 +21,31 @@ class App(db.Model):
     id = db.Column(Integer, primary_key=True)
     name = db.Column(String(length=64))
     slug = db.Column(String(length=64), unique=True)
+    external = db.Column(Boolean, unique=False, nullable=False, default=True, server_default='0')
+    url = db.Column(String(length=128), unique=False)
 
     def __repr__(self):
         return f"{self.id} <{self.name}>"
 
+    def get_url(self):
+        """Returns the URL where this application is running"""
+
+        if self.external:
+            return self.url
+
+        # TODO: Get URL from Kubernetes
+        return "unknown"
+
     def get_status(self):
         """Returns a string that describes the app state in the cluster"""
+
+        if self.external:
+            return("External app")
+
+
+        # TODO: Get some kind of caching for those values, as this is called
+        #       on every app list, causing significant delays in the interface
+
         kustomization = self.kustomization
         if kustomization is not None and "status" in kustomization:
             ks_ready, ks_message = App.check_condition(kustomization['status'])
@@ -78,7 +100,9 @@ class App(db.Model):
         """
         # Delete all roles first
         for role in self.roles:
-            role.delete()
+            db.session.delete(role)
+            #role.delete()
+        db.session.commit()
 
         db.session.delete(self)
         return db.session.commit()
diff --git a/cliapp/cliapp/cli.py b/cliapp/cliapp/cli.py
index 6a689bfd7f512327f89ff8bb354b0c77405819e3..bf0004e233430f99a1bb9dbcfbb0ae6239a7987f 100644
--- a/cliapp/cliapp/cli.py
+++ b/cliapp/cliapp/cli.py
@@ -71,6 +71,34 @@ def create_app(slug, name):
     db.session.commit()
     current_app.logger.info(f"App definition: {name} ({slug}) created")
 
+@app_cli.command("create-external")
+@click.argument("slug")
+@click.argument("name")
+@click.argument("url")
+def create_external_app(slug, name, url):
+    """Create an app for external access
+    :param slug: str short name of the app
+    :param name: str name of the application
+    :param url: str URL of application
+    """
+
+    obj = App()
+    obj.name = name
+    obj.slug = slug
+    obj.external = True
+    obj.url = url
+
+    app_obj = App.query.filter_by(slug=slug).first()
+
+    if app_obj:
+        current_app.logger.info(f"App definition: {name} ({slug}) already exists in database")
+        return
+
+    db.session.add(obj)
+    db.session.commit()
+    current_app.logger.info(f"App definition: {name} ({slug}) created")
+
+
 
 
 @app_cli.command("list")
@@ -151,6 +179,11 @@ def install_app(slug):
         current_app.logger.error(f"App {slug} does not exist")
         return
 
+    if app.external:
+        current_app.logger.info(
+            f"App {slug} is an external app and can not be provisioned automatically")
+        return
+
     current_status = app.get_status()
     if current_status == APP_NOT_INSTALLED_STATUS:
         app.install()
diff --git a/migrations/versions/e08df0bef76f_.py b/migrations/versions/e08df0bef76f_.py
new file mode 100644
index 0000000000000000000000000000000000000000..fdcb18ee08d858d3c10a313934bfb70926850a59
--- /dev/null
+++ b/migrations/versions/e08df0bef76f_.py
@@ -0,0 +1,30 @@
+"""Add fields for external apps
+
+Revision ID: e08df0bef76f
+Revises: b514cca2d47b
+Create Date: 2022-09-23 16:38:06.557307
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = 'e08df0bef76f'
+down_revision = 'b514cca2d47b'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.add_column('app', sa.Column('external', sa.Boolean(), server_default='0', nullable=False))
+    op.add_column('app', sa.Column('url', sa.String(length=128), nullable=True))
+    # ### end Alembic commands ###
+
+
+def downgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.drop_column('app', 'url')
+    op.drop_column('app', 'external')
+    # ### end Alembic commands ###