diff --git a/src/api/ponos.ts b/src/api/ponos.ts
index 4e916462a4bd964f87b3f35ea88e362d750a310c..08dc6d6b0a5de6037dde9275e6f941b570bdc9d2 100644
--- a/src/api/ponos.ts
+++ b/src/api/ponos.ts
@@ -10,6 +10,8 @@ type TaskUpdateResponse = Pick<Task, 'id' | 'state'>
 
 export const updateTask = async (id: UUID, payload: TaskUpdatePayload): Promise<TaskUpdateResponse> => (await axios.patch(`/task/${id}/`, payload)).data
 
+export const restartTask = async (id: UUID): Promise<Task> => (await axios.post(`/task/${id}/restart/`)).data
+
 export const listArtifacts = unique(async (id: UUID): Promise<Artifact[]> => (await axios.get(`/task/${id}/artifacts/`)).data)
 
 // List Ponos agents with their status
diff --git a/src/components/Process/Status/Task.vue b/src/components/Process/Status/Task.vue
index 8238f1e6ca4c0927e19a5f7b98e7208f461a1fb6..ebb465932cfd614f04e36610158caca339cba495 100644
--- a/src/components/Process/Status/Task.vue
+++ b/src/components/Process/Status/Task.vue
@@ -14,20 +14,23 @@
             </a>
           </div>
           <Artifacts :task="task" />
-          <div v-if="isAdmin" class="control">
+          <div class="control">
             <button
-              v-if="['completed', 'failed', 'error', 'stopped'].includes(task.state)"
-              v-on:click="updateTask('pending')"
-              class="button is-small is-warning"
+              v-if="finishedTask"
+              v-on:click="restartModal = finishedTask"
+              class="button is-small"
             >
-              Restart task
+              Restart
             </button>
+          </div>
+          <div class="control">
             <button
-              v-if="task.state === 'running'"
-              v-on:click="updateTask('stopping')"
+              :disabled="task.state !== 'running' || undefined"
+              :title="task.state === 'running' ? 'Force stop this task' : 'Task must be running to be stopped'"
+              v-on:click="task.state === 'running' && updateTask('stopping')"
               class="button is-small is-danger"
             >
-              Stop task
+              Stop
             </button>
           </div>
         </div>
@@ -46,20 +49,46 @@
       <Logs :logs="task.logs" />
     </div>
   </div>
+  <Modal v-model="restartModal" :title="`Restart task ${task.slug}`">
+    <span>Restarting the task will cause a clone of the task to be started in place of the current one.</span>
+    <br />
+    <span>Are you sure you want to proceed?</span>
+    <template v-slot:footer="{ close }">
+      <button class="button ml-auto" v-on:click="close">Cancel</button>
+      <button
+        class="button is-primary"
+        :class="{ 'is-loading': restartLoading }"
+        v-on:click="restart"
+      >
+        Restart
+      </button>
+    </template>
+  </Modal>
 </template>
 
 <script>
-import { mapActions, mapGetters, mapMutations } from 'vuex'
+import { mapActions } from 'pinia'
+import {
+  mapActions as mapVuexActions,
+  mapGetters as mapVuexGetters,
+  mapMutations as mapVuexMutations
+} from 'vuex'
+
+import { PROCESS_FINAL_STATES } from '@/config'
+import { errorParser } from '@/helpers'
+import { useNotificationStore } from '@/stores'
 
 import Artifacts from './Artifacts.vue'
 import Logs from './Logs.vue'
 import StateTag from '../StateTag.vue'
+import Modal from '@/components/Modal.vue'
 
 export default {
   components: {
     Artifacts,
     Logs,
-    StateTag
+    StateTag,
+    Modal
   },
   props: {
     task: {
@@ -67,6 +96,10 @@ export default {
       required: true
     }
   },
+  data: () => ({
+    restartModal: false,
+    restartLoading: false
+  }),
   mounted () {
     this.startTaskPolling(this.task.id)
   },
@@ -74,18 +107,35 @@ export default {
     this.stopTaskPolling(this.task.id)
   },
   computed: {
-    ...mapGetters('auth', ['isAdmin'])
-  },
-  watch: {
-    task (newValue, oldValue) {
-      if (newValue && newValue.id && (!oldValue || oldValue.id !== newValue.id)) this.startTaskPolling(newValue.id)
+    ...mapVuexGetters('auth', ['isAdmin']),
+    finishedTask () {
+      return PROCESS_FINAL_STATES.includes(this.task.state)
     }
   },
   methods: {
-    ...mapActions('process', ['startTaskPolling']),
-    ...mapMutations('process', ['stopTaskPolling']),
+    ...mapActions(useNotificationStore, ['notify']),
+    ...mapVuexActions('process', ['startTaskPolling', 'restartTask']),
+    ...mapVuexMutations('process', ['stopTaskPolling']),
     updateTask (state) {
       this.$store.dispatch('process/updateTask', { id: this.task.id, state })
+    },
+    async restart () {
+      if (this.restartLoading) return
+      this.restartLoading = true
+      try {
+        await this.restartTask(this.task.id)
+        this.restartModal = false
+        this.notify({ type: 'success', text: 'Task has been restarted' })
+      } catch (err) {
+        this.notify({ type: 'error', text: errorParser(err) })
+      } finally {
+        this.restartLoading = false
+      }
+    }
+  },
+  watch: {
+    task (newValue, oldValue) {
+      if (newValue && newValue.id && (!oldValue || oldValue.id !== newValue.id)) this.startTaskPolling(newValue.id)
     }
   }
 }
diff --git a/src/store/process.js b/src/store/process.js
index fbeb67fc308f407be9480292e002a2e1e45b12ac..19c63839d67dd8fa8ae46c8f3db24996d6f65d4b 100644
--- a/src/store/process.js
+++ b/src/store/process.js
@@ -407,6 +407,15 @@ export const actions = {
     }
   },
 
+  async restartTask ({ state, commit }, id) {
+    const processId = state.tasks[id]?.process_id
+    const newTask = await api.restartTask(id)
+    commit('setTask', {
+      ...newTask,
+      process_id: processId
+    })
+  },
+
   async getTaskArtifacts ({ commit }, id) {
     try {
       const data = await api.listArtifacts(id)