diff --git a/manifests/nbiservice.yaml b/manifests/nbiservice.yaml
index 5c954d1bb4d5f0fb53097fe3a8db51fb75bcccfe..108195d377d9958203ee36f1fe6e949eda34a197 100644
--- a/manifests/nbiservice.yaml
+++ b/manifests/nbiservice.yaml
@@ -23,9 +23,6 @@ spec:
   replicas: 1
   template:
     metadata:
-      annotations:
-        config.linkerd.io/skip-inbound-ports: "8762"
-        config.linkerd.io/skip-outbound-ports: "8762"
       labels:
         app: nbiservice
     spec:
@@ -36,22 +33,28 @@ spec:
           imagePullPolicy: Always
           ports:
             - containerPort: 8080
-            - containerPort: 9090
             - containerPort: 9192
-            - containerPort: 8761
           env:
             - name: LOG_LEVEL
               value: "INFO"
+            - name: FLASK_ENV
+              value: "production"  # change to "development" if developing
             - name: IETF_NETWORK_RENDERER
               value: "LIBYANG"
-            - name: WS_IP_PORT
-              value: "8761"
           readinessProbe:
-            exec:
-              command: ["/bin/grpc_health_probe", "-addr=:9090"]
+            httpGet:
+              path: /healthz
+              port: 8080
+            initialDelaySeconds: 5
+            periodSeconds: 10
+            failureThreshold: 3
           livenessProbe:
-            exec:
-              command: ["/bin/grpc_health_probe", "-addr=:9090"]
+            httpGet:
+              path: /healthz
+              port: 8080
+            initialDelaySeconds: 5
+            periodSeconds: 10
+            failureThreshold: 3
           resources:
             requests:
               cpu: 50m
@@ -75,15 +78,7 @@ spec:
       protocol: TCP
       port: 8080
       targetPort: 8080
-    - name: grpc
-      protocol: TCP
-      port: 9090
-      targetPort: 9090
     - name: metrics
       protocol: TCP
       port: 9192
       targetPort: 9192
-    - name: websocket
-      protocol: TCP
-      port: 8761
-      targetPort: 8761
diff --git a/proto/nbi.proto b/proto/nbi.proto
deleted file mode 100644
index a81df70ef20e0e3389047a9fd8a6bab927b74114..0000000000000000000000000000000000000000
--- a/proto/nbi.proto
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-syntax = "proto3";
-package nbi;
-
-import "context.proto";
-
-service NbiService {
-  rpc CheckCredentials                 (context.TeraFlowController) returns (context.AuthenticationResult) {}
-  rpc GetConnectivityServiceStatus     (context.ServiceId         ) returns (context.ServiceStatus       ) {}
-  rpc CreateConnectivityService        (context.Service           ) returns (context.ServiceId           ) {}
-  rpc EditConnectivityService          (context.Service           ) returns (context.ServiceId           ) {}
-  rpc DeleteConnectivityService        (context.Service           ) returns (context.Empty               ) {}
-  rpc GetAllActiveConnectivityServices (context.Empty             ) returns (context.ServiceIdList       ) {}
-  rpc ClearAllConnectivityServices     (context.Empty             ) returns (context.Empty               ) {}
-}
diff --git a/src/common/Constants.py b/src/common/Constants.py
index 68200764610b5fded328dbec741fdbbf70bc0930..6508ac151e489db71d3f2ef176b220b23a420186 100644
--- a/src/common/Constants.py
+++ b/src/common/Constants.py
@@ -87,7 +87,6 @@ DEFAULT_SERVICE_GRPC_PORTS = {
     ServiceNameEnum.POLICY                 .value :  6060,
     ServiceNameEnum.MONITORING             .value :  7070,
     ServiceNameEnum.DLT                    .value :  8080,
-    ServiceNameEnum.NBI                    .value :  9090,
     ServiceNameEnum.L3_CAD                 .value : 10001,
     ServiceNameEnum.L3_AM                  .value : 10002,
     ServiceNameEnum.DBSCANSERVING          .value : 10008,
diff --git a/src/nbi/.gitlab-ci.yml b/src/nbi/.gitlab-ci.yml
index 71bf223ba9408e178e252d600c625dc2256dbe92..d97fab701c9560ea3d7e5c04b60bbf35292a9d9b 100644
--- a/src/nbi/.gitlab-ci.yml
+++ b/src/nbi/.gitlab-ci.yml
@@ -62,7 +62,7 @@ unit_test nbi:
       fi
   script:
     - docker pull "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
-    - docker run --name $IMAGE_NAME -d -p 9090:9090 -v "$PWD/src/$IMAGE_NAME/tests:/opt/results" --network=teraflowbridge $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG
+    - docker run --name $IMAGE_NAME -d -v "$PWD/src/$IMAGE_NAME/tests:/opt/results" --network=teraflowbridge $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG
     - sleep 5
     - docker ps -a
     - docker logs $IMAGE_NAME
diff --git a/src/nbi/Dockerfile b/src/nbi/Dockerfile
index a9be06d37c15757f51d6d849f395d683885e9508..5f5aef914c7f20df4bb2022b424cafad0a417bc7 100644
--- a/src/nbi/Dockerfile
+++ b/src/nbi/Dockerfile
@@ -16,7 +16,7 @@ FROM python:3.9-slim
 
 # Install dependencies
 RUN apt-get --yes --quiet --quiet update && \
-    apt-get --yes --quiet --quiet install wget g++ git build-essential cmake libpcre2-dev python3-dev python3-cffi && \
+    apt-get --yes --quiet --quiet install g++ git build-essential cmake libpcre2-dev python3-dev python3-cffi && \
     rm -rf /var/lib/apt/lists/*
 
 # Download, build and install libyang. Note that APT package is outdated
@@ -37,23 +37,11 @@ RUN ldconfig
 # Set Python to show logs as they occur
 ENV PYTHONUNBUFFERED=0
 
-# Download the gRPC health probe
-RUN GRPC_HEALTH_PROBE_VERSION=v0.2.0 && \
-    wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \
-    chmod +x /bin/grpc_health_probe
-
 # Get generic Python packages
 RUN python3 -m pip install --upgrade pip
 RUN python3 -m pip install --upgrade setuptools wheel
 RUN python3 -m pip install --upgrade pip-tools
 
-# Get common Python packages
-# Note: this step enables sharing the previous Docker build steps among all the Python components
-WORKDIR /var/teraflow
-COPY common_requirements.in common_requirements.in
-RUN pip-compile --quiet --output-file=common_requirements.txt common_requirements.in
-RUN python3 -m pip install -r common_requirements.txt
-
 # Add common files into working directory
 WORKDIR /var/teraflow/common
 COPY src/common/. ./
@@ -94,4 +82,5 @@ RUN mkdir -p /var/teraflow/tests/tools
 COPY src/tests/tools/mock_osm/. tests/tools/mock_osm/
 
 # Start the service
-ENTRYPOINT ["python", "-m", "nbi.service"]
+#ENTRYPOINT ["gunicorn", "-w", "4", "--worker-class", "eventlet", "-b", "0.0.0.0:8080", "nbi.service:nbi_app"]
+ENTRYPOINT ["gunicorn", "-w", "4", "--worker-class", "geventwebsocket.gunicorn.workers.GeventWebSocketWorker", "-b", "0.0.0.0:8080", "nbi.service.app:nbi_app"]
diff --git a/src/nbi/client/NbiClient.py b/src/nbi/client/NbiClient.py
deleted file mode 100644
index 043035c5a4b0612652b1473ac6deeaa9e3cfce19..0000000000000000000000000000000000000000
--- a/src/nbi/client/NbiClient.py
+++ /dev/null
@@ -1,96 +0,0 @@
-# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import grpc, logging
-from common.Constants import ServiceNameEnum
-from common.Settings import get_service_host, get_service_port_grpc
-from common.proto.nbi_pb2_grpc import NbiServiceStub
-from common.proto.context_pb2 import (
-    AuthenticationResult, Empty, Service, ServiceId, ServiceIdList, ServiceStatus, TeraFlowController)
-from common.tools.client.RetryDecorator import retry, delay_exponential
-from common.tools.grpc.Tools import grpc_message_to_json_string
-
-LOGGER = logging.getLogger(__name__)
-MAX_RETRIES = 15
-DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0)
-RETRY_DECORATOR = retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
-
-class NbiClient:
-    def __init__(self, host=None, port=None):
-        if not host: host = get_service_host(ServiceNameEnum.NBI)
-        if not port: port = get_service_port_grpc(ServiceNameEnum.NBI)
-        self.endpoint = '{:s}:{:s}'.format(str(host), str(port))
-        LOGGER.debug('Creating channel to {:s}...'.format(str(self.endpoint)))
-        self.channel = None
-        self.stub = None
-        self.connect()
-        LOGGER.debug('Channel created')
-
-    def connect(self):
-        self.channel = grpc.insecure_channel(self.endpoint)
-        self.stub = NbiServiceStub(self.channel)
-
-    def close(self):
-        if self.channel is not None: self.channel.close()
-        self.channel = None
-        self.stub = None
-
-    @RETRY_DECORATOR
-    def CheckCredentials(self, request : TeraFlowController) -> AuthenticationResult:
-        LOGGER.debug('CheckCredentials request: {:s}'.format(grpc_message_to_json_string(request)))
-        response = self.stub.CheckCredentials(request)
-        LOGGER.debug('CheckCredentials result: {:s}'.format(grpc_message_to_json_string(response)))
-        return response
-
-    @RETRY_DECORATOR
-    def GetConnectivityServiceStatus(self, request : ServiceId) -> ServiceStatus:
-        LOGGER.debug('GetConnectivityServiceStatus request: {:s}'.format(grpc_message_to_json_string(request)))
-        response = self.stub.GetConnectivityServiceStatus(request)
-        LOGGER.debug('GetConnectivityServiceStatus result: {:s}'.format(grpc_message_to_json_string(response)))
-        return response
-
-    @RETRY_DECORATOR
-    def CreateConnectivityService(self, request : Service) -> ServiceId:
-        LOGGER.debug('CreateConnectivityService request: {:s}'.format(grpc_message_to_json_string(request)))
-        response = self.stub.CreateConnectivityService(request)
-        LOGGER.debug('CreateConnectivityService result: {:s}'.format(grpc_message_to_json_string(response)))
-        return response
-
-    @RETRY_DECORATOR
-    def EditConnectivityService(self, request : Service) -> ServiceId:
-        LOGGER.debug('EditConnectivityService request: {:s}'.format(grpc_message_to_json_string(request)))
-        response = self.stub.EditConnectivityService(request)
-        LOGGER.debug('EditConnectivityService result: {:s}'.format(grpc_message_to_json_string(response)))
-        return response
-
-    @RETRY_DECORATOR
-    def DeleteConnectivityService(self, request : Service) -> Empty:
-        LOGGER.debug('DeleteConnectivityService request: {:s}'.format(grpc_message_to_json_string(request)))
-        response = self.stub.DeleteConnectivityService(request)
-        LOGGER.debug('DeleteConnectivityService result: {:s}'.format(grpc_message_to_json_string(response)))
-        return response
-
-    @RETRY_DECORATOR
-    def GetAllActiveConnectivityServices(self, request : Empty) -> ServiceIdList:
-        LOGGER.debug('GetAllActiveConnectivityServices request: {:s}'.format(grpc_message_to_json_string(request)))
-        response = self.stub.GetAllActiveConnectivityServices(request)
-        LOGGER.debug('GetAllActiveConnectivityServices result: {:s}'.format(grpc_message_to_json_string(response)))
-        return response
-
-    @RETRY_DECORATOR
-    def ClearAllConnectivityServices(self, request : Empty) -> Empty:
-        LOGGER.debug('ClearAllConnectivityServices request: {:s}'.format(grpc_message_to_json_string(request)))
-        response = self.stub.ClearAllConnectivityServices(request)
-        LOGGER.debug('ClearAllConnectivityServices result: {:s}'.format(grpc_message_to_json_string(response)))
-        return response
diff --git a/src/nbi/requirements.in b/src/nbi/requirements.in
index d56ee821b19894e435368cd6250b25d3bdc33c10..431dbf2d2a869aaea4b2f20ca91c3ad6d1a8f7e5 100644
--- a/src/nbi/requirements.in
+++ b/src/nbi/requirements.in
@@ -14,10 +14,14 @@
 
 deepdiff==6.7.*
 deepmerge==1.1.*
+eventlet
 Flask==2.1.3
 Flask-HTTPAuth==4.5.0
 Flask-RESTful==0.3.9
+flask-socketio
 jsonschema==4.4.0
+gevent
+gunicorn
 libyang==2.8.4
 netaddr==0.9.0
 pyang==2.6.0
@@ -25,4 +29,19 @@ git+https://github.com/robshakir/pyangbind.git
 pydantic==2.6.3
 requests==2.27.1
 werkzeug==2.3.7
-websockets==12.0
+#websockets==12.0
+
+# from common_requirements; take required ones
+#coverage==6.3
+#grpcio==1.47.*
+#grpcio-health-checking==1.47.*
+#grpcio-reflection==1.47.*
+#grpcio-tools==1.47.*
+#grpclib==0.4.4
+#prettytable==3.5.0
+#prometheus-client==0.13.0
+#protobuf==3.20.*
+#pytest==6.2.5
+#pytest-benchmark==3.4.1
+#python-dateutil==2.8.2
+#pytest-depends==1.0.1
diff --git a/src/nbi/service/NbiApplication.py b/src/nbi/service/NbiApplication.py
new file mode 100644
index 0000000000000000000000000000000000000000..5c2c00eac47765e83faadbf4f167978f83db2e7f
--- /dev/null
+++ b/src/nbi/service/NbiApplication.py
@@ -0,0 +1,63 @@
+import logging, time
+from typing import Any, Optional
+from flask import Flask, request
+from flask_restful import Api, Resource
+from flask_socketio import Namespace, SocketIO
+
+
+LOGGER = logging.getLogger(__name__)
+
+def log_request(response):
+    timestamp = time.strftime('[%Y-%b-%d %H:%M]')
+    LOGGER.info(
+        '%s %s %s %s %s', timestamp, request.remote_addr, request.method,
+        request.full_path, response.status
+    )
+    return response
+
+class NbiApplication:
+    def __init__(self, base_url : Optional[str] = None) -> None:
+        if base_url is None: base_url = ''
+        self.base_url = base_url
+
+        self.app = Flask(__name__)
+        self.app.after_request(log_request)
+        self.api = Api(self.app, prefix=base_url)
+        #websocket_path = '/'.join([base_url.rstrip('/'), 'websocket'])
+        self.sio = SocketIO(self.app, path=base_url, cors_allowed_origins="*")
+
+    def add_rest_api_resource(self, resource_class : Resource, *urls, **kwargs) -> None:
+        self.api.add_resource(resource_class, *urls, **kwargs)
+
+    def add_websocket_namespace(self, namespace_class : Namespace, namespace_url : str) -> None:
+        self.sio.on_namespace(namespace_class(namespace=namespace_url))
+
+    def websocket_emit_message(
+        self, event : str, *args : Any, namespace : str = "/", to : Optional[str] = None
+    ) -> None:
+        self.sio.emit(event, *args, namespace=namespace, to=to)
+
+    def dump_configuration(self) -> None:
+        LOGGER.debug('Configured Resources:')
+        for resource in self.api.resources:
+            LOGGER.debug(' - {:s}'.format(str(resource)))
+
+        LOGGER.debug('Configured Rules:')
+        for rule in self.app.url_map.iter_rules():
+            LOGGER.debug(' - {:s}'.format(str(rule)))
+
+    def run_standalone(
+        self, bind_address : Optional[str] = None, bind_port : Optional[int] = None
+    ) -> None:
+        # Run method used when started in a standalone mode, i.e., outside gunicorn or
+        # similar WSGI HTTP servers. Otherwise, use mechanism defined by the used
+        # WSGI HTTP server.
+
+        #logging.getLogger('werkzeug').setLevel(logging.WARNING)
+
+        endpoint = 'http://{:s}:{:s}'.format(str(bind_address), str(bind_port))
+        if self.base_url is not None:
+            endpoint = '/'.join([endpoint.rstrip('/'), self.base_url])
+
+        LOGGER.info('Listening on {:s}...'.format(endpoint))
+        self.sio.run(self.app, host=bind_address, port=bind_port)
diff --git a/src/nbi/service/NbiService.py b/src/nbi/service/NbiService.py
deleted file mode 100644
index fe7bf2e7b22271ea27746084ebee831257aa43d0..0000000000000000000000000000000000000000
--- a/src/nbi/service/NbiService.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from common.Constants import ServiceNameEnum
-from common.Settings import get_service_port_grpc
-from common.proto.nbi_pb2_grpc import add_NbiServiceServicer_to_server
-from common.tools.service.GenericGrpcService import GenericGrpcService
-from nbi.service.NbiServiceServicerImpl import NbiServiceServicerImpl
-
-class NbiService(GenericGrpcService):
-    def __init__(self, cls_name: str = __name__) -> None:
-        port = get_service_port_grpc(ServiceNameEnum.NBI)
-        super().__init__(port, cls_name=cls_name)
-        self.nbi_servicer = NbiServiceServicerImpl()
-
-    def install_servicers(self):
-        add_NbiServiceServicer_to_server(self.nbi_servicer, self.server)
diff --git a/src/nbi/service/NbiServiceServicerImpl.py b/src/nbi/service/NbiServiceServicerImpl.py
deleted file mode 100644
index 5719b67e4bb7a151d1f668132c7a8dc7073d4948..0000000000000000000000000000000000000000
--- a/src/nbi/service/NbiServiceServicerImpl.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import grpc, logging
-from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method
-from common.proto.context_pb2 import (
-    AuthenticationResult, Empty, Service, ServiceId, ServiceIdList, ServiceStatus, TeraFlowController)
-from common.proto.nbi_pb2_grpc import NbiServiceServicer
-
-LOGGER = logging.getLogger(__name__)
-
-METRICS_POOL = MetricsPool('NBI', 'RPC')
-
-class NbiServiceServicerImpl(NbiServiceServicer):
-    def __init__(self):
-        LOGGER.info('Creating Servicer...')
-        LOGGER.info('Servicer Created')
-
-    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
-    def CheckCredentials(self, request : TeraFlowController, context : grpc.ServicerContext) -> AuthenticationResult:
-        LOGGER.warning('NOT IMPLEMENTED')
-        return AuthenticationResult()
-
-    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
-    def GetConnectivityServiceStatus(self, request : ServiceId, context : grpc.ServicerContext) -> ServiceStatus:
-        LOGGER.warning('NOT IMPLEMENTED')
-        return ServiceStatus()
-
-    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
-    def CreateConnectivityService(self, request : Service, context : grpc.ServicerContext) -> ServiceId:
-        LOGGER.warning('NOT IMPLEMENTED')
-        return ServiceId()
-
-    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
-    def EditConnectivityService(self, request : Service, context : grpc.ServicerContext) -> ServiceId:
-        LOGGER.warning('NOT IMPLEMENTED')
-        return ServiceId()
-
-    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
-    def DeleteConnectivityService(self, request : Service, context : grpc.ServicerContext) -> Empty:
-        LOGGER.warning('NOT IMPLEMENTED')
-        return Empty()
-
-    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
-    def GetAllActiveConnectivityServices(self, request : Empty, context : grpc.ServicerContext) -> ServiceIdList:
-        LOGGER.warning('NOT IMPLEMENTED')
-        return ServiceIdList()
-
-    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
-    def ClearAllConnectivityServices(self, request : Empty, context : grpc.ServicerContext) -> Empty:
-        LOGGER.warning('NOT IMPLEMENTED')
-        return Empty()
diff --git a/src/nbi/service/__main__.py b/src/nbi/service/app.py
similarity index 53%
rename from src/nbi/service/__main__.py
rename to src/nbi/service/app.py
index 1d470f4eac30795e2272c9145baf947f3c982ba5..023f1500bdcfb167e4881329a4df0d34fb98a716 100644
--- a/src/nbi/service/__main__.py
+++ b/src/nbi/service/app.py
@@ -12,16 +12,17 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import logging, signal, sys, threading
+import logging
+from typing import Optional
 from prometheus_client import start_http_server
 from common.Constants import ServiceNameEnum
 from common.Settings import (
     ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC,
-    get_env_var_name, get_log_level, get_metrics_port,
+    get_env_var_name, get_http_bind_address, get_log_level,
+    get_metrics_port, get_service_baseurl_http, get_service_port_http,
     wait_for_environment_variables
 )
-from .NbiService import NbiService
-from .rest_server.RestServer import RestServer
+from .NbiApplication import NbiApplication
 from .rest_server.nbi_plugins.etsi_bwm import register_etsi_bwm_api
 from .rest_server.nbi_plugins.ietf_hardware import register_ietf_hardware
 from .rest_server.nbi_plugins.ietf_l2vpn import register_ietf_l2vpn
@@ -32,22 +33,34 @@ from .rest_server.nbi_plugins.ietf_acl import register_ietf_acl
 from .rest_server.nbi_plugins.qkd_app import register_qkd_app
 from .rest_server.nbi_plugins.tfs_api import register_tfs_api
 from .rest_server.nbi_plugins import register_restconf
-from .context_subscription import register_context_subscription
-
-terminate = threading.Event()
-LOGGER = None
-
-def signal_handler(signal, frame): # pylint: disable=redefined-outer-name, unused-argument
-    LOGGER.warning('Terminate signal received')
-    terminate.set()
-
-def main():
-    global LOGGER # pylint: disable=global-statement
-
-    log_level = get_log_level()
-    logging.basicConfig(level=log_level)
-    LOGGER = logging.getLogger(__name__)
-
+from .websocket_namespaces.hearthbeat import register_heartbeat
+
+
+LOG_LEVEL = get_log_level()
+logging.basicConfig(level=LOG_LEVEL)
+LOGGER = logging.getLogger(__name__)
+
+BIND_ADDRESS = get_http_bind_address()
+BIND_PORT    = get_service_port_http(ServiceNameEnum.NBI)
+BASE_URL     = get_service_baseurl_http(ServiceNameEnum.NBI) or ''
+
+REGISTER_METHODS = [
+    register_etsi_bwm_api,
+    register_ietf_hardware,
+    register_ietf_l2vpn,
+    register_ietf_l3vpn,
+    register_ietf_network,
+    register_ietf_nss,
+    register_ietf_acl,
+    register_qkd_app,
+    register_tfs_api,
+    register_restconf,
+    register_heartbeat,
+]
+
+def configure_nbi(
+    base_url : Optional[str] = None
+) -> NbiApplication:
     wait_for_environment_variables([
         get_env_var_name(ServiceNameEnum.CONTEXT, ENVVAR_SUFIX_SERVICE_HOST     ),
         get_env_var_name(ServiceNameEnum.CONTEXT, ENVVAR_SUFIX_SERVICE_PORT_GRPC),
@@ -57,52 +70,25 @@ def main():
         get_env_var_name(ServiceNameEnum.SERVICE, ENVVAR_SUFIX_SERVICE_PORT_GRPC),
     ])
 
-    signal.signal(signal.SIGINT,  signal_handler)
-    signal.signal(signal.SIGTERM, signal_handler)
-
     LOGGER.info('Starting...')
 
     # Start metrics server
     metrics_port = get_metrics_port()
     start_http_server(metrics_port)
 
-    # Starting NBI service
-    grpc_service = NbiService()
-    grpc_service.start()
-
-    rest_server = RestServer()
-    register_etsi_bwm_api(rest_server)
-    register_ietf_hardware(rest_server)
-    register_ietf_l2vpn(rest_server)  # Registering L2VPN entrypoint
-    register_ietf_l3vpn(rest_server)  # Registering L3VPN entrypoint
-    register_ietf_network(rest_server)
-    register_ietf_nss(rest_server)  # Registering NSS entrypoint
-    register_ietf_acl(rest_server)
-    register_qkd_app(rest_server)
-    register_tfs_api(rest_server)
-    register_restconf(rest_server)
-    rest_server.start()
-
-    register_context_subscription()
-
-    LOGGER.debug('Configured Resources:')
-    for resource in rest_server.api.resources:
-        LOGGER.debug(' - {:s}'.format(str(resource)))
-
-    LOGGER.debug('Configured Rules:')
-    for rule in rest_server.app.url_map.iter_rules():
-        LOGGER.debug(' - {:s}'.format(str(rule)))
+    nbi_app = NbiApplication(base_url=base_url)
 
-    # Wait for Ctrl+C or termination signal
-    while not terminate.wait(timeout=1.0): pass
+    for register_method in REGISTER_METHODS:
+        register_method(nbi_app)
 
-    LOGGER.info('Terminating...')
-    grpc_service.stop()
-    rest_server.shutdown()
-    rest_server.join()
+    nbi_app.dump_configuration()
 
-    LOGGER.info('Bye')
-    return 0
+    return nbi_app
 
 if __name__ == '__main__':
-    sys.exit(main())
+    # Only used to run it locally during development stage;
+    # otherwise, app is directly launched by gunicorn.
+    _nbi_app = configure_nbi(base_url=BASE_URL)
+    _nbi_app.run_standalone(
+        bind_address=BIND_ADDRESS, bind_port=BIND_PORT
+    )
diff --git a/src/nbi/client/__init__.py b/src/nbi/service/websocket_namespaces/__init__.py
similarity index 100%
rename from src/nbi/client/__init__.py
rename to src/nbi/service/websocket_namespaces/__init__.py
diff --git a/src/nbi/service/websocket_namespaces/hearthbeat/HeartbeatNamespace.py b/src/nbi/service/websocket_namespaces/hearthbeat/HeartbeatNamespace.py
new file mode 100644
index 0000000000000000000000000000000000000000..b9b8b0be2d63e6da79ce8ca658993f78bb8566d5
--- /dev/null
+++ b/src/nbi/service/websocket_namespaces/hearthbeat/HeartbeatNamespace.py
@@ -0,0 +1,41 @@
+import logging, threading, time
+from flask import request
+from flask_socketio import Namespace, join_room, leave_room
+from nbi.service.NbiApplication import NbiApplication
+
+LOGGER = logging.getLogger(__name__)
+
+NAMESPACE_NAME = 'heartbeat'
+NAMESPACE_URL  = '/heartbeat'
+
+# WebSocket Heartbeat Namespace for debugging purposes
+class DebugHeartbeatHandler(Namespace):
+    def on_connect(self):
+        LOGGER.debug('Client {:s} connected'.format(str(request.sid)))
+        join_room(NAMESPACE_NAME)
+
+    def on_disconnect(self, reason):
+        LOGGER.debug('Client {:s} disconnected: reason={:s}'.format(
+            str(request.sid), str(reason)
+        ))
+        leave_room(NAMESPACE_NAME)
+
+class DebugHeartbeatThread(threading.Thread):
+    INTERVAL = 1 # second
+
+    def __init__(self, nbi_app : NbiApplication):
+        super().__init__(daemon=True)
+        self.nbi_app = nbi_app
+
+    def run(self):
+        interval   = DebugHeartbeatThread.INTERVAL
+        start_time = time.time()
+        while True:
+            time.sleep(interval)
+            uptime = time.time() - start_time
+            self.nbi_app.websocket_emit_message(
+                'uptime', {'uptime_seconds': uptime},
+                namespace=NAMESPACE_URL, to=NAMESPACE_NAME
+            )
+
+NAMESPACE_DESCRIPTOR = (NAMESPACE_NAME, DebugHeartbeatHandler, NAMESPACE_URL)
diff --git a/src/nbi/service/rest_server/RestServer.py b/src/nbi/service/websocket_namespaces/hearthbeat/__init__.py
similarity index 56%
rename from src/nbi/service/rest_server/RestServer.py
rename to src/nbi/service/websocket_namespaces/hearthbeat/__init__.py
index 521de2735f4d8476c59a263651a67f42851084e2..435a425b4848813a450f00eaa746b9a3e29b6609 100644
--- a/src/nbi/service/rest_server/RestServer.py
+++ b/src/nbi/service/websocket_namespaces/hearthbeat/__init__.py
@@ -12,12 +12,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from common.Constants import ServiceNameEnum
-from common.Settings import get_service_baseurl_http, get_service_port_http
-from common.tools.service.GenericRestServer import GenericRestServer
 
-class RestServer(GenericRestServer):
-    def __init__(self, cls_name: str = __name__) -> None:
-        bind_port = get_service_port_http(ServiceNameEnum.NBI)
-        base_url = get_service_baseurl_http(ServiceNameEnum.NBI)
-        super().__init__(bind_port, base_url, cls_name=cls_name)
+from nbi.service.NbiApplication import NbiApplication
+from .HeartbeatNamespace import NAMESPACE_DESCRIPTOR
+
+def register_heartbeat(nbi_app : NbiApplication):
+    _, namespace_class, namespace_url = NAMESPACE_DESCRIPTOR
+    nbi_app.add_websocket_namespace(namespace_class, namespace_url)
diff --git a/src/webui/service/__init__.py b/src/webui/service/__init__.py
index f64ecca3b6453baf193c683c11fbb2ca27629286..d33e2118e61e2eb2be8bb17afa1c10fa822d348a 100644
--- a/src/webui/service/__init__.py
+++ b/src/webui/service/__init__.py
@@ -137,6 +137,6 @@ def create_app(use_config=None, web_app_root=None):
         'is_deployed_slice'   : is_deployed_slice,
     })
 
-    if web_app_root is not None:
+    if web_app_root is not None and len(web_app_root) > 0:
         app.wsgi_app = SetSubAppMiddleware(app.wsgi_app, web_app_root)
     return app