diff --git a/MANIFEST.in b/MANIFEST.in
index 58874ea53d89bcd754171ef8f9c65be2a37b38ad..19e228904bea02449a42b500c5cda214201794b6 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -4,5 +4,5 @@ include compendium_v2/templates/survey-index.html
 include compendium_v2/migrations/alembic.ini
 recursive-include compendium_v2/migrations/versions *
 recursive-include compendium_v2/migrations/surveymodels *
-recursive-include compendium_v2/background_task/resources *
+recursive-include compendium_v2/resources *
 recursive-exclude test *
diff --git a/compendium_v2/conversion/mapping.py b/compendium_v2/conversion/mapping.py
index 1258da6c95a6489910d00980879bf13b2f1d9b41..4a4f7df82496f7f64a7fb3e1c557e925417f2ddb 100644
--- a/compendium_v2/conversion/mapping.py
+++ b/compendium_v2/conversion/mapping.py
@@ -1,3 +1,5 @@
+from compendium_v2.db.presentation_models import ServiceCategory
+
 ANSWERS_2022_QUERY = """
 SELECT question_id, value
 FROM answers a
@@ -664,3 +666,14 @@ SERVICES_MAPPING = {
     'SaaS': 'services_hosting:saas',
     'Virtual machines/IaaS': 'services_hosting:virtual-machines-iaas',
 }
+
+SERVICE_CATEGORY_MAPPING = {
+    'services_collaboration': ServiceCategory.collaboration.value,
+    'services_identity': ServiceCategory.identity.value,
+    'services_isp': ServiceCategory.isp_support.value,
+    'services_multimedia': ServiceCategory.multimedia.value,
+    'services_network': ServiceCategory.network_services.value,
+    'services_professional': ServiceCategory.professional_services.value,
+    'services_security': ServiceCategory.security.value,
+    'services_hosting': ServiceCategory.storage_and_hosting.value,
+}
diff --git a/compendium_v2/db/presentation_models.py b/compendium_v2/db/presentation_models.py
index 9c18348bedd094da8aa1501df596eb8df2e68793..a25200e7a21a3b0551b75e09b36994939bc76df3 100644
--- a/compendium_v2/db/presentation_models.py
+++ b/compendium_v2/db/presentation_models.py
@@ -471,3 +471,25 @@ class NetworkAutomation(db.Model):
     year: Mapped[int_pk]
     network_automation: Mapped[YesNoPlanned]
     network_automation_specifics: Mapped[json_str_list]
+
+
+class Service(db.Model):
+    __tablename__ = 'service'
+
+    name_key: Mapped[str128_pk]
+    name: Mapped[str128]
+    category: Mapped[ServiceCategory]
+    description: Mapped[str]
+
+
+class NRENService(db.Model):
+    __tablename__ = 'nren_service'
+
+    nren_id: Mapped[int_pk_fkNREN]
+    nren: Mapped[NREN] = relationship(lazy='joined')
+    year: Mapped[int_pk]
+    service_key: Mapped[str128] = mapped_column(ForeignKey("service.name_key"), primary_key=True)
+    service: Mapped[Service] = relationship(lazy='joined')
+    product_name: Mapped[str128]
+    additional_information: Mapped[str]
+    official_description: Mapped[str]
diff --git a/compendium_v2/migrations/versions/1fbc4582c0ab_added_service_and_nrenservice_tables.py b/compendium_v2/migrations/versions/1fbc4582c0ab_added_service_and_nrenservice_tables.py
new file mode 100644
index 0000000000000000000000000000000000000000..1c3dc3964133f843a7842756766aa77106c101cd
--- /dev/null
+++ b/compendium_v2/migrations/versions/1fbc4582c0ab_added_service_and_nrenservice_tables.py
@@ -0,0 +1,55 @@
+"""added Service and NRENService tables
+
+Revision ID: 1fbc4582c0ab
+Revises: 87e1e35051a0
+Create Date: 2023-09-19 22:23:36.448210
+
+"""
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects import postgresql
+
+# revision identifiers, used by Alembic.
+revision = '1fbc4582c0ab'
+down_revision = '87e1e35051a0'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.create_table(
+        'service',
+        sa.Column('name_key', sa.String(length=128), nullable=False),
+        sa.Column('name', sa.String(), nullable=False),
+        sa.Column('category', postgresql.ENUM(
+            'network_services', 'isp_support', 'security', 'identity',
+            'collaboration', 'multimedia', 'storage_and_hosting',
+            'professional_services', name='servicecategory',
+            create_type=False), nullable=False
+        ),
+        sa.Column('description', sa.String(), nullable=False),
+        sa.PrimaryKeyConstraint('name_key', name=op.f('pk_service'))
+    )
+    op.create_table(
+        'nren_service',
+        sa.Column('nren_id', sa.Integer(), nullable=False),
+        sa.Column('year', sa.Integer(), nullable=False),
+        sa.Column('service_key', sa.String(length=128), nullable=False),
+        sa.Column('product_name', sa.String(), nullable=False),
+        sa.Column('additional_information', sa.String(), nullable=False),
+        sa.Column('official_description', sa.String(), nullable=False),
+        sa.ForeignKeyConstraint(['nren_id'], ['nren.id'], name=op.f('fk_nren_service_nren_id_nren')),
+        sa.ForeignKeyConstraint(
+            ['service_key'], ['service.name_key'], name=op.f('fk_nren_service_service_key_service')
+        ),
+        sa.PrimaryKeyConstraint('nren_id', 'year', 'service_key', name=op.f('pk_nren_service'))
+    )
+    # ### end Alembic commands ###
+
+
+def downgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.drop_table('nren_service')
+    op.drop_table('service')
+    # ### end Alembic commands ###
diff --git a/compendium_v2/publishers/excel_parser.py b/compendium_v2/publishers/excel_parser.py
index 41f7c0149b3f4cb9a89c30e42dbf9304a336cb25..ce1efff48e133c142f4d888ec3186bed05a5734a 100644
--- a/compendium_v2/publishers/excel_parser.py
+++ b/compendium_v2/publishers/excel_parser.py
@@ -1,22 +1,25 @@
 import logging
+
 import openpyxl
-import os
 
-from compendium_v2.db.presentation_model_enums import FeeType
+from compendium_v2.conversion import mapping
+from compendium_v2.db.presentation_models import FeeType
 from compendium_v2.environment import setup_logging
+from compendium_v2.resources import get_resource_file_path
 
 setup_logging()
 
 logger = logging.getLogger(__name__)
 
-EXCEL_FILE = os.path.join(os.path.dirname(__file__), "../resources", "2021_Organisation_DataSeries.xlsx")
-NETWORK_EXCEL_FILE = os.path.join(os.path.dirname(__file__), "../resources", "2022_Networks_DataSeries.xlsx")
+EXCEL_FILE_ORGANISATION = get_resource_file_path("2021_Organisation_DataSeries.xlsx")
+EXCEL_FILE_NETWORKS = get_resource_file_path("2022_Networks_DataSeries.xlsx")
+EXCEL_FILE_NREN_SERVICES = get_resource_file_path("NREN-Services-prefills_2023_Recovered.xlsx")
 
 
 def fetch_budget_excel_data():
     # load the xlsx file
     sheet_name = "1. Budget"
-    wb = openpyxl.load_workbook(EXCEL_FILE, data_only=True, read_only=True)
+    wb = openpyxl.load_workbook(EXCEL_FILE_ORGANISATION, data_only=True, read_only=True)
 
     # select the active worksheet
     ws = wb[sheet_name]
@@ -36,7 +39,7 @@ def fetch_budget_excel_data():
 
 def fetch_funding_excel_data():
     # load the xlsx file
-    wb = openpyxl.load_workbook(EXCEL_FILE, data_only=True, read_only=True)
+    wb = openpyxl.load_workbook(EXCEL_FILE_ORGANISATION, data_only=True, read_only=True)
 
     # select the active worksheet
     sheet_name = "2. Income Sources"
@@ -119,7 +122,7 @@ def fetch_funding_excel_data():
 
 def fetch_charging_structure_excel_data():
     # load the xlsx file
-    wb = openpyxl.load_workbook(EXCEL_FILE, data_only=True, read_only=True)
+    wb = openpyxl.load_workbook(EXCEL_FILE_ORGANISATION, data_only=True, read_only=True)
 
     # select the active worksheet
     sheet_name = "3. Charging mechanism"
@@ -186,7 +189,7 @@ def fetch_charging_structure_excel_data():
 
 def fetch_staffing_excel_data():
     # load the xlsx file
-    wb = openpyxl.load_workbook(EXCEL_FILE, data_only=True, read_only=True)
+    wb = openpyxl.load_workbook(EXCEL_FILE_ORGANISATION, data_only=True, read_only=True)
 
     # select the active worksheet
     sheet_name = "4. Staff"
@@ -234,7 +237,7 @@ def fetch_staffing_excel_data():
 
 def fetch_staff_function_excel_data():
     # load the xlsx file
-    wb = openpyxl.load_workbook(EXCEL_FILE, data_only=True, read_only=True)
+    wb = openpyxl.load_workbook(EXCEL_FILE_ORGANISATION, data_only=True, read_only=True)
 
     # select the active worksheet
     sheet_name = "5. Staff by Function"
@@ -302,7 +305,7 @@ def fetch_staff_function_excel_data():
 
 def fetch_ecproject_excel_data():
     # load the xlsx file
-    wb = openpyxl.load_workbook(EXCEL_FILE, data_only=True, read_only=True)
+    wb = openpyxl.load_workbook(EXCEL_FILE_ORGANISATION, data_only=True, read_only=True)
 
     # select the active worksheet
     sheet_name = "7. EC Projects"
@@ -334,7 +337,7 @@ def fetch_ecproject_excel_data():
 
 def fetch_organization_excel_data():
     # load the xlsx file
-    wb = openpyxl.load_workbook(EXCEL_FILE, data_only=True, read_only=True)
+    wb = openpyxl.load_workbook(EXCEL_FILE_ORGANISATION, data_only=True, read_only=True)
 
     # select the active worksheet
     sheet_name = "Organization"
@@ -352,7 +355,7 @@ def fetch_organization_excel_data():
 
 def fetch_traffic_excel_data():
     # load the xlsx file
-    wb = openpyxl.load_workbook(NETWORK_EXCEL_FILE, data_only=True, read_only=True)
+    wb = openpyxl.load_workbook(EXCEL_FILE_NETWORKS, data_only=True, read_only=True)
 
     # select the active worksheet
     sheet_name = "Estimated_Traffic TByte"
@@ -392,3 +395,48 @@ def fetch_traffic_excel_data():
     yield from create_points_for_year(2020, 14)
     yield from create_points_for_year(2021, 8)
     yield from create_points_for_year(2022, 2)
+
+
+def fetch_nren_services_excel_data():
+    wb = openpyxl.load_workbook(EXCEL_FILE_NREN_SERVICES, data_only=True, read_only=True)
+    ws = wb["Sheet1"]
+    rows = list(ws.rows)
+
+    titles = rows[0]
+
+    nren_service_data_columns = {}
+
+    def normalize_nren_name(n: str) -> str:
+        n = n.split(' ')[0].upper()
+        return {'KIFÜ': 'KIFU', 'AZSCIENCENET': 'ANAS', 'PSNC': 'PIONIER'}.get(n, n)
+
+    for i in range(0, 131):
+        if titles[i].value:
+            name = normalize_nren_name(titles[i].value)
+            nren_service_data_columns[name] = i
+
+    for nren_name, start_column in nren_service_data_columns.items():
+        for row_index in range(2, 61):
+            row = rows[row_index]
+            service_name = row[0].value
+            if row[start_column].value and row[start_column].value.upper() == 'YES':
+                product_name = ''
+                additional_information = ''
+                if row[start_column + 1].value:
+                    product_name = row[start_column + 1].value
+                if row[start_column + 2].value:
+                    additional_information = row[start_column + 2].value
+
+                service_category, service_name_key = mapping.SERVICES_MAPPING[service_name].split(':')
+                service_category = mapping.SERVICE_CATEGORY_MAPPING[service_category]
+
+                yield {
+                    'nren_name': nren_name,
+                    'service_name': service_name,
+                    'service_category': service_category,
+                    'service_name_key': service_name_key,
+                    'year': 2022,
+                    'product_name': product_name.strip(),
+                    'additional_information': additional_information.strip(),
+                    'official_description': '',
+                }
diff --git a/compendium_v2/publishers/survey_publisher.py b/compendium_v2/publishers/survey_publisher.py
index 10a50baf7e32a762aaa251cd79fbac3b65d4fddf..ccd4b0940343d79fa4d35d4ce97bb5aaebaee602 100644
--- a/compendium_v2/publishers/survey_publisher.py
+++ b/compendium_v2/publishers/survey_publisher.py
@@ -23,7 +23,7 @@ from compendium_v2.db.presentation_models import BudgetEntry, ChargingStructure,
     CommercialChargingLevel, RemoteCampuses, DarkFibreLease, DarkFibreInstalled, FibreLight, \
     NetworkMapUrls, MonitoringTools, PassiveMonitoring, TrafficStatistics, SiemVendors, \
     CertificateProviders, WeatherMap, PertTeam, AlienWave, Capacity, NonREPeers, TrafficRatio, \
-    OpsAutomation, NetworkFunctionVirtualisation, NetworkAutomation
+    OpsAutomation, NetworkFunctionVirtualisation, NetworkAutomation, NRENService
 from compendium_v2.db.presentation_model_enums import CarryMechanism, CommercialCharges, YesNoPlanned, \
     CommercialConnectivityCoverage, ConnectivityCoverage, FeeType, MonitoringMethod, ServiceCategory, UserCategory
 from compendium_v2.db.survey_models import ResponseStatus, SurveyResponse
@@ -64,7 +64,7 @@ def _map_2023(nren, answers) -> None:
                         CommercialChargingLevel, RemoteCampuses, DarkFibreLease, DarkFibreInstalled, FibreLight,
                         NetworkMapUrls, MonitoringTools, PassiveMonitoring, TrafficStatistics, SiemVendors,
                         CertificateProviders, WeatherMap, PertTeam, AlienWave, Capacity, NonREPeers, TrafficRatio,
-                        OpsAutomation, NetworkFunctionVirtualisation, NetworkAutomation]:
+                        OpsAutomation, NetworkFunctionVirtualisation, NetworkAutomation, NRENService]:
         db.session.execute(delete(table_class).where(table_class.year == year))  # type: ignore
 
     answers = answers["data"]
@@ -516,6 +516,22 @@ def _map_2023(nren, answers) -> None:
             network_automation_specifics=network_automation_tasks
         ))
 
+    all_services = {}
+    for question_name in ["services_collaboration", "services_hosting", "services_identity", "services_isp",
+                          "services_multimedia", "services_network", "services_professional", "services_security"]:
+        all_services.update(answers.get(question_name, {}))
+
+    for service_key, answers in all_services.items():
+        offered = answers.get("offered")
+        if offered == ["yes"]:
+            db.session.add(NRENService(
+                nren_id=nren.id, nren=nren, year=year,
+                service_key=service_key,
+                product_name=answers.get("name", ""),
+                additional_information=answers.get("additional_information", ""),
+                official_description=answers.get("description", "")
+            ))
+
 
 def publish(year):
     responses = db.session.scalars(
diff --git a/compendium_v2/publishers/survey_publisher_legacy_excel.py b/compendium_v2/publishers/survey_publisher_legacy_excel.py
index 6503b79faeda6e296c9fe2bf043658e95f4b8c5b..dfb6b6b51574f2b78cc93d77536d1d16103209d7 100644
--- a/compendium_v2/publishers/survey_publisher_legacy_excel.py
+++ b/compendium_v2/publishers/survey_publisher_legacy_excel.py
@@ -8,18 +8,21 @@ Registered as click cli command when installing compendium-v2.
 
 """
 from __future__ import annotations
+
+import json
 import logging
 import math
-import click
 
+import click
 from sqlalchemy import select
 
 import compendium_v2
-from compendium_v2.environment import setup_logging
 from compendium_v2.config import load
 from compendium_v2.db import db, presentation_models
-from compendium_v2.survey_db import model as survey_model
+from compendium_v2.environment import setup_logging
 from compendium_v2.publishers import helpers, excel_parser
+from compendium_v2.resources import get_resource_file_path
+from compendium_v2.survey_db import model as survey_model
 
 setup_logging()
 
@@ -224,7 +227,49 @@ def db_traffic_volume_migration(nren_dict):
     db.session.commit()
 
 
-def _cli(config, app):
+def db_services_migration():
+    with open(get_resource_file_path('nren_services.json')) as f:
+        services = json.load(f)
+        for name_key, record in services.items():
+            service = presentation_models.Service(
+                name_key=name_key,
+                name=record['name'],
+                category=record['category'],
+                description=record['description'],
+            )
+            db.session.merge(service)
+        db.session.commit()
+
+
+def db_nren_services_migration(nren_dict):
+    services = [s for s in db.session.scalars(select(presentation_models.Service))]
+
+    for service_info in excel_parser.fetch_nren_services_excel_data():
+        [service] = [s for s in services if s.name_key == service_info['service_name_key']]
+
+        abbrev = service_info['nren_name']
+        if abbrev not in nren_dict:
+            logger.warning(f'{abbrev} unknown. Skipping.')
+            continue
+
+        nren = nren_dict[abbrev]
+        nren_service = presentation_models.NRENService(
+            nren=nren,
+            nren_id=nren.id,
+            year=service_info['year'],
+            service=service,
+            service_key=service.name_key,
+            product_name=service_info['product_name'],
+            additional_information=service_info['additional_information'],
+            official_description=service_info['official_description']
+        )
+
+        db.session.merge(nren_service)
+
+    db.session.commit()
+
+
+def _cli(app):
     with app.app_context():
         nren_dict = helpers.get_uppercase_nren_dict()
         db_budget_migration(nren_dict)
@@ -234,6 +279,8 @@ def _cli(config, app):
         db_ecprojects_migration(nren_dict)
         db_organizations_migration(nren_dict)
         db_traffic_volume_migration(nren_dict)
+        db_services_migration()
+        db_nren_services_migration(nren_dict)
 
 
 @click.command()
@@ -245,7 +292,7 @@ def cli(config):
 
     app = compendium_v2._create_app_with_db(app_config)
     print("survey-publisher-v1 starting")
-    _cli(app_config, app)
+    _cli(app)
 
 
 if __name__ == "__main__":
diff --git a/compendium_v2/resources/__init__.py b/compendium_v2/resources/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e2499aeb09afcf692f1532633362b5e0d8dd3647
--- /dev/null
+++ b/compendium_v2/resources/__init__.py
@@ -0,0 +1,6 @@
+from pathlib import Path
+
+
+def get_resource_file_path(filename: str) -> Path:
+    resource_dir = Path(__file__).resolve().parent
+    return resource_dir / filename
diff --git a/compendium_v2/resources/nren_services.json b/compendium_v2/resources/nren_services.json
new file mode 100644
index 0000000000000000000000000000000000000000..29d14104ef2d6dfed5d9d90b4c8c7cb917d29020
--- /dev/null
+++ b/compendium_v2/resources/nren_services.json
@@ -0,0 +1,372 @@
+{
+  "anti-spam": {
+    "name": "Anti-spam solution",
+    "category": "security",
+    "description": "Anti-spam solutions for detecting and eliminating viruses and spam mails."
+  },
+  "class-registration": {
+    "name": "Class registration tool",
+    "category": "collaboration",
+    "description": "Software for teachers to register students for classes etc."
+  },
+  "cloud-service-end-user": {
+    "name": "Cloud storage (end user)",
+    "category": "storage_and_hosting",
+    "description": "Browser based virtual storage service for individuals."
+  },
+  "connectivity": {
+    "name": "IP connectivity",
+    "category": "network_services",
+    "description": "Basic IP connectivity services inc. R+E and commodity internet"
+  },
+  "consultancy": {
+    "name": "Consultancy/training",
+    "category": "professional_services",
+    "description": "Training and consultancy services provided by the NREN."
+  },
+  "content-delivery-hosting": {
+    "name": "Content delivery hosting",
+    "category": "storage_and_hosting",
+    "description": "Hosting of content delivery servers e.g. Akamai."
+  },
+  "content-management-system": {
+    "name": "CMS",
+    "category": "collaboration",
+    "description": "Provision of software systems for website authoring, collaboration and administration tools."
+  },
+  "csirt": {
+    "name": "CERT/CSIRT",
+    "category": "security",
+    "description": "A single point of contact for users to deal with computer security incidents and prevention."
+  },
+  "ddos-prevention": {
+    "name": "DDos mitigation",
+    "category": "security",
+    "description": "Tools and techniques for mitigating Distributed Denial of Service attacks."
+  },
+  "domain-registration": {
+    "name": "Domain name registration",
+    "category": "isp_support",
+    "description": "Administration/registration of top and second level domain names."
+  },
+  "eduroam-wifi": {
+    "name": "Eduroam",
+    "category": "identity",
+    "description": "Inter-WLAN service to facilitate easy and secure Internet access for roaming educational users."
+  },
+  "email-services": {
+    "name": "Email server hosting",
+    "category": "collaboration",
+    "description": "NREN hosted email servers."
+  },
+  "filesender": {
+    "name": "Filesender",
+    "category": "storage_and_hosting",
+    "description": "Web based application that allows authenticated users to securely and easily send arbitrarily large files."
+  },
+  "firewall-on-demand": {
+    "name": "Firewall-on-demand",
+    "category": "security",
+    "description": "Provision of a dynamic firewall services to mitigate against DDOS attacks. "
+  },
+  "home-vpn": {
+    "name": "Remote access VPN service",
+    "category": "network_services",
+    "description": "Remote access and site-to-site secure VPN."
+  },
+  "aai": {
+    "name": "Hosted campus AAI",
+    "category": "identity",
+    "description": "Hosting of an Identity Provider service on behalf of connected institutions to authenticate users."
+  },
+  "instant-messaging": {
+    "name": "Instant messaging",
+    "category": "collaboration",
+    "description": "Commercial or NREN own online chat service which offers real-time text transmission over the Internet."
+  },
+  "interfederation": {
+    "name": "Interfederation",
+    "category": "identity",
+    "description": "Participation in an inter-federation (i.e. eduGAIN, KALMAR)."
+  },
+  "internet-radio-tv": {
+    "name": "TV/radio streaming",
+    "category": "multimedia",
+    "description": "Internet and radio streaming services."
+  },
+  "internship-database": {
+    "name": "Database services",
+    "category": "collaboration",
+    "description": "Provision of tools or services to create or host databases."
+  },
+  "ip-address-allocation": {
+    "name": "IP address allocation/LIR",
+    "category": "isp_support",
+    "description": "Local Internet Registry service for assigning of IP address space."
+  },
+  "ipv6": {
+    "name": "IPv6",
+    "category": "network_services",
+    "description": "The new version of the Internet Protocol (IP) that should eventually replace the current IP (version 4)."
+  },
+  "ix-operation": {
+    "name": "National IX operation",
+    "category": "isp_support",
+    "description": "Operation of a national Internet Exchange."
+  },
+  "journal-library-access": {
+    "name": "Journal access",
+    "category": "collaboration",
+    "description": "Access to academic journals."
+  },
+  "lambda": {
+    "name": "Optical wavelength",
+    "category": "network_services",
+    "description": "Layer 1 optical channel for provision of dedicated capacity to demanding users."
+  },
+  "mailing-lists": {
+    "name": "Mailing lists",
+    "category": "collaboration",
+    "description": "Service for operation of electronic discussion lists."
+  },
+  "mobile-data": {
+    "name": "3G/4G data service",
+    "category": "network_services",
+    "description": "Provision of mobile broadband and phone contracts to users."
+  },
+  "multicast": {
+    "name": "Multicast",
+    "category": "network_services",
+    "description": "Extension to the IP protocol which allows individual packets to be sent to multiple hosts on the Internet."
+  },
+  "news-service": {
+    "name": "Netnews/USENET service",
+    "category": "storage_and_hosting",
+    "description": "Netnews/USENET news feed service."
+  },
+  "online-payment": {
+    "name": "Online payment connectivity",
+    "category": "identity",
+    "description": "Connectivity for chip and pin payment terminals."
+  },
+  "open-lightpath-exchange": {
+    "name": "Open Lightpath Exchange",
+    "category": "network_services",
+    "description": "Provision of an Open Lightpath exchange for users to connect to other parties."
+  },
+  "pert": {
+    "name": "PERT",
+    "category": "network_services",
+    "description": "Team supporting resolution of end-to-end performance problems for networked applications."
+  },
+  "plagarism-detection": {
+    "name": "Plagarism detection",
+    "category": "collaboration",
+    "description": "Provision of software for use by teachers etc to detect plagarism."
+  },
+  "point-to-point-circuit-vpn": {
+    "name": "Virtual circuit/VPN",
+    "category": "network_services",
+    "description": "Virtual point to point circuits or VPNs delivered as a service to users."
+  },
+  "procurement": {
+    "name": "Procurement/brokerage",
+    "category": "professional_services",
+    "description": "Procurement services, inc. negotiating agreements and framework agreements."
+  },
+  "project-collaboration-toolkit": {
+    "name": "Project collaboration tools",
+    "category": "collaboration",
+    "description": "Packaged services for virtual project groups e.g. mailing lists, storage, web meetings, wiki."
+  },
+  "quality-of-service": {
+    "name": "Quality of Service",
+    "category": "network_services",
+    "description": "Preferential service to specific applications or classes of applications."
+  },
+  "scheduling-tool": {
+    "name": "Scheduling tool",
+    "category": "collaboration",
+    "description": "Provision of tools to users for scheduling appointments or classes."
+  },
+  "sdn-testbed": {
+    "name": "SDN testbed",
+    "category": "network_services",
+    "description": "Software defined networking testbed available to users."
+  },
+  "sms-messaging": {
+    "name": "SMS messaging",
+    "category": "collaboration",
+    "description": "Service for users to send or receive SMS message by email."
+  },
+  "software-development": {
+    "name": "Software development",
+    "category": "professional_services",
+    "description": "Software development service for users."
+  },
+  "software-licenses": {
+    "name": "Software licenses",
+    "category": "professional_services",
+    "description": "Provision of software for organisational or institutional purchase."
+  },
+  "storage-co-location": {
+    "name": "Housing/co-location",
+    "category": "storage_and_hosting",
+    "description": "Hosting of user equipment in a managed data centre."
+  },
+  "survey-tool": {
+    "name": "Survey/polling tool",
+    "category": "collaboration",
+    "description": "Provision of applications for creating surveys or polls."
+  },
+  "system-backup": {
+    "name": "Hot standby",
+    "category": "storage_and_hosting",
+    "description": "Failover protection for primary web servers."
+  },
+  "timeserver-ntp": {
+    "name": "NTP service",
+    "category": "isp_support",
+    "description": "Allows the synchronization of computer clocks over  the Internet."
+  },
+  "video-portal": {
+    "name": "Provision of content portal/s to users for hosting and viewing multi-media content.",
+    "category": "multimedia",
+    "description": "Multi-media content portal"
+  },
+  "videoconferencing": {
+    "name": "Event recording/streaming",
+    "category": "multimedia",
+    "description": "Provision of equipment and/or software to support event streaming/recording."
+  },
+  "virtual-learning-environment": {
+    "name": "VLE",
+    "category": "collaboration",
+    "description": "Online e-learning education system that  provides virtual access to resources used in teaching.."
+  },
+  "virtual-machines-iaas": {
+    "name": "Virtual machines/IaaS",
+    "category": "storage_and_hosting",
+    "description": "Access to virtual computing resources."
+  },
+  "voip": {
+    "name": "VoIP",
+    "category": "collaboration",
+    "description": "Service to deliver voice communications and multimedia sessions over Internet Protocol (IP) networks."
+  },
+  "vulnerability-testing": {
+    "name": "Vulnerability scanning",
+    "category": "security",
+    "description": "Vulnerability service that allows users to scan their own IP networks for security holes."
+  },
+  "web-conferencing": {
+    "name": "Web/desktop conferencing",
+    "category": "multimedia",
+    "description": "Video conferencing service to desktops and hand-held devices using software."
+  },
+  "web-design-production": {
+    "name": "Web development",
+    "category": "professional_services",
+    "description": "Web development service provided to NREN users."
+  },
+  "web-email-hosting": {
+    "name": "Web hosting",
+    "category": "storage_and_hosting",
+    "description": "Service to provide space on central web servers for users to publish their website."
+  },
+  "dns-server": {
+    "name": "DNS hosting",
+    "category": "storage_and_hosting",
+    "description": "Hosting of primary and secondary DNS servers."
+  },
+  "dissemination": {
+    "name": "Dissemination",
+    "category": "professional_services",
+    "description": "Dissemination of information to users e.g. newsletters and magazines."
+  },
+  "network-monitoring": {
+    "name": "Network monitoring",
+    "category": "network_services",
+    "description": "Network information system that shows the current and past performance of the network."
+  },
+  "disaster-recovery": {
+    "name": "Disaster recovery",
+    "category": "storage_and_hosting",
+    "description": "Off site backup services."
+  },
+  "user-monitoring": {
+    "name": "Network troubleshooting",
+    "category": "network_services",
+    "description": "Enables users at connected institutions to monitor Internet services in real time."
+  },
+  "netflow": {
+    "name": "Netflow tool",
+    "category": "network_services",
+    "description": "Network protocol for collecting IP traffic information and monitoring network traffic."
+  },
+  "managed-router": {
+    "name": "Managed router service",
+    "category": "network_services",
+    "description": "Remote network router support to institutions."
+  },
+  "intrusion": {
+    "name": "Intrusion detection",
+    "category": "security",
+    "description": "Systems for detecting and preventing Intrusions (IDS/IPS)."
+  },
+  "security-audit": {
+    "name": "Security auditing",
+    "category": "security",
+    "description": "Carrying out vulnerability assesments and security reviews of user systems and resources on their behalf."
+  },
+  "web-filtering": {
+    "name": "Web filtering",
+    "category": "security",
+    "description": "Centralised web content filtering service for protection against access to inappropriate content."
+  },
+  "pgp-key": {
+    "name": "PGP key server",
+    "category": "security",
+    "description": "Operation of PGP key server."
+  },
+  "identifier-reg": {
+    "name": "Identifier registry",
+    "category": "security",
+    "description": "Registering of unique and automatically-processable identifiers in the form of text or numeric strings."
+  },
+  "post-production": {
+    "name": "Media post production",
+    "category": "multimedia",
+    "description": "Work undertaken after the process of recording (or production) e.g. editing, synchronisation."
+  },
+  "e-portfolio": {
+    "name": "e-portfolio service",
+    "category": "collaboration",
+    "description": "Functions to create and share user professional and career e-portfolios."
+  },
+  "user-conference": {
+    "name": "User conferences",
+    "category": "professional_services",
+    "description": "Hosting of regular user conferences."
+  },
+  "user-portal": {
+    "name": "User portals",
+    "category": "professional_services",
+    "description": "User portal for service management and monitoring."
+  },
+  "admin-tools": {
+    "name": "Finance/admin systems",
+    "category": "professional_services",
+    "description": "Provision of ICT systems used in finance and administration."
+  },
+  "nameserver": {
+    "name": "Nameserver services",
+    "category": "isp_support",
+    "description": "Operation of nameservers and maintenance of DNS information on behalf of users."
+  },
+  "saas": {
+    "name": "SaaS",
+    "category": "storage_and_hosting",
+    "description": "Software as a service e.g. GoogleApps for Education."
+  }
+}
\ No newline at end of file
diff --git a/compendium_v2/routes/api.py b/compendium_v2/routes/api.py
index 9af16ad2ced857457ba14daa49cf8ab841a9c81b..cc7ac6e0d5c8530611cea3acae7a38fb052f4979 100644
--- a/compendium_v2/routes/api.py
+++ b/compendium_v2/routes/api.py
@@ -16,6 +16,7 @@ from compendium_v2.routes.nren import routes as nren_routes
 from compendium_v2.routes.response import routes as response_routes
 from compendium_v2.routes.traffic import routes as traffic_routes
 from compendium_v2.routes.institutions_urls import routes as institutions_urls_routes
+from compendium_v2.routes.nren_services import routes as nren_services_routes
 
 routes = Blueprint('compendium-v2-api', __name__)
 routes.register_blueprint(budget_routes, url_prefix='/budget')
@@ -31,6 +32,7 @@ routes.register_blueprint(nren_routes, url_prefix='/nren')
 routes.register_blueprint(response_routes, url_prefix='/response')
 routes.register_blueprint(traffic_routes, url_prefix='/traffic')
 routes.register_blueprint(institutions_urls_routes, url_prefix='/institutions-urls')
+routes.register_blueprint(nren_services_routes, url_prefix='/nren-services')
 
 logger = logging.getLogger(__name__)
 
diff --git a/compendium_v2/routes/institutions_urls.py b/compendium_v2/routes/institutions_urls.py
index 8af1bbb67cd51df9a922ca0be240ba4278856713..45c71287bb1ed025810df7377e0d2c4f39a6a352 100644
--- a/compendium_v2/routes/institutions_urls.py
+++ b/compendium_v2/routes/institutions_urls.py
@@ -1,10 +1,8 @@
 from typing import Any
 
 from flask import Blueprint, jsonify
-from sqlalchemy import select
 
-from compendium_v2.db import db
-from compendium_v2.db.presentation_models import NREN, InstitutionURLs
+from compendium_v2.db.presentation_models import InstitutionURLs
 from compendium_v2.routes import common
 
 routes = Blueprint('institutions-urls', __name__)
@@ -40,7 +38,10 @@ def institutions_urls_view() -> Any:
     or organizations connected to the NREN.
     Many NRENs have one or more pages on their website listing such user institutions.
 
-    response will be formatted as per INSTITUTION_URLS_RESPONSE_SCHEMA
+    response will be formatted as:
+
+    .. asjson::
+        compendium_v2.routes.institutions_urls.INSTITUTION_URLS_RESPONSE_SCHEMA
 
     :return:
     """
@@ -54,8 +55,7 @@ def institutions_urls_view() -> Any:
         }
 
     entries = []
-    records = db.session.scalars(
-        select(InstitutionURLs).join(NREN).order_by(NREN.name.asc(), InstitutionURLs.year.desc()))
+    records = common.get_data(InstitutionURLs)
 
     for entry in records:
         entries.append(_extract_data(entry))
diff --git a/compendium_v2/routes/nren_services.py b/compendium_v2/routes/nren_services.py
new file mode 100644
index 0000000000000000000000000000000000000000..f4ed08b9a7242e9111e68988e06b29c27abd4fa6
--- /dev/null
+++ b/compendium_v2/routes/nren_services.py
@@ -0,0 +1,66 @@
+from typing import Any
+
+from flask import Blueprint, jsonify
+
+from compendium_v2.db.presentation_models import NRENService
+from compendium_v2.routes import common
+
+routes = Blueprint('nren-services', __name__)
+
+NREN_SERVICES_RESPONSE_SCHEMA = {
+    '$schema': 'http://json-schema.org/draft-07/schema#',
+    'definitions': {
+        'nren_services': {
+            'type': 'object',
+            'properties': {
+                'nren': {'type': 'string'},
+                'nren_country': {'type': 'string'},
+                'year': {'type': 'integer'},
+                'product_name': {'type': 'string'},
+                'additional_information': {'type': 'string'},
+                'official_description': {'type': 'string'},
+                'service_name': {'type': 'string'},
+                'service_category': {'type': 'string'},
+                'service_description': {'type': 'string'}
+            },
+            'required': ['nren', 'nren_country', 'year', 'product_name', 'additional_information',
+                         'official_description', 'service_name', 'service_category', 'service_description'],
+            'additionalProperties': False
+        }
+    },
+    'type': 'array',
+    'items': {'$ref': '#/definitions/nren_services'}
+}
+
+
+@routes.route('/', methods=['GET'])
+@common.require_accepts_json
+def nren_service_view() -> Any:
+    """
+    handler for /api/nren-services/ requests
+    Endpoint for getting the service matrix that shows which NREN uses which services.
+
+    response will be formatted as:
+
+    .. asjson::
+        compendium_v2.routes.nren_services.NREN_SERVICES_RESPONSE_SCHEMA
+
+    :return:
+    """
+
+    def _extract_data(nren_service: NRENService) -> dict:
+        return {
+            'nren': nren_service.nren.name,
+            'nren_country': nren_service.nren.country,
+            'year': nren_service.year,
+            'product_name': nren_service.product_name,
+            'additional_information': nren_service.additional_information,
+            'official_description': nren_service.official_description,
+            'service_name': nren_service.service.name,
+            'service_category': nren_service.service.category.value,
+            'service_description': nren_service.service.description
+        }
+
+    entries = [_extract_data(entry) for entry in common.get_data(NRENService)]
+
+    return jsonify(entries)
diff --git a/test/conftest.py b/test/conftest.py
index 591fe482257bc5bf6e96244309c9f7ed484dc57f..7bb203ae7cbed1d9b25d4ada1f0c3281b4cd509c 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -7,6 +7,7 @@ from sqlalchemy import select
 from flask_login import LoginManager  # type: ignore
 import compendium_v2
 from compendium_v2.db import db, presentation_models, survey_models
+from compendium_v2.db.presentation_model_enums import ServiceCategory
 from compendium_v2.survey_db import model as survey_db_model
 from compendium_v2.auth.session_management import setup_login_manager, User, ROLES
 
@@ -408,3 +409,60 @@ def test_institution_urls_data(app):
         created_nrens = _create_and_save_nrens(unique_nren_names)
         _create_and_save_institution_urls(predefined_nrens_and_years, created_nrens)
         db.session.commit()
+
+
+@pytest.fixture
+def test_nren_services_data(app):
+    def _create_and_save_nrens(nren_names):
+        nrens = {}
+        for nren_name in nren_names:
+            nren_instance = presentation_models.NREN(name=nren_name, country='country')
+            nrens[nren_name] = nren_instance
+            db.session.add(nren_instance)
+        return nrens
+
+    def _create_and_save_services(services):
+        services_map = {}
+        for service in services:
+            service_instance = presentation_models.Service(
+                name=service[0],
+                name_key=service[1],
+                description='description',
+                category=service[2],
+            )
+            services_map[service[1]] = service_instance
+            db.session.add(service_instance)
+
+        return services_map
+
+    def _create_and_save_nren_services(nren_services, nrens, services):
+        for nren_name, year, service_name, service_name_key, service_category in nren_services:
+            nren_instance = nrens[nren_name]
+            service_instance = services[service_name_key]
+
+            institution_urls_model = presentation_models.NRENService(
+                nren=nren_instance,
+                year=year,
+                service=service_instance,
+                product_name="brand name",
+                additional_information="extra info",
+                official_description="description"
+            )
+
+            db.session.add(institution_urls_model)
+
+    with app.app_context():
+        predefined_nrens_services = [
+            ('nren1', 2019, 'service1', 'service1_key', ServiceCategory.network_services.value),
+            ('nren1', 2020, 'service1', 'service1_key', ServiceCategory.network_services.value),
+            ('nren1', 2021, 'service1', 'service1_key', ServiceCategory.network_services.value),
+            ('nren2', 2019, 'service2', 'service2_key', ServiceCategory.professional_services.value),
+            ('nren2', 2021, 'service2', 'service2_key', ServiceCategory.professional_services.value)
+        ]
+
+        unique_nren_names = {nren[0] for nren in predefined_nrens_services}
+        unique_services = {nren[2:] for nren in predefined_nrens_services}
+        created_nrens = _create_and_save_nrens(unique_nren_names)
+        created_services = _create_and_save_services(unique_services)
+        _create_and_save_nren_services(predefined_nrens_services, created_nrens, created_services)
+        db.session.commit()
diff --git a/test/test_nren_services.py b/test/test_nren_services.py
new file mode 100644
index 0000000000000000000000000000000000000000..ce832e6e1147c5f973e70c2baccea0434e326995
--- /dev/null
+++ b/test/test_nren_services.py
@@ -0,0 +1,14 @@
+import json
+import jsonschema
+
+from compendium_v2.routes.nren_services import NREN_SERVICES_RESPONSE_SCHEMA
+
+
+def test_nren_services(client, test_nren_services_data):
+    rv = client.get(
+        '/api/nren-services/',
+        headers={'Accept': ['application/json']})
+    assert rv.status_code == 200
+    result = json.loads(rv.data.decode('utf-8'))
+    jsonschema.validate(result, NREN_SERVICES_RESPONSE_SCHEMA)
+    assert result
diff --git a/test/test_survey_publisher.py b/test/test_survey_publisher.py
index 3e66d6f0605d1a62195f49355dc3bd70bbe61c65..2889eebbc26d928de2af57e3df5d346fd1aee742 100644
--- a/test/test_survey_publisher.py
+++ b/test/test_survey_publisher.py
@@ -306,3 +306,15 @@ def test_v2_publisher_full(app):
         assert network_automation.network_automation_specifics == [
             "config_management", "provisioning", "data_collection", "compliance", "reporting", "troubleshooting"
         ]
+
+        services = [s for s in db.session.scalars(select(presentation_models.NRENService))]
+        assert len(services) == 59
+        virtual_learning_environment = [s for s in services if s.service_key == "virtual-learning-environment"]
+        assert len(virtual_learning_environment) == 1
+        assert virtual_learning_environment[0].additional_information == "zd"
+        csirt = [s for s in services if s.service_key == "csirt"]
+        assert len(csirt) == 1
+        assert csirt[0].official_description == "zdf"
+        connectivity = [s for s in services if s.service_key == "connectivity"]
+        assert len(connectivity) == 1
+        assert connectivity[0].product_name == "zfz"
diff --git a/test/test_survey_publisher_legacy_excel.py b/test/test_survey_publisher_legacy_excel.py
index 5a20fc063e843f3d930ce463034f5f910762bf5b..b066767e579ce05fe14f1dcba005b7e0af2276ff 100644
--- a/test/test_survey_publisher_legacy_excel.py
+++ b/test/test_survey_publisher_legacy_excel.py
@@ -9,15 +9,15 @@ from compendium_v2.publishers.survey_publisher_legacy_excel import _cli
 EXCEL_FILE = os.path.join(os.path.dirname(__file__), "data", "2021_Organisation_DataSeries.xlsx")
 
 
-def test_publisher(app_with_survey_db, mocker, dummy_config):
-    mocker.patch('compendium_v2.publishers.excel_parser.EXCEL_FILE', EXCEL_FILE)
+def test_excel_publisher(app_with_survey_db, mocker):
+    mocker.patch('compendium_v2.publishers.excel_parser.EXCEL_FILE_ORGANISATION', EXCEL_FILE)
 
     with app_with_survey_db.app_context():
         nren_names = ['SURF', 'KIFU', 'University of Malta', 'ASNET-AM', 'SIKT', 'LAT', 'RASH', 'ANAS', 'GRNET', 'CSC']
         db.session.add_all([presentation_models.NREN(name=nren_name, country='country') for nren_name in nren_names])
         db.session.commit()
 
-    _cli(dummy_config, app_with_survey_db)
+    _cli(app_with_survey_db)
 
     with app_with_survey_db.app_context():
         budget_count = db.session.scalar(select(func.count(presentation_models.BudgetEntry.year)))
@@ -94,3 +94,13 @@ def test_publisher(app_with_survey_db, mocker, dummy_config):
         assert len(asnet2021) == 1
         assert asnet2021[0].organization\
             == 'Institute for Informatics and Automation Problems of the National Academy of Sciences of Armenia'
+
+        service_data = db.session.scalars(select(presentation_models.Service)).all()
+        assert len(service_data) == 74
+
+        nren_service_data = db.session.scalars(select(presentation_models.NRENService)).all()
+        # test a random entry
+        sikt2022 = [x for x in nren_service_data
+                    if x.nren.name == 'SIKT' and x.year == 2022 and x.service.name == 'Journal access']
+        assert len(sikt2022) == 1
+        assert sikt2022[0].additional_information.startswith("Sikt negotiates license a")
diff --git a/test/test_db_survey_publisher_2022.py b/test/test_survey_publisher_old_db_2022.py
similarity index 100%
rename from test/test_db_survey_publisher_2022.py
rename to test/test_survey_publisher_old_db_2022.py