diff --git a/gso/auth/oidc_policy_helper.py b/gso/auth/oidc_policy_helper.py
index eca66512a3e6c55cddbc1fd117dee147e1cb62a5..51b6bf01a4d88e65aca6209557d6176c486228c9 100644
--- a/gso/auth/oidc_policy_helper.py
+++ b/gso/auth/oidc_policy_helper.py
@@ -364,7 +364,7 @@ def _evaluate_decision(decision: OPAResult, *, auto_error: bool, **context: dict
 
     raise HTTPException(
         status_code=HTTPStatus.FORBIDDEN,
-        detail=f"User is not allowed to access resource: {context.get('resource')} Decision was taken with id: {did}",
+        detail=f"User is not allowed to access resource: {context.get("resource")} Decision was taken with id: {did}",
     )
 
 
diff --git a/gso/cli/imports.py b/gso/cli/imports.py
index 0a41ac740ccd19cb64a1596994da5ec672eae4e3..3a6dcf1c814aecb96a2ecc1a406a78fd9fda9f9a 100644
--- a/gso/cli/imports.py
+++ b/gso/cli/imports.py
@@ -307,14 +307,14 @@ def import_iptrunks(filepath: str = common_filepath_option) -> None:
             iptrunk_ipv4_network = ipv4_network_a
         else:
             # Handle the case where IPv4 networks are different
-            typer.echo(f"Error: IPv4 networks are different for trunk {trunk['id']}.")
+            typer.echo(f"Error: IPv4 networks are different for trunk {trunk["id"]}.")
             continue
         # Check if IPv6 networks are the same
         if ipv6_network_a == ipv6_network_b:
             iptrunk_ipv6_network = ipv6_network_a
         else:
             # Handle the case where IPv6 networks are different
-            typer.echo(f"Error: IPv6 networks are different for trunk {trunk['id']}.")
+            typer.echo(f"Error: IPv6 networks are different for trunk {trunk["id"]}.")
             continue
 
         typer.echo(
@@ -343,7 +343,7 @@ def import_iptrunks(filepath: str = common_filepath_option) -> None:
             )
             start_process("create_imported_iptrunk", [initial_data.model_dump()])
             successfully_imported_data.append(trunk["id"])
-            typer.echo(f"Successfully imported IP Trunk: {trunk['id']}")
+            typer.echo(f"Successfully imported IP Trunk: {trunk["id"]}")
         except ValidationError as e:
             typer.echo(f"Validation error: {e}")
 
diff --git a/gso/services/librenms_client.py b/gso/services/librenms_client.py
index e4564b832482346569e29bae3fa3d1dc6f06d686..749974f103fcfe5a40f458c6d3aa7e1e82e5dfb9 100644
--- a/gso/services/librenms_client.py
+++ b/gso/services/librenms_client.py
@@ -26,7 +26,7 @@ class LibreNMSClient:
         self.snmp_config = config.SNMP
 
         self.headers = {
-            "User-Agent": f"geant-service-orchestrator/{metadata.version('geant-service-orchestrator')}",
+            "User-Agent": f"geant-service-orchestrator/{metadata.version("geant-service-orchestrator")}",
             "Accept": "application/json",
             "Content-Type": "application/json",
             "X-Auth-Token": token,
diff --git a/gso/utils/helpers.py b/gso/utils/helpers.py
index d27e7c6d83f8c3888bd9d34dcae6f1848a4ce3f6..edd01009e7eb139a0b8481a875fb3ad9879110bc 100644
--- a/gso/utils/helpers.py
+++ b/gso/utils/helpers.py
@@ -47,7 +47,7 @@ def available_interfaces_choices(router_id: UUID, speed: str) -> Choice | None:
     if get_router_vendor(router_id) != Vendor.NOKIA:
         return None
     interfaces = {
-        interface["name"]: f"{interface['name']}  {interface['description']}"
+        interface["name"]: f"{interface["name"]}  {interface["description"]}"
         for interface in NetboxClient().get_available_interfaces(router_id, speed)
     }
     return Choice("ae member", zip(interfaces.keys(), interfaces.items(), strict=True))  # type: ignore[arg-type]
@@ -77,7 +77,7 @@ def available_interfaces_choices_including_current_members(
         ],
     )
     options = {
-        interface["name"]: f"{interface['name']}  {interface['description']}" for interface in available_interfaces
+        interface["name"]: f"{interface["name"]}  {interface["description"]}" for interface in available_interfaces
     }
     return Choice("ae member", zip(options.keys(), options.items(), strict=True))  # type: ignore[arg-type]
 
diff --git a/pyproject.toml b/pyproject.toml
index 376371b7d41c286cd416a176fc760ce25cd1a663..e9979242b773ca0eb5812755a4a240733cca96fa 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -31,6 +31,10 @@ extend-exclude = [
     "gso/migrations",
     "docs",
 ]
+target-version = "py312"
+line-length = 120
+
+[tool.ruff.lint]
 ignore = [
     "COM812",
     "D203",
@@ -41,7 +45,6 @@ ignore = [
     "PLR0904",
     "PLW1514",
 ]
-line-length = 120
 select = [
     "A",
     "ARG",
@@ -92,16 +95,16 @@ select = [
     "W",
     "YTT"
 ]
-target-version = "py311"
 
-[tool.ruff.flake8-tidy-imports]
+[tool.ruff.lint.flake8-tidy-imports]
 ban-relative-imports = "all"
 
-[tool.ruff.per-file-ignores]
-"test/*" = ["ARG001", "D", "S101", "PLR2004"]
+[tool.ruff.lint.per-file-ignores]
+"gso/workflows/*" = ["PLR0917", "PLR0914"]
+"test/*" = ["ARG001", "D", "S101", "PLR2004", "PLR0917", "PLR0914", "PLC0415", "PLC2701"]
 "setup.py" = ["D100"]
 
-[tool.ruff.isort]
+[tool.ruff.lint.isort]
 known-third-party = ["pydantic", "migrations"]
 known-first-party = ["test", "docs"]
 
@@ -113,6 +116,3 @@ filterwarnings = [
     "ignore",
     "default:::gso",
 ]
-[tool.ruff.lint.per-file-ignores]
-"test/*" = ["PLR0917", "S101", "D104", "D105", "D103", "D100", "ARG001", "D102", "PLR2004", "D101", "D106", "D107", "PLR0914", "PLC0415", "PLC2701"]
-"gso/workflows/*" = ["PLR0917", "PLR0914"]
\ No newline at end of file
diff --git a/test/workflows/iptrunk/test_create_iptrunk.py b/test/workflows/iptrunk/test_create_iptrunk.py
index ea82a5bf7254dd535201ecadf08a5b0e91af6091..de15a7124e0311e4a3127ccc10477fd39a4a02b7 100644
--- a/test/workflows/iptrunk/test_create_iptrunk.py
+++ b/test/workflows/iptrunk/test_create_iptrunk.py
@@ -143,7 +143,7 @@ def test_successful_iptrunk_creation_with_standard_lso_result(
     ])
     assert subscription.status == "provisioning"
     assert subscription.description == (
-        f"IP trunk {sorted_sides[0]} {sorted_sides[1]}, geant_s_sid:{input_form_wizard_data[0]['geant_s_sid']}"
+        f"IP trunk {sorted_sides[0]} {sorted_sides[1]}, geant_s_sid:{input_form_wizard_data[0]["geant_s_sid"]}"
     )
 
     assert mock_execute_playbook.call_count == 6
diff --git a/test/workflows/router/test_create_router.py b/test/workflows/router/test_create_router.py
index 5a96dbc702002b80890e7c059c4d32ef70b0b067..e6cc65ba6604ebc3268f062aa5d0fd613eefdf13 100644
--- a/test/workflows/router/test_create_router.py
+++ b/test/workflows/router/test_create_router.py
@@ -59,7 +59,7 @@ def test_create_nokia_router_success(
     mock_v4 = faker.ipv4()
     mock_v6 = faker.ipv6()
     mock_fqdn = (
-        f"{router_creation_input_form_data['hostname']}.{mock_site.site_name.lower()}."
+        f"{router_creation_input_form_data["hostname"]}.{mock_site.site_name.lower()}."
         f"{mock_site.site_country_code.lower()}.geant.net"
     )
     mock_hostname_available.return_value = True
@@ -143,7 +143,7 @@ def test_create_nokia_router_lso_failure(
     mock_v4_net = faker.ipv4(network=True)
     mock_v6 = faker.ipv6()
     mock_fqdn = (
-        f"{router_creation_input_form_data['hostname']}.{mock_site.site_name.lower()}."
+        f"{router_creation_input_form_data["hostname"]}.{mock_site.site_name.lower()}."
         f"{mock_site.site_country_code.lower()}.geant.net"
     )
     mock_hostname_available.return_value = True
diff --git a/test/workflows/site/test_create_site.py b/test/workflows/site/test_create_site.py
index f6c196da217320506521e9d993b7e7609f8e05db..7642debea0c2739b1a5845db4404f35c5205884a 100644
--- a/test/workflows/site/test_create_site.py
+++ b/test/workflows/site/test_create_site.py
@@ -35,7 +35,7 @@ def test_create_site(responses, faker):
     assert subscription.status == "active"
     assert (
         subscription.description
-        == f"Site in {initial_site_data[1]['site_city']}, {initial_site_data[1]['site_country']}"
+        == f"Site in {initial_site_data[1]["site_city"]}, {initial_site_data[1]["site_country"]}"
     )