diff --git a/check_docker.sh b/check_docker.sh
index c4a79264667168b51124868b82df1e58b14fc488..880b890e7ebb877e689317703befe995749f6b62 100755
--- a/check_docker.sh
+++ b/check_docker.sh
@@ -1,5 +1,8 @@
 #!/bin/bash
 
+# Copyright © 2023 Le Filament (<http://www.le-filament.com>)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
 # Default threshold values.
 cpu_threshold_warning=25
 cpu_threshold_critical=50
diff --git a/check_fail2ban.sh b/check_fail2ban.sh
index 22e35bfee4d3cdeb7524059908c0e7872e00c409..7588c95fc22a259ba9875f2d0a06cea8110c0db3 100755
--- a/check_fail2ban.sh
+++ b/check_fail2ban.sh
@@ -1,5 +1,8 @@
 #!/bin/bash
 
+# Copyright © 2023 Le Filament (<http://www.le-filament.com>)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
 # Default threshold values.
 ips_threshold_warning=20
 ips_threshold_critical=40
diff --git a/check_psi.py b/check_psi.py
new file mode 100755
index 0000000000000000000000000000000000000000..c4ac5275c2ea7e9cbf3c17cecc028c8bc3d838ca
--- /dev/null
+++ b/check_psi.py
@@ -0,0 +1,147 @@
+#!/usr/bin/env python3
+
+# Copyright © 2025 Le Filament (<http://www.le-filament.com>)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
+import sys
+
+
+SELECTED_KEYS = ["avg10", "avg60", "avg300", "total_share"]
+
+
+def read_psi(psi_path):
+    with open(psi_path, "r") as psi_file:
+        psi_data = {}
+        for line in psi_file.readlines():
+            line = line.split()
+            metric = line[0]
+            values = line[1:]
+            key_values = [kv.split("=") for kv in values]
+            psi_data[metric] = {k: float(v) for k, v in key_values}
+        return psi_data
+
+
+class MetricPSIConfig:
+    def __init__(self, argv):
+        self.ressource = None
+        self.metric = None
+        self.thresholds = {"warning": {}, "critical": {}}
+
+        argv = iter(argv)
+        for arg in argv:
+            match arg:
+                case "-r" | "--ressource":
+                    self.ressource = next(argv)
+                case "-m" | "--metric":
+                    self.metric = next(argv).split(",")
+                case "-w" | "--warning":
+                    all_metric_warnings = next(argv).split(";")
+                    for metric_warnings in all_metric_warnings:
+                        metric, warnings = metric_warnings.split(":")
+                        warnings = warnings.split(",")
+                        self.thresholds["warning"][metric] = {
+                            "avg10": float(warnings[0]),
+                            "avg60": float(warnings[1]),
+                            "avg300": float(warnings[2]),
+                            "total_share": float(warnings[3]),
+                        }
+                case "-c" | "--critical":
+                    all_metric_criticals = next(argv).split(";")
+                    for metric_criticals in all_metric_criticals:
+                        metric, criticals = metric_criticals.split(":")
+                        criticals = criticals.split(",")
+                        self.thresholds["critical"][metric] = {
+                            "avg10": float(criticals[0]),
+                            "avg60": float(criticals[1]),
+                            "avg300": float(criticals[2]),
+                            "total_share": float(criticals[3]),
+                        }
+
+
+class MetricPSI:
+    def __init__(self, config, psi_data, uptime_us):
+        self.config = config
+        self.psi_data = psi_data
+        self.uptime_us = uptime_us
+
+        for metric in self.psi_data.keys():
+            self.psi_data[metric]["total_share"] = (
+                self.psi_data[metric]["total"] / self.uptime_us * 100
+            )
+
+    def status(self):
+        states = {
+            "warning": {metric: False for metric in self.config.metric},
+            "critical": {metric: False for metric in self.config.metric},
+        }
+        data_str_by_metric = {}
+        perfdata_str = ""
+
+        for metric in self.config.metric:
+            data_str_by_metric[metric] = f"{metric} "
+            for key in SELECTED_KEYS:
+                if key in self.psi_data[metric]:
+                    data_str_by_metric[metric] += (
+                        f"{key}={self.psi_data[metric][key]:.2f}% "
+                    )
+                    perfdata_str += f"{metric}_{key}={self.psi_data[metric][key]:.2f}%;{self.config.thresholds['warning'][metric][key]:.2f};{self.config.thresholds['critical'][metric][key]:.2f};0;100 "
+                    for state in states.keys():
+                        if (
+                            self.psi_data[metric][key]
+                            > self.config.thresholds[state][metric][key]
+                        ):
+                            states[state][metric] = True
+
+        return_code = (
+            2
+            if any(states["critical"].values())
+            else 1
+            if any(states["warning"].values())
+            else 0
+        )
+
+        text_output = ""
+        for index, metric in enumerate(self.config.metric):
+            state = (
+                "CRITICAL"
+                if states["critical"][metric]
+                else "WARNING"
+                if states["warning"][metric]
+                else "OK"
+            )
+            terminaison = "\\n" if index + 1 < len(self.config.metric) else ""
+            text_output += f"{state} {data_str_by_metric[metric].strip()}{terminaison}"
+
+        return {
+            "return_code": return_code,
+            "text_output": text_output.strip(),
+            "perfdata": perfdata_str.strip(),
+        }
+
+
+def get_uptime_us():
+    with open("/proc/uptime", "r") as uptime_file:
+        return float(uptime_file.read().split()[0]) * 1000000
+
+
+def main(argv: [str, ...]):
+    config = MetricPSIConfig(argv)
+
+    match config.ressource:
+        case "cpu":
+            psi_path = "/proc/pressure/cpu"
+        case "io":
+            psi_path = "/proc/pressure/io"
+        case "memory":
+            psi_path = "/proc/pressure/memory"
+
+    psi_data = read_psi(psi_path)
+    uptime_us = get_uptime_us()
+    metric_psi = MetricPSI(config, psi_data, uptime_us)
+    output = metric_psi.status()
+    print(f"{output['text_output']} | {output['perfdata']}")
+    sys.exit(output["return_code"])
+
+
+if __name__ == "__main__":
+    main(sys.argv)