diff --git a/lib/jqueue.py b/lib/jqueue.py
index ed81ff784e10709f3565ca2ba4558ff365377441..4d31b8390d1418d9409a16c1b5680aa02c357736 100644
--- a/lib/jqueue.py
+++ b/lib/jqueue.py
@@ -292,6 +292,14 @@ class JobStorageBase(object):
 
     return "%s%010d" % (prefix, job_id)
 
+  def _ShouldJobBeArchivedUnlocked(self, job):
+    if job.GetStatus() not in (constants.JOB_STATUS_CANCELED,
+                               constants.JOB_STATUS_SUCCESS,
+                               constants.JOB_STATUS_ERROR):
+      logging.debug("Job %s is not yet done", job.id)
+      return False
+    return True
+
 
 class DiskJobStorage(JobStorageBase):
   _RE_JOB_FILE = re.compile(r"^job-(%s)$" % constants.JOB_ID_TEMPLATE)
@@ -538,8 +546,35 @@ class DiskJobStorage(JobStorageBase):
   def UpdateJob(self, job):
     return self._UpdateJobUnlocked(job)
 
+  @utils.LockedMethod
   def ArchiveJob(self, job_id):
-    raise NotImplementedError()
+    """Archives a job.
+
+    @type job_id: string
+    @param job_id: Job ID of job to be archived.
+
+    """
+    logging.debug("Archiving job %s", job_id)
+
+    job = self._LoadJobUnlocked(job_id)
+    if not job:
+      logging.debug("Job %s not found", job_id)
+      return
+
+    if not self._ShouldJobBeArchivedUnlocked(job):
+      return
+
+    try:
+      old = self._GetJobPath(job.id)
+      new = self._GetArchivedJobPath(job.id)
+
+      os.rename(old, new)
+
+      logging.debug("Successfully archived job %s", job.id)
+    finally:
+      # Cleaning the cache because we don't know what os.rename actually did
+      # and to be on the safe side.
+      self._CleanCacheUnlocked([])
 
 
 class JobQueue:
@@ -582,7 +617,7 @@ class JobQueue:
     return job.id
 
   def ArchiveJob(self, job_id):
-    raise NotImplementedError()
+    self._jobs.ArchiveJob(job_id)
 
   def CancelJob(self, job_id):
     raise NotImplementedError()