diff --git a/README.md b/README.md
index 974ffd74d831c475f38ed0ab01c655652994e4b2..a4e3e8adb4180c98b927af06435de1981cdb565e 100644
--- a/README.md
+++ b/README.md
@@ -71,7 +71,9 @@ Copy the `config-example.yaml` into `config.yaml` and edit it for matching your
 
     php exec/generate-keypair.php
 
-A cron job for key rotation has to be set up.
+Once a day rotate the keys using a cron job that calls:
+
+    php exec/rotate-keys.php
 
 ### Logs directory
 
diff --git a/classes/JWKSHandler.php b/classes/JWKSHandler.php
index b6cd3b04aa8f84516b815aec3a88be0c1cc95efe..aa287e93cc8b795ff63a4a6e6e43bb2f7631713e 100644
--- a/classes/JWKSHandler.php
+++ b/classes/JWKSHandler.php
@@ -76,6 +76,10 @@ class JWKSHandler {
         ];
     }
 
+    public function deleteKeyPair(RSAKeyPair $keyPair): void {
+        $this->locator->getJWKSDAO()->deleteKeyPair($keyPair->keyId);
+    }
+
     private function getTagContent(string $publicKeyXML, string $tagname): string {
         $matches = [];
         $pattern = "#<\s*?$tagname\b[^>]*>(.*?)</$tagname\b[^>]*>#s";
diff --git a/classes/datalayer/JWKSDAO.php b/classes/datalayer/JWKSDAO.php
index f3bd067dd5371b4e939c8c09d98ed9173171cd0f..8c2c215537330bbae2618a018af4f930b5735c56 100644
--- a/classes/datalayer/JWKSDAO.php
+++ b/classes/datalayer/JWKSDAO.php
@@ -17,4 +17,6 @@ interface JWKSDAO {
     public function insertRSAKeyPair(RSAKeyPair $keyPair): RSAKeyPair;
 
     public function getNewestKeyPair(): ?RSAKeyPair;
+
+    public function deleteKeyPair(string $id): void;
 }
diff --git a/classes/datalayer/mysql/MySQLJWKSDAO.php b/classes/datalayer/mysql/MySQLJWKSDAO.php
index 8c5f7d72381d61b04f74d452223d3cb10dbea44a..a48c20f7f0a45b94a0a30f30e4f26f0682251eba 100644
--- a/classes/datalayer/mysql/MySQLJWKSDAO.php
+++ b/classes/datalayer/mysql/MySQLJWKSDAO.php
@@ -21,7 +21,7 @@ class MySQLJWKSDAO extends BaseMySQLDAO implements JWKSDAO {
         $query = "INSERT INTO rsa_keypairs(id, private_key, public_key, alg, creation_time) VALUES (:id, :private_key, :public_key, :alg, :creation_time)";
 
         $now = time();
-        
+
         $stmt = $dbh->prepare($query);
         $stmt->bindParam(':id', $keyPair->keyId);
         $stmt->bindParam(':private_key', $keyPair->privateKey);
@@ -38,7 +38,7 @@ class MySQLJWKSDAO extends BaseMySQLDAO implements JWKSDAO {
 
         $dbh = $this->getDBHandler();
 
-        $query = "SELECT id, private_key, public_key, alg, creation_time FROM rsa_keypairs";
+        $query = "SELECT id, private_key, public_key, alg, creation_time FROM rsa_keypairs ORDER BY creation_time DESC";
 
         $stmt = $dbh->prepare($query);
         $stmt->execute();
@@ -94,4 +94,15 @@ class MySQLJWKSDAO extends BaseMySQLDAO implements JWKSDAO {
         return $keyPair;
     }
 
+    public function deleteKeyPair(string $id): void {
+
+        $dbh = $this->getDBHandler();
+
+        $query = "DELETE FROM rsa_keypairs WHERE id = :id";
+
+        $stmt = $dbh->prepare($query);
+        $stmt->bindParam(':id', $id);
+        $stmt->execute();
+    }
+
 }
diff --git a/exec/rotate-keys.php b/exec/rotate-keys.php
new file mode 100644
index 0000000000000000000000000000000000000000..8a98b852f78b36e56369b7f045fdda94e76e7d4c
--- /dev/null
+++ b/exec/rotate-keys.php
@@ -0,0 +1,17 @@
+<?php
+
+chdir(dirname(__FILE__));
+
+include '../include/init.php';
+
+$handler = new \RAP\JWKSHandler($locator);
+$handler->generateKeyPair();
+
+$dao = $locator->getJWKSDAO();
+
+$keyPairs = $dao->getRSAKeyPairs();
+
+if (count($keyPairs) > 3) {
+    // delete oldest keypair
+    $handler->deleteKeyPair($keyPairs[count($keyPairs) - 1]);
+}