diff --git a/gso/schedules/__init__.py b/gso/schedules/__init__.py
index 8257a874c6c090c1d284966d2ff4f9064d04e831..f3100cf84e60ed34dff2fdf16362b834fe171e33 100644
--- a/gso/schedules/__init__.py
+++ b/gso/schedules/__init__.py
@@ -1 +1,9 @@
 """Tasks that are scheduled to run periodically in :term:`GSO`."""
+
+from orchestrator.schedules import SchedulingFunction  # type:ignore[attr-defined]
+
+from gso.schedules.validate_routers_nightly import run_validate_routers
+
+#  TODO: This list overwrites the default, and therefore leaves all default schedules unused.
+#  TODO: Consider using the default schedules.
+ALL_SCHEDULERS: list[SchedulingFunction] = [run_validate_routers]
diff --git a/gso/schedules/validate_routers_nightly.py b/gso/schedules/validate_routers_nightly.py
new file mode 100644
index 0000000000000000000000000000000000000000..9b60df42dd67c8d7b6a14e0004eb36a5b5a2e456
--- /dev/null
+++ b/gso/schedules/validate_routers_nightly.py
@@ -0,0 +1,21 @@
+"""Nightly schedule for validating all active Routers' configuration."""
+
+import logging
+
+from orchestrator.schedules.scheduling import scheduler
+from orchestrator.services.processes import start_process
+
+from gso.services.subscriptions import get_active_router_subscriptions
+
+logger = logging.getLogger(__name__)
+
+
+@scheduler(name="Validate routers", time_unit="day", at="03:00")
+def run_validate_routers() -> None:
+    """Validate configuration on all active Routers, every night at 3AM."""
+    routers = get_active_router_subscriptions(includes=["subscription_id"])
+
+    for router in routers:
+        msg = f"Validating configuration of router subscription {router['subscription_id']}"
+        logger.info(msg)
+        start_process("validate_router", [{"subscription_id": router["subscription_id"]}])
diff --git a/gso/workflows/tasks/validate_router.py b/gso/workflows/tasks/validate_router.py
index 18ab6e3e73564efed9bb90997ad5a3bdd13eeac3..465e4c33ec0ee75606b2b887823845a4fed26b6c 100644
--- a/gso/workflows/tasks/validate_router.py
+++ b/gso/workflows/tasks/validate_router.py
@@ -1,29 +1,36 @@
+"""Router validation workflow. Used in a nightly schedule."""
+
 import json
 
 from orchestrator.targets import Target
-from orchestrator.types import State
 from orchestrator.utils.json import json_dumps
-from orchestrator.workflow import StepList, init, workflow, done, step
-from orchestrator.workflows.steps import store_process_subscription, unsync, resync
+from orchestrator.workflow import StepList, done, init, step, workflow
+from orchestrator.workflows.steps import resync, store_process_subscription, unsync
+from orchestrator.workflows.utils import wrap_modify_initial_input_form
+from products import Router
 
-from gso.services.provisioning_proxy import pp_interaction, execute_playbook
+from gso.services.provisioning_proxy import execute_playbook, pp_interaction
 from gso.workflows.router.create_router import verify_ipam_loopback
-from products import Router
 
 
 @step("Validate router configuration")
 def validate_router_config(subscription: Router, callback_route: str) -> None:
+    """Run an Ansible playbook that validates the configuration that is present on an active Router."""
     extra_vars = {"wfo_router_json": json.loads(json_dumps(subscription)), "verb": "validate"}
 
     execute_playbook(
         playbook_name="base_config.yaml",
         callback_route=callback_route,
         inventory=subscription.router.router_fqdn,
-        extra_vars=extra_vars
+        extra_vars=extra_vars,
     )
 
 
-@workflow("Validate router configuration", target=Target.SYSTEM)
+@workflow(
+    "Validate router configuration",
+    target=Target.SYSTEM,
+    initial_input_form=wrap_modify_initial_input_form(None),
+)
 def validate_router() -> StepList:
     """Validate an existing, active Router subscription.