diff --git a/manifests/qos_profileservice.yaml b/manifests/qos_profileservice.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..1bcaa500fd204cef1bdcde0db35306113f9cacd1
--- /dev/null
+++ b/manifests/qos_profileservice.yaml
@@ -0,0 +1,72 @@
+# Copyright 2022-2024 ETSI OSG/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.
+
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: qos-profileservice
+spec:
+  selector:
+    matchLabels:
+      app: qos-profileservice
+  #replicas: 1
+  template:
+    metadata:
+      labels:
+        app: qos-profileservice
+    spec:
+      terminationGracePeriodSeconds: 5
+      containers:
+        - name: server
+          image: labs.etsi.org:5050/tfs/controller/qos_profile:latest
+          imagePullPolicy: Always
+          ports:
+            - containerPort: 30060
+            - containerPort: 9192
+          env:
+            - name: LOG_LEVEL
+              value: "INFO"
+          readinessProbe:
+            exec:
+              command: ["/bin/grpc_health_probe", "-addr=:30060"]
+          livenessProbe:
+            exec:
+              command: ["/bin/grpc_health_probe", "-addr=:30060"]
+          resources:
+            requests:
+              cpu: 250m
+              memory: 128Mi
+            limits:
+              cpu: 1000m
+              memory: 1024Mi
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: qos-profileservice
+  labels:
+    app: qos-profileservice
+spec:
+  type: ClusterIP
+  selector:
+    app: qos-profileservice
+  ports:
+    - name: grpc
+      protocol: TCP
+      port: 30060
+      targetPort: 30060
+    - name: metrics
+      protocol: TCP
+      port: 9192
+      targetPort: 9192
diff --git a/src/context/service/ContextServiceServicerImpl.py b/src/context/service/ContextServiceServicerImpl.py
index ee4bc4de6a80ff07e5f49a80377721ccdfb3ba72..41454800088c39e67711cca2f00e993997f09971 100644
--- a/src/context/service/ContextServiceServicerImpl.py
+++ b/src/context/service/ContextServiceServicerImpl.py
@@ -264,11 +264,11 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer
 
     @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
     def DeleteQoSProfile(self, request : QoSProfileId, context : grpc.ServicerContext) -> Empty:
-        return delete_qos_profile(self.db_engine, request)
+        return delete_qos_profile(self.db_engine, request.qos_profile_id.uuid)
 
     @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
     def GetQoSProfile(self, request : QoSProfileId, context : grpc.ServicerContext) -> QoSProfile:
-        return get_qos_profile(self.db_engine, request)
+        return get_qos_profile(self.db_engine, request.qos_profile_id.uuid)
 
     @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
     def GetQoSProfiles(self, request : Empty, context : grpc.ServicerContext) -> Iterator[QoSProfile]:
diff --git a/src/context/service/database/QoSProfile.py b/src/context/service/database/QoSProfile.py
index 8b50f64def2ebd8235e94fade8f651b55c3d125f..8ffe8e0ecb17acf71c3b08e01d5bc240722a65ac 100644
--- a/src/context/service/database/QoSProfile.py
+++ b/src/context/service/database/QoSProfile.py
@@ -19,7 +19,7 @@ from sqlalchemy.orm import Session, sessionmaker
 from sqlalchemy_cockroachdb import run_transaction
 from typing import List, Optional
 
-from common.proto.context_pb2 import Empty, QoSProfileId, QoSProfileValueUnitPair, QoSProfile
+from common.proto.context_pb2 import Empty, Uuid, QoSProfileId, QoSProfileValueUnitPair, QoSProfile
 from common.method_wrappers.ServiceExceptions import NotFoundException
 from common.tools.grpc.Tools import grpc_message_to_json
 from .models.QoSProfile import QoSProfileModel
@@ -27,8 +27,8 @@ from .models.QoSProfile import QoSProfileModel
 LOGGER = logging.getLogger(__name__)
 
 def grpc_message_to_qos_table_data(message: QoSProfile) -> dict:
-    return [{
-    'qos_profile_id'            : message.qos_profile_id.uuid,
+    return {
+    'qos_profile_id'            : message.qos_profile_id.qos_profile_id.uuid,
     'name'                      : message.name,
     'description'               : message.description,
     'status'                    : message.status,
@@ -44,11 +44,11 @@ def grpc_message_to_qos_table_data(message: QoSProfile) -> dict:
     'packetDelayBudget'         : grpc_message_to_json(message.packetDelayBudget),
     'jitter'                    : grpc_message_to_json(message.jitter),
     'packetErrorLossRate'       : message.packetErrorLossRate,
-    }]
+    }
 
 def qos_table_data_to_grpc_message(data: QoSProfileModel) -> QoSProfile:
-    QoSProfile(
-    qos_profile_id            = QoSProfileId(uuid=data.qos_profile_id),
+    return QoSProfile(
+    qos_profile_id            = QoSProfileId(qos_profile_id=Uuid(uuid=data.qos_profile_id)),
     name                      = data.name,
     description               = data.description,
     status                    = data.status,
@@ -69,10 +69,32 @@ def qos_table_data_to_grpc_message(data: QoSProfileModel) -> QoSProfile:
 def set_qos_profile(db_engine : Engine, request : QoSProfile) -> QoSProfile:
     qos_profile_data = grpc_message_to_qos_table_data(request)
     def callback(session : Session) -> bool:
-        stmt = insert(QoSProfileModel).values(qos_profile_data)
-        session.execute(stmt)
-        return get_qos_profile(db_engine, request.qos_profile_id.uuid)
-    return run_transaction(sessionmaker(bind=db_engine), callback)
+        stmt = insert(QoSProfileModel).values([qos_profile_data])
+        stmt = stmt.on_conflict_do_update(index_elements=[QoSProfileModel.qos_profile_id],
+            set_=dict(
+
+                    name                      = stmt.excluded.name,
+                    description               = stmt.excluded.description,
+                    status                    = stmt.excluded.status,
+                    targetMinUpstreamRate     = stmt.excluded.targetMinUpstreamRate,
+                    maxUpstreamRate           = stmt.excluded.maxUpstreamRate,
+                    maxUpstreamBurstRate      = stmt.excluded.maxUpstreamBurstRate,
+                    targetMinDownstreamRate   = stmt.excluded.targetMinDownstreamRate,
+                    maxDownstreamRate         = stmt.excluded.maxDownstreamRate,
+                    maxDownstreamBurstRate    = stmt.excluded.maxDownstreamBurstRate,
+                    minDuration               = stmt.excluded.minDuration,
+                    maxDuration               = stmt.excluded.maxDuration,
+                    priority                  = stmt.excluded.priority,
+                    packetDelayBudget         = stmt.excluded.packetDelayBudget,
+                    jitter                    = stmt.excluded.jitter,
+                    packetErrorLossRate       = stmt.excluded.packetErrorLossRate,
+                )
+        )
+        stmt = stmt.returning(QoSProfileModel)
+        qos_profile = session.execute(stmt).fetchall()
+        return qos_profile[0]
+    qos_profile_row = run_transaction(sessionmaker(bind=db_engine), callback)
+    return qos_table_data_to_grpc_message(qos_profile_row)
 
 def delete_qos_profile(db_engine : Engine, request : str) -> Empty:
     def callback(session : Session) -> bool:
diff --git a/src/context/service/database/models/QoSProfile.py b/src/context/service/database/models/QoSProfile.py
index 0ce7365aa6b8c862604c194a2c6e4d3db29a00f5..431d0f503ce7c5224bff108d570c953ff6f538c6 100644
--- a/src/context/service/database/models/QoSProfile.py
+++ b/src/context/service/database/models/QoSProfile.py
@@ -13,19 +13,13 @@
 # limitations under the License.
 
 from sqlalchemy import Column, Integer, String, JSON
-from sqlalchemy.dialects.postgresql import UUID
-from typing import TypedDict
 from ._Base import _Base
 
 
-class QoSProfileValueUnitPair(TypedDict):
-    value: int
-    unit: str
-
 class QoSProfileModel(_Base):
     __tablename__ = 'qos_profile'
 
-    qos_profile_id            = Column(UUID(as_uuid=False), primary_key=True)
+    qos_profile_id            = Column(String, primary_key=True)
     name                      = Column(String, nullable=False)
     description               = Column(String, nullable=False)
     status                    = Column(String, nullable=False)
diff --git a/src/qos_profile/Config.py b/src/qos_profile/Config.py
new file mode 100644
index 0000000000000000000000000000000000000000..3ee6f7071f145e06c3aeaefc09a43ccd88e619e3
--- /dev/null
+++ b/src/qos_profile/Config.py
@@ -0,0 +1,14 @@
+# Copyright 2022-2024 ETSI OSG/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.
+
diff --git a/src/qos_profile/Dockerfile b/src/qos_profile/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..a35c2e741c8db372421bc8525f50e77f47e28be3
--- /dev/null
+++ b/src/qos_profile/Dockerfile
@@ -0,0 +1,76 @@
+# Copyright 2022-2024 ETSI OSG/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 python:3.9-slim
+
+# Install dependencies
+RUN apt-get --yes --quiet --quiet update && \
+    apt-get --yes --quiet --quiet install wget g++ git && \
+    rm -rf /var/lib/apt/lists/*
+
+# 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/. ./
+RUN rm -rf proto
+
+# Create proto sub-folder, copy .proto files, and generate Python code
+RUN mkdir -p /var/teraflow/common/proto
+WORKDIR /var/teraflow/common/proto
+RUN touch __init__.py
+COPY proto/*.proto ./
+RUN python3 -m grpc_tools.protoc -I=. --python_out=. --grpc_python_out=. *.proto
+RUN rm *.proto
+RUN find . -type f -exec sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' {} \;
+
+# Create component sub-folders, get specific Python packages
+RUN mkdir -p /var/teraflow/qos_profile
+WORKDIR /var/teraflow/qos_profile
+COPY src/service/requirements.in requirements.in
+RUN pip-compile --quiet --output-file=requirements.txt requirements.in
+RUN python3 -m pip install -r requirements.txt
+
+# Add component files into working directory
+WORKDIR /var/teraflow
+COPY src/context/__init__.py context/__init__.py
+COPY src/context/client/. context/client/
+COPY src/device/__init__.py device/__init__.py
+COPY src/device/client/. device/client/
+COPY src/pathcomp/frontend/__init__.py pathcomp/frontend/__init__.py
+COPY src/pathcomp/frontend/client/. pathcomp/frontend/client/
+COPY src/e2e_orchestrator/__init__.py e2e_orchestrator/__init__.py
+COPY src/e2e_orchestrator/client/. e2e_orchestrator/client/
+COPY src/qos_profile/. qos_profile/
+
+# Start the service
+ENTRYPOINT ["python", "-m", "qos_profile.service"]
diff --git a/src/qos_profile/__init__.py b/src/qos_profile/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..3ee6f7071f145e06c3aeaefc09a43ccd88e619e3
--- /dev/null
+++ b/src/qos_profile/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022-2024 ETSI OSG/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.
+
diff --git a/src/qos_profile/client/QoSProfileClient.py b/src/qos_profile/client/QoSProfileClient.py
new file mode 100644
index 0000000000000000000000000000000000000000..7a857ec25ed103a3bcb1c15dab04f3af19868884
--- /dev/null
+++ b/src/qos_profile/client/QoSProfileClient.py
@@ -0,0 +1,82 @@
+# Copyright 2022-2024 ETSI OSG/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 typing import Iterator
+import grpc, logging
+from common.Constants import ServiceNameEnum
+from common.Settings import get_service_host, get_service_port_grpc
+from common.proto.context_pb2 import Empty, QoSProfileId, QoSProfile
+from common.proto.qos_profile_pb2_grpc import QoSProfileServiceStub
+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 QoSProfileClient:
+    def __init__(self, host=None, port=None):
+        if not host: host = get_service_host(ServiceNameEnum.QOSPROFILE)
+        if not port: port = get_service_port_grpc(ServiceNameEnum.QOSPROFILE)
+        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 = QoSProfileServiceStub(self.channel)
+
+    def close(self):
+        if self.channel is not None: self.channel.close()
+        self.channel = None
+        self.stub = None
+
+    @RETRY_DECORATOR
+    def CreateQoSProfile(self, request: QoSProfile) -> QoSProfile:
+        LOGGER.debug('CreateQoSProfile request: {:s}'.format(grpc_message_to_json_string(request)))
+        response = self.stub.CreateQoSProfile(request)
+        LOGGER.debug('CreateQoSProfile result: {:s}'.format(grpc_message_to_json_string(response)))
+        return response
+
+    @RETRY_DECORATOR
+    def UpdateQoSProfile(self, request: QoSProfile) -> QoSProfile:
+        LOGGER.debug('UpdateQoSProfile request: {:s}'.format(grpc_message_to_json_string(request)))
+        response = self.stub.UpdateQoSProfile(request)
+        LOGGER.debug('UpdateQoSProfile result: {:s}'.format(grpc_message_to_json_string(response)))
+        return response
+
+    @RETRY_DECORATOR
+    def DeleteQoSProfile(self, request: QoSProfileId) -> Empty:
+        LOGGER.debug('DeleteQoSProfile request: {:s}'.format(grpc_message_to_json_string(request)))
+        response = self.stub.DeleteQoSProfile(request)
+        LOGGER.debug('DeleteQoSProfile result: {:s}'.format(grpc_message_to_json_string(response)))
+        return response
+
+    @RETRY_DECORATOR
+    def GetQoSProfile(self, request: QoSProfileId) -> QoSProfile:
+        LOGGER.debug('GetQoSProfile request: {:s}'.format(grpc_message_to_json_string(request)))
+        response = self.stub.GetQoSProfile(request)
+        LOGGER.debug('GetQoSProfile result: {:s}'.format(grpc_message_to_json_string(response)))
+        return response
+
+    @RETRY_DECORATOR
+    def GetQoSProfiles(self, request: Empty) -> Iterator[QoSProfile]:
+        LOGGER.debug('GetQoSProfiles request: {:s}'.format(grpc_message_to_json_string(request)))
+        response = self.stub.GetQoSProfiles(request)
+        LOGGER.debug('GetQoSProfiles result: {:s}'.format(grpc_message_to_json_string(response)))
+        return response
diff --git a/src/qos_profile/client/__init__.py b/src/qos_profile/client/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..3ee6f7071f145e06c3aeaefc09a43ccd88e619e3
--- /dev/null
+++ b/src/qos_profile/client/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022-2024 ETSI OSG/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.
+
diff --git a/src/qos_profile/requirements.in b/src/qos_profile/requirements.in
new file mode 100644
index 0000000000000000000000000000000000000000..5cf553eaaec41de7599b6723e31e4ca3f82cbcae
--- /dev/null
+++ b/src/qos_profile/requirements.in
@@ -0,0 +1,15 @@
+# Copyright 2022-2024 ETSI OSG/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.
+
+
diff --git a/src/qos_profile/service/QoSProfileService.py b/src/qos_profile/service/QoSProfileService.py
new file mode 100644
index 0000000000000000000000000000000000000000..bdc90f5bc0a12f5a1afbe59cf651439ee1f3219a
--- /dev/null
+++ b/src/qos_profile/service/QoSProfileService.py
@@ -0,0 +1,28 @@
+# Copyright 2022-2024 ETSI OSG/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.qos_profile_pb2_grpc import add_QoSProfileServiceServicer_to_server
+from common.tools.service.GenericGrpcService import GenericGrpcService
+from .QoSProfileServiceServicerImpl import QoSProfileServiceServicerImpl
+
+class QoSProfileService(GenericGrpcService):
+    def __init__(self, cls_name: str = __name__) -> None:
+        port = get_service_port_grpc(ServiceNameEnum.QOSPROFILE)
+        super().__init__(port, cls_name=cls_name)
+        self.qos_profile_servicer = QoSProfileServiceServicerImpl()
+
+    def install_servicers(self):
+        add_QoSProfileServiceServicer_to_server(self.qos_profile_servicer, self.server)
diff --git a/src/qos_profile/service/QoSProfileServiceServicerImpl.py b/src/qos_profile/service/QoSProfileServiceServicerImpl.py
new file mode 100644
index 0000000000000000000000000000000000000000..38ad608d18471d29212e8c665450fb38da170cc3
--- /dev/null
+++ b/src/qos_profile/service/QoSProfileServiceServicerImpl.py
@@ -0,0 +1,77 @@
+# Copyright 2022-2024 ETSI OSG/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 typing import Iterator
+from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method
+from common.method_wrappers.ServiceExceptions import  AlreadyExistsException
+from common.proto.context_pb2 import Empty, QoSProfileId, QoSProfile
+from common.proto.qos_profile_pb2_grpc import QoSProfileServiceServicer
+from context.client.ContextClient import ContextClient
+
+
+LOGGER = logging.getLogger(__name__)
+
+METRICS_POOL = MetricsPool('QoSProfile', 'RPC')
+
+class QoSProfileServiceServicerImpl(QoSProfileServiceServicer):
+    def __init__(self ) -> None:
+        LOGGER.debug('Servicer Created')
+
+    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
+    def CreateQoSProfile(self, request: QoSProfile, context: grpc.ServicerContext) -> QoSProfile:
+        context_client = ContextClient()
+        try:
+            qos_profile_get = context_client.GetQoSProfile(request.qos_profile_id)
+        except grpc.RpcError:
+            qos_profile_get = None
+        if qos_profile_get:
+            raise AlreadyExistsException('QoSProfile', request.qos_profile_id)
+        qos_profile = context_client.CreateQoSProfile(request)
+        return qos_profile
+
+    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
+    def UpdateQoSProfile(self, request: QoSProfile, context: grpc.ServicerContext) -> QoSProfile:
+        context_client = ContextClient()
+        try:
+            _ = context_client.GetQoSProfile(request.qos_profile_id)
+        except grpc.RpcError as exc:
+            raise exc
+        qos_profile = context_client.UpdateQoSProfile(request)
+        return qos_profile
+
+    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
+    def DeleteQoSProfile(self, request: QoSProfileId, context: grpc.ServicerContext) -> Empty:
+        context_client = ContextClient()
+        try:
+            _ = context_client.GetQoSProfile(request)
+        except grpc.RpcError as exc:
+            raise exc
+        empty = context_client.DeleteQoSProfile(request)
+        return empty
+
+    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
+    def GetQoSProfile(self, request: QoSProfileId, context: grpc.ServicerContext) -> QoSProfile:
+        context_client = ContextClient()
+        try:
+            qos_profile = context_client.GetQoSProfile(request)
+        except grpc.RpcError as exc:
+            raise exc
+        return qos_profile
+
+    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
+    def GetQoSProfiles(self, request: Empty, context: grpc.ServicerContext) -> Iterator[QoSProfile]:
+        context_client = ContextClient()
+        yield from context_client.GetQoSProfiles(request)
+
diff --git a/src/qos_profile/service/__init__.py b/src/qos_profile/service/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..3ee6f7071f145e06c3aeaefc09a43ccd88e619e3
--- /dev/null
+++ b/src/qos_profile/service/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022-2024 ETSI OSG/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.
+
diff --git a/src/qos_profile/service/__main__.py b/src/qos_profile/service/__main__.py
new file mode 100644
index 0000000000000000000000000000000000000000..ccd1ca23ffa4504569b44e6f2250ce583eafcf81
--- /dev/null
+++ b/src/qos_profile/service/__main__.py
@@ -0,0 +1,66 @@
+# Copyright 2022-2024 ETSI OSG/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 logging, signal, sys, threading
+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,
+    wait_for_environment_variables
+)
+from .QoSProfileService import QoSProfileService
+
+terminate = threading.Event()
+LOGGER : logging.Logger = None
+
+def signal_handler(signal, frame): # pylint: disable=redefined-outer-name
+    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, format="[%(asctime)s] %(levelname)s:%(name)s:%(message)s")
+    LOGGER = logging.getLogger(__name__)
+
+    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),
+    ])
+
+    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 service service
+    grpc_service = QoSProfileService()
+    grpc_service.start()
+
+    # Wait for Ctrl+C or termination signal
+    while not terminate.wait(timeout=1.0): pass
+
+    LOGGER.info('Terminating...')
+    grpc_service.stop()
+
+    LOGGER.info('Bye')
+    return 0
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/src/qos_profile/tests/.gitignore b/src/qos_profile/tests/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..6b97d6fe3ad32f39097745229ab7f547f26ecb12
--- /dev/null
+++ b/src/qos_profile/tests/.gitignore
@@ -0,0 +1 @@
+# Add here your files containing confidential testbed details such as IP addresses, ports, usernames, passwords, etc.
diff --git a/src/qos_profile/tests/__init__.py b/src/qos_profile/tests/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..3ee6f7071f145e06c3aeaefc09a43ccd88e619e3
--- /dev/null
+++ b/src/qos_profile/tests/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022-2024 ETSI OSG/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.
+
diff --git a/src/qos_profile/tests/conftest.py b/src/qos_profile/tests/conftest.py
new file mode 100644
index 0000000000000000000000000000000000000000..37110c0b60aaf76655b44a8a79fb35aff77a8745
--- /dev/null
+++ b/src/qos_profile/tests/conftest.py
@@ -0,0 +1,23 @@
+# Copyright 2022-2024 ETSI OSG/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 pytest
+from qos_profile.client.QoSProfileClient import QoSProfileClient
+
+@pytest.fixture(scope='function')
+def qos_profile_client():
+    _client = QoSProfileClient(host='0.0.0.0', port=30060)
+    yield _client
+    _client.close()
+
diff --git a/src/qos_profile/tests/test_crud.py b/src/qos_profile/tests/test_crud.py
new file mode 100644
index 0000000000000000000000000000000000000000..1c5171da004b88c7a0b22345445c1a111c878d2f
--- /dev/null
+++ b/src/qos_profile/tests/test_crud.py
@@ -0,0 +1,130 @@
+# Copyright 2022-2024 ETSI OSG/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 grpc import RpcError
+import logging, pytest
+from common.proto.context_pb2 import Empty, Uuid, QoSProfileValueUnitPair, QoSProfileId, QoSProfile
+
+from common.tools.grpc.Tools import grpc_message_to_json_string
+from qos_profile.client.QoSProfileClient import QoSProfileClient
+
+LOGGER = logging.getLogger(__name__)
+LOGGER.setLevel(logging.DEBUG)
+
+qos_profile_data = {
+  "qos_profile_id": "f00406f5-8e36-4abc-a0ec-b871c7f062b7",
+  "name": "QCI_1_voice",
+  "description": "QoS profile for video streaming",
+  "status": "ACTIVE",
+  "targetMinUpstreamRate": {
+    "value": 10,
+    "unit": "bps"
+  },
+  "maxUpstreamRate": {
+    "value": 10,
+    "unit": "bps"
+  },
+  "maxUpstreamBurstRate": {
+    "value": 10,
+    "unit": "bps"
+  },
+  "targetMinDownstreamRate": {
+    "value": 10,
+    "unit": "bps"
+  },
+  "maxDownstreamRate": {
+    "value": 10,
+    "unit": "bps"
+  },
+  "maxDownstreamBurstRate": {
+    "value": 10,
+    "unit": "bps"
+  },
+  "minDuration": {
+    "value": 12,
+    "unit": "Minutes"
+  },
+  "maxDuration": {
+    "value": 12,
+    "unit": "Minutes"
+  },
+  "priority": 20,
+  "packetDelayBudget": {
+    "value": 12,
+    "unit": "Minutes"
+  },
+  "jitter": {
+    "value": 12,
+    "unit": "Minutes"
+  },
+  "packetErrorLossRate": 3
+}
+
+def create_qos_profile_from_json(qos_profile_data: dict) -> QoSProfile:
+    def create_QoSProfileValueUnitPair(data) -> QoSProfileValueUnitPair:
+        return QoSProfileValueUnitPair(value=data['value'], unit=data['unit'])
+    qos_profile = QoSProfile()
+    qos_profile.qos_profile_id.CopyFrom(QoSProfileId(qos_profile_id=Uuid(uuid=qos_profile_data['qos_profile_id'])))
+    qos_profile.name = qos_profile_data['name']
+    qos_profile.description = qos_profile_data['description']
+    qos_profile.status = qos_profile_data['status']
+    qos_profile.targetMinUpstreamRate.CopyFrom(create_QoSProfileValueUnitPair(qos_profile_data['targetMinUpstreamRate']))
+    qos_profile.maxUpstreamRate.CopyFrom(create_QoSProfileValueUnitPair(qos_profile_data['maxUpstreamRate']))
+    qos_profile.maxUpstreamBurstRate.CopyFrom(create_QoSProfileValueUnitPair(qos_profile_data['maxUpstreamBurstRate']))
+    qos_profile.targetMinDownstreamRate.CopyFrom(create_QoSProfileValueUnitPair(qos_profile_data['targetMinDownstreamRate']))
+    qos_profile.maxDownstreamRate.CopyFrom(create_QoSProfileValueUnitPair(qos_profile_data['maxDownstreamRate']))
+    qos_profile.maxDownstreamBurstRate.CopyFrom(create_QoSProfileValueUnitPair(qos_profile_data['maxDownstreamBurstRate']))
+    qos_profile.minDuration.CopyFrom(create_QoSProfileValueUnitPair(qos_profile_data['minDuration']))
+    qos_profile.maxDuration.CopyFrom(create_QoSProfileValueUnitPair(qos_profile_data['maxDuration']))
+    qos_profile.priority = qos_profile_data['priority']
+    qos_profile.packetDelayBudget.CopyFrom(create_QoSProfileValueUnitPair(qos_profile_data['packetDelayBudget']))
+    qos_profile.jitter.CopyFrom(create_QoSProfileValueUnitPair(qos_profile_data['jitter']))
+    qos_profile.packetErrorLossRate = qos_profile_data['packetErrorLossRate']
+    return qos_profile
+
+def test_create_qos_profile(qos_profile_client: QoSProfileClient):
+    qos_profile = create_qos_profile_from_json(qos_profile_data)
+    qos_profile_created = qos_profile_client.CreateQoSProfile(qos_profile)
+    LOGGER.info('qos_profile_data = {:s}'.format(grpc_message_to_json_string(qos_profile_created)))
+    assert qos_profile == qos_profile_created
+
+
+def test_failed_create_qos_profile(qos_profile_client: QoSProfileClient):
+    qos_profile = create_qos_profile_from_json(qos_profile_data)
+    with pytest.raises(RpcError) as exc:
+      qos_profile_created = qos_profile_client.CreateQoSProfile(qos_profile)
+
+def test_get_qos_profile(qos_profile_client: QoSProfileClient):
+    qos_profile = create_qos_profile_from_json(qos_profile_data)
+    qos_profile_got = qos_profile_client.GetQoSProfile(qos_profile.qos_profile_id)
+    LOGGER.info('qos_profile_data = {:s}'.format(grpc_message_to_json_string(qos_profile_got)))
+    assert qos_profile == qos_profile_got
+
+def test_get_qos_profiles(qos_profile_client: QoSProfileClient):
+    qos_profile = create_qos_profile_from_json(qos_profile_data)
+    qos_profiles_got = list(qos_profile_client.GetQoSProfiles(Empty()))
+    LOGGER.info('qos_profile_data = {:s}'.format(grpc_message_to_json_string(qos_profiles_got)))
+    assert qos_profile == qos_profiles_got[0]
+
+def test_update_qos_profile(qos_profile_client: QoSProfileClient):
+    qos_profile = create_qos_profile_from_json(qos_profile_data)
+    qos_profile.packetErrorLossRate = 5
+    qos_profile_updated = qos_profile_client.UpdateQoSProfile(qos_profile)
+    LOGGER.info('qos_profile_data = {:s}'.format(grpc_message_to_json_string(qos_profile_updated)))
+    assert qos_profile_updated.packetErrorLossRate == 5
+
+def test_delete_qos_profiles(qos_profile_client: QoSProfileClient):
+    qos_profile = create_qos_profile_from_json(qos_profile_data)
+    with pytest.raises(RpcError) as exc:
+      qos_profiles_deleted = qos_profile_client.DeleteQoSProfile(QoSProfileId(qos_profile_id=Uuid(uuid='f8b1c625-ac01-405c-b1f7-b5ee06e16282')))
\ No newline at end of file