diff --git a/advancedqueue.info b/advancedqueue.info
index ace3a37..63853e2 100644
--- a/advancedqueue.info
+++ b/advancedqueue.info
@@ -2,6 +2,7 @@ name = Advanced Queues
 description = Helper module for advanced queuing.
 package = Other
 core = 7.x
+configure = admin/config/system/advancedqueue
 
 files[] = advancedqueue.queue.inc
 
diff --git a/advancedqueue.module b/advancedqueue.module
index 0249c2a..9e2fc2e 100644
--- a/advancedqueue.module
+++ b/advancedqueue.module
@@ -48,9 +48,31 @@ function advancedqueue_entity_info() {
 }
 
 /**
+ * Implements hook_menu().
+ */
+function advancedqueue_menu() {
+  $items = array();
+
+  $items['admin/config/system/advancedqueue'] = array(
+    'title' => 'Advanced Queue',
+    'description' => 'Configure the Advanced Queue table clean up settings.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('advancedqueue_settings_form'),
+    'access arguments' => array('administer site configuration'),
+    'type' => MENU_NORMAL_ITEM,
+    'file' => 'advancedqueue.admin.inc',
+  );
+
+  return $items;
+}
+
+/**
  * Implements hook_cron().
  */
 function advancedqueue_cron() {
+  // Delete older entries and make sure there are no stale items in the table.
+  _advancedqueue_cleanup_table();
+
   if (!variable_get('advancedqueue_use_cron', FALSE)) {
     return;
   }
@@ -213,3 +235,83 @@ function advancedqueue_views_api() {
     'path' => drupal_get_path('module', 'advancedqueue') . '/views',
   );
 }
+
+/**
+ * Helper function to clean the advancedqueue table.
+ */
+function _advancedqueue_cleanup_table() {
+  _advancedqueue_purge_old_processed_items();
+  _advancedqueue_release_stale_items();
+}
+
+/**
+ * Helper function to remove data we don't need anymore.
+ *
+ * Removes old entries of processed items.
+ */
+function _advancedqueue_purge_old_processed_items() {
+  // The number of processed items we want to keep.
+  $row_limit = variable_get('advancedqueue_threshold', 0);
+
+  if (!$row_limit) {
+    // No limit means we don't remove old entries.
+    return;
+  }
+
+  // Item status we want to clean.
+  $statuses = array(
+    ADVANCEDQUEUE_STATUS_SUCCESS,
+    ADVANCEDQUEUE_STATUS_FAILURE,
+  );
+
+  // Find the row after which we consider items old enough to purge.
+  $min_row = db_select('advancedqueue', 'a')
+    ->fields('a', array('item_id'))
+    ->condition('status', $statuses, 'IN')
+    ->orderBy('item_id', 'DESC')
+    ->range($row_limit - 1, 1)
+    ->execute()->fetchField();
+
+  // Remove all rows above the limit.
+  if ($min_row) {
+    db_delete('advancedqueue')
+      ->condition('item_id', $min_row, '<')
+      ->condition('status', $statuses, 'IN')
+      ->execute();
+  }
+}
+
+/**
+ * Helper function to release stale items.
+ *
+ * Requeues long expired entries that are in processing state.
+ * Items can be stuck in the ADVANCEDQUEUE_STATUS_PROCESSING state
+ * if the PHP process crashes or is killed while processing an item.
+ */
+function _advancedqueue_release_stale_items() {
+  $timeout = variable_get('advancedqueue_release_timeout', 0);
+
+  if (!$timeout) {
+    // No timeout means we don't touch stale items.
+    return;
+  }
+
+  $before = REQUEST_TIME - $timeout;
+
+  $items = db_select('advancedqueue', 'a')
+    ->fields('a', array('item_id', 'name'))
+    ->condition('status', ADVANCEDQUEUE_STATUS_PROCESSING)
+    ->condition('expire', $before, '<=')
+    ->orderBy('name')
+    ->execute();
+
+  $queues = array();
+
+  // Releasing stale items to put them back in queued status.
+  foreach ($items as $item) {
+    // DrupalQueue::get() statically caches queues objects,
+    // we wouldn't improve performance by grouping items by queue.
+    $queue = DrupalQueue::get($item->name);
+    $queue->releaseItem($item);
+  }
+}
diff --git a/drush/advancedqueue.drush.inc b/drush/advancedqueue.drush.inc
index 8e24a28..584f0cd 100644
--- a/drush/advancedqueue.drush.inc
+++ b/drush/advancedqueue.drush.inc
@@ -59,6 +59,10 @@ function drush_advancedqueue_process_queue($queue = NULL) {
     $queues = array_intersect_key($queues_info, $queues);
   }
 
+  // Delete older entries and make sure there are no stale items in the table.
+  drush_log(dt('Cleanup processed and locked items.'));
+  _advancedqueue_cleanup_table();
+
   // Run the worker for a certain period of time before killing it.
   $timeout = drush_get_option('timeout');
   $end = $timeout ? time() + $timeout : 0;
