diff --git a/web/app/jobs/tasks.py b/web/app/jobs/tasks.py
index 58e66d83be7749ac92299ee8d543bfa34edfaea6..e17bf5b413eb04c9a499a5ebf54aa8ac37cd7cfe 100644
--- a/web/app/jobs/tasks.py
+++ b/web/app/jobs/tasks.py
@@ -1,5 +1,5 @@
 from time import sleep
-from .. import db
+from .. import db, logger
 from ..decorators import background
 from ..models import Job
 import os
@@ -27,3 +27,24 @@ def delete_job(job_id, *args, **kwargs):
                             'jobs', str(job.id))
         shutil.rmtree(path, ignore_errors=True)
         job.delete()
+
+
+@background
+def restart_job(job_id, *args, **kwargs):
+    app = kwargs['app']
+    with app.app_context():
+        job = Job.query.get(job_id)
+        if job is None:
+            logger.warning('Job not found')
+            return
+        if job.status != 'failed':
+            logger.warning('Job not failed')
+            return
+        logger.warning('Restarted')
+        job_dir = os.path.join(app.config['NOPAQUE_STORAGE'],
+                               str(job.user_id),
+                               'jobs',
+                               str(job.id))
+        shutil.rmtree(os.path.join(job_dir, 'output'), ignore_errors=True)
+        shutil.rmtree(os.path.join(job_dir, 'pyflow.data'), ignore_errors=True)
+        job.restart()
diff --git a/web/app/jobs/views.py b/web/app/jobs/views.py
index f8f9f6a9212673375a84ecde38350d65d1b04941..16b6756d0acdff6deb5727e77d666db2a28d7ecc 100644
--- a/web/app/jobs/views.py
+++ b/web/app/jobs/views.py
@@ -3,6 +3,7 @@ from flask import (abort, current_app, flash, redirect, render_template,
 from flask_login import current_user, login_required
 from . import jobs
 from . import tasks
+from ..decorators import admin_required
 from ..models import Job, JobInput, JobResult
 import os
 
@@ -49,6 +50,23 @@ def download_job_input(job_id, job_input_id):
                                filename=job_input.filename)
 
 
+@jobs.route('/<int:job_id>/restart')
+@login_required
+@admin_required
+def restart(job_id):
+    job = Job.query.get_or_404(job_id)
+    if job.status != 'failed':
+        flash('Job can not be restarted!', 'job')
+    else:
+        tasks.restart_job(job_id)
+        flash('Job has been restarted!', 'job')
+    job_inputs = [dict(filename=input.filename,
+                       id=input.id,
+                       job_id=job.id)
+                  for input in job.inputs]
+    return redirect(url_for('jobs.job', job_id=job_id))
+
+
 @jobs.route('/<int:job_id>/results/<int:job_result_id>/download')
 @login_required
 def download_job_result(job_id, job_result_id):
diff --git a/web/app/models.py b/web/app/models.py
index ba165c3f83ea32c61681d19eb0993013a6027acd..e2cf09fb68d73dc579f06fca71ba49d561ac4b96 100644
--- a/web/app/models.py
+++ b/web/app/models.py
@@ -368,6 +368,17 @@ class Job(db.Model):
         db.session.delete(self)
         db.session.commit()
 
+    def restart(self):
+        '''
+        Restart a job - only if the status is failed
+        '''
+
+        if self.status != 'failed':
+            return
+        self.end_date = None
+        self.status = 'submitted'
+        db.session.commit()
+
     def to_dict(self):
         return {'id': self.id,
                 'user_id': self.user_id,
@@ -534,7 +545,7 @@ class Corpus(db.Model):
         return '<Corpus {corpus_title}>'.format(corpus_title=self.title)
 
 
-class Result (db.Model):
+class Result(db.Model):
     '''
     Class to define a result set of one query.
     '''