diff --git a/gso/oss-params-example.json b/gso/oss-params-example.json
index 4ee622611c12c48be7874eeab7c8ffadac395889..7ff96dad784fcd70156331ab2d9ce910dac99078 100644
--- a/gso/oss-params-example.json
+++ b/gso/oss-params-example.json
@@ -1,6 +1,7 @@
 {
   "GENERAL": {
-    "public_hostname": "https://gap.geant.org"
+    "public_hostname": "https://gap.geant.org",
+    "environment": "lab"
   },
   "NETBOX": {
     "api": "https://127.0.0.1:8000",
@@ -45,6 +46,30 @@
       "dns_view": "default"
     }
   },
+  "MONITORING": {
+    "LIBRENMS": {
+      "endpoint": "https://librenms.test.gap.geant.org/",
+      "token": "<token>",
+      "DEVICE_GROUPS": {
+        "routers_lab": "lab_routers",
+        "routers_prod": "prod_routers"
+      }
+    },
+    "SNMP": {
+      "version": "v2c",
+      "V2": {
+        "community": "librenms-community"
+      },
+      "V3": {
+        "authlevel": "AuthPriv",
+        "authname": "librenms",
+        "authpass": "<password1>",
+        "authalgo": "sha",
+        "cryptopass": "<password2>",
+        "cryptoalgo": "aes"
+      }
+    }
+  },
   "PROVISIONING_PROXY": {
     "scheme": "https",
     "api_base": "localhost:44444",
diff --git a/gso/services/librenms.py b/gso/services/librenms.py
new file mode 100644
index 0000000000000000000000000000000000000000..1ab11514c3972389d4f01c34165aee79503e25f8
--- /dev/null
+++ b/gso/services/librenms.py
@@ -0,0 +1,176 @@
+"""
+The LibreNMS module interacts with the LibreNMS instance when
+- Creating a device.
+- Validating the input of a device.
+- Terminating a device.
+"""
+import json
+import logging
+import requests
+from gso import settings
+
+
+logger = logging.getLogger(__name__)
+
+
+class CfgStruct(object):
+    pass
+
+
+def _get_cfg():
+    """
+    Internal function to retrieve all needed configuration.
+    """
+    oss = settings.load_oss_params()
+    cfg = CfgStruct()
+    # Hack for later ease: 1st setattr will fill in the inner's dict
+    setattr(cfg, "_hack", "")
+    # Update inner dict
+    cfg.__dict__.update(oss.MONITORING)
+    assert cfg.__dict__ is not None
+
+    # Add parameters on-the-fly
+    cfg.headers = {"X-Auth-Token": cfg.LIBRENMS.token}
+
+    sep = "/"
+    if cfg.LIBRENMS.endpoint.endswith("/"):
+        sep = ""
+    cfg.base_url = f"{cfg.LIBRENMS.endpoint}{sep}api/v0"
+    cfg.url_devices = f"{cfg.base_url}/devices"
+    cfg.url_switches = f"{cfg.base_url}/devicegroups/switches"
+    cfg.device_groups = cfg.LIBRENMS.DEVICE_GROUPS
+    cfg.environment = oss.GENERAL.environment
+    if cfg.environment.startswith("lab"):
+        cfg_dg_rtr_lab = cfg.device_groups.routers_lab
+        cfg.url_routers = f"{cfg.base_url}/devicegroups/{cfg_dg_rtr_lab}"
+    elif cfg.environment.startswith("prod"):
+        cfg_dg_rtr_prod = cfg.device_groups.routers_prod
+        cfg.url_routers = f"{cfg.base_url}/devicegroups/{cfg_dg_rtr_prod}"
+
+    return cfg
+
+
+def validate_device(fqdn: str):
+    """
+    Function that validates the existence of a device in LibreNMS.
+
+    :param FQDN of the device to validate.
+    """
+    CFG = _get_cfg()
+
+    # Validate existence
+    nms_result = requests.get(
+        CFG.url_devices, headers=CFG.headers)
+    assert nms_result is not None
+
+    device_id = list(map(
+        lambda x: x.get("device_id"),
+        filter(lambda x: x.get("hostname") == fqdn,
+               nms_result.json().get("devices"))))
+
+    if len(device_id) != 1 or device_id[0] is None:
+        error_msg = f"Device with FQDN={fqdn} is not registered in LibreNMS"
+        print(error_msg)
+        raise AssertionError(error_msg)
+
+    # Validate correctness
+    device_id = device_id[0]
+    url_device = f"{CFG.url_devices}/{device_id}"
+    logger.debug(f"Connecting to URL: {url_device}"
+                 f"with headers: {CFG.headers}")
+    nms_result = requests.get(
+        url_device, headers=CFG.headers)
+    logger.debug(f"LibreNMS response={nms_result.content}")
+
+    if nms_result.status_code != 200:
+        print(nms_result.content)
+        raise AssertionError(nms_result.content)
+
+    # nms_dev_sysname = nms_result.json().get("sysName")
+    nms_dev_hostname = nms_result.json().get("devices")[0].get("hostname")
+    if fqdn != nms_dev_hostname:
+        error_msg = f"Device with FQDN={fqdn} may not be correctly "\
+                f"registered in LibreNMS (expected FQDN: {nms_dev_hostname})"
+        print(error_msg)
+        raise AssertionError(error_msg)
+
+
+def register_device(fqdn: str):
+    """
+    Function that registers a new device in LibreNMS.
+
+    :param FQDN of the device to register.
+    """
+    CFG = _get_cfg()
+    logger.debug(f"Registering FQDN={fqdn} in LibreNMS")
+
+    device_data = {
+        "display": fqdn,
+        "hostname": fqdn,
+        "sysName": fqdn,
+        # "override_icmp_disable": "true",
+        # IMPORTANT: uncomment if testing with FQDNs that are not reachable
+        # from LibreNMS (e.g. ContainerLab routers)
+        # "force_add": "true"
+    }
+    if CFG.SNMP.version == "v2c":
+        device_data.update({
+            "community": CFG.SNMP.V2.community
+            })
+
+    elif CFG.SNMP.version == "v3":
+        for key in [
+                "authlevel", "authname", "authpass", "authalgo",
+                "cryptopass", "cryptoalgo"]:
+            device_data.update({key: getattr(CFG.SNMP.V3, key)})
+
+    logger.debug(f"Connecting to URL: {CFG.url_devices}"
+                 f"with headers: {CFG.headers} and"
+                 f"payload: {device_data}")
+    nms_result = requests.post(
+        CFG.url_devices, headers=CFG.headers,
+        data=json.dumps(device_data))
+    logger.debug(f"LibreNMS response={nms_result.content}")
+
+    if nms_result.status_code != 200:
+        print(nms_result.content)
+        raise AssertionError(nms_result.content)
+
+
+def deregister_device(fqdn: str):
+    """
+    Function that reregisters a device from LibreNMS.
+
+    :param FQDN of the device to deregister.
+    """
+    CFG = _get_cfg()
+    logger.debug(f"Deregistering FQDN={fqdn} from LibreNMS")
+
+    nms_result = requests.get(
+        CFG.url_devices, headers=CFG.headers)
+    assert nms_result is not None
+    device_id = list(map(
+        lambda x: x.get("device_id"),
+        filter(lambda x: x.get("hostname") == fqdn,
+               nms_result.json().get("devices"))))
+    if len(device_id) != 1:
+        return
+    device_id = device_id[0]
+
+    # https://docs.librenms.org/API/Devices/#endpoint-categories
+    device_data = {
+        "field": "disabled",
+        "data": "1"
+    }
+    url_device = f"{CFG.url_devices}/{device_id}"
+    logger.debug(f"Connecting to URL: {url_device}"
+                 f"with headers: {CFG.headers} and"
+                 f"payload: {device_data}")
+    nms_result = requests.patch(
+        url_device, headers=CFG.headers,
+        data=json.dumps(device_data))
+    logger.debug(f"LibreNMS response={nms_result.content}")
+
+    # Fail silently if device was not registered
+    if nms_result.status_code != 200:
+        print(nms_result.content)
diff --git a/gso/settings.py b/gso/settings.py
index 78a27d756b03de700f1f7afcbcca6fe681a960f9..0e9d55e3f4c5345628031f7fe4ea3168d37ba7f9 100644
--- a/gso/settings.py
+++ b/gso/settings.py
@@ -21,6 +21,8 @@ class GeneralParams(BaseSettings):
     public_hostname: str
     """The hostname that :term:`GSO` is publicly served at, used for building the callback URL that the provisioning
     proxy uses."""
+    environment: str
+    """The environment in which :term:`GSO` runs."""
 
 
 class CeleryParams(BaseSettings):
@@ -94,6 +96,59 @@ class IPAMParams(BaseSettings):
     LT_IAS: ServiceNetworkParams
 
 
+class MonitoringLibreNMSDevGroupsParams(BaseSettings):
+    """
+    Parameters related to LibreNMS device groups.
+    """
+    routers_lab: str
+    routers_prod: str
+
+
+class MonitoringSNMPV2Params(BaseSettings):
+    """
+    Parameters related to SNMPv2.
+    """
+    community: str
+
+
+class MonitoringSNMPV3Params(BaseSettings):
+    """
+    Parameters related to SNMPv3.
+    """
+    authlevel: str
+    authname: str
+    authpass: str
+    authalgo: str
+    cryptopass: str
+    cryptoalgo: str
+
+
+class MonitoringSNMPParams(BaseSettings):
+    """
+    Parameters related to SNMP.
+    """
+    version: str
+    V2: MonitoringSNMPV2Params
+    V3: MonitoringSNMPV3Params
+
+
+class MonitoringLibreNMSParams(BaseSettings):
+    """
+    Parameters related to LibreNMS.
+    """
+    endpoint: str
+    token: str
+    DEVICE_GROUPS: MonitoringLibreNMSDevGroupsParams
+
+
+class MonitoringParams(BaseSettings):
+    """
+    Parameters related to the monitoring.
+    """
+    LIBRENMS: MonitoringLibreNMSParams
+    SNMP: MonitoringSNMPParams
+
+
 class ProvisioningProxyParams(BaseSettings):
     """Parameters for the provisioning proxy."""
 
@@ -118,6 +173,7 @@ class OSSParams(BaseSettings):
     GENERAL: GeneralParams
     IPAM: IPAMParams
     NETBOX: NetBoxParams
+    MONITORING: MonitoringParams
     PROVISIONING_PROXY: ProvisioningProxyParams
     CELERY: CeleryParams