diff --git a/sql/updates/23.sql b/sql/updates/23.sql
new file mode 100644
--- /dev/null
+++ b/sql/updates/23.sql
@@ -0,0 +1,71 @@
+insert into dbversion (version, release, description)
+       values (23, now(), 'Work In Progress');
+
+create or replace function swh_scheduler_update_task_on_task_end ()
+  returns trigger
+  language plpgsql
+as $$
+declare
+  cur_task task%rowtype;
+  cur_task_type task_type%rowtype;
+  adjustment_factor float;
+  new_interval interval;
+begin
+  select * from task where id = new.task into cur_task;
+  select * from task_type where type = cur_task.type into cur_task_type;
+
+  case
+    when new.status = 'permfailed' then
+      update task
+        set status = 'disabled'
+        where id = cur_task.id;
+    when new.status in ('eventful', 'uneventful') then
+      case
+        when cur_task.policy = 'oneshot' then
+          update task
+            set status = 'completed'
+            where id = cur_task.id;
+        when cur_task.policy = 'recurring' then
+          if new.status = 'uneventful' then
+            adjustment_factor := 1/cur_task_type.backoff_factor;
+          else
+            adjustment_factor := 1/cur_task_type.backoff_factor;
+          end if;
+          new_interval := greatest(
+            cur_task_type.min_interval,
+            least(
+              cur_task_type.max_interval,
+              adjustment_factor * cur_task.current_interval));
+          update task
+            set status = 'next_run_not_scheduled',
+                next_run = new.ended + new_interval,
+                current_interval = new_interval,
+                retries_left = coalesce(cur_task_type.num_retries, 0)
+            where id = cur_task.id;
+      end case;
+    else -- new.status in 'failed', 'lost'
+      if cur_task.retries_left > 0 then
+        update task
+          set status = 'next_run_not_scheduled',
+              next_run = new.ended + coalesce(cur_task_type.retry_delay, interval '1 hour'),
+              retries_left = cur_task.retries_left - 1
+          where id = cur_task.id;
+      else -- no retries left
+        case
+          when cur_task.policy = 'oneshot' then
+            update task
+              set status = 'disabled'
+              where id = cur_task.id;
+          when cur_task.policy = 'recurring' then
+            update task
+              set status = 'next_run_not_scheduled',
+                  next_run = new.ended + cur_task.current_interval,
+                  retries_left = coalesce(cur_task_type.num_retries, 0)
+              where id = cur_task.id;
+        end case;
+      end if; -- retries
+  end case;
+  return null;
+end;
+$$;
+
diff --git a/swh/scheduler/sql/30-schema.sql b/swh/scheduler/sql/30-schema.sql
--- a/swh/scheduler/sql/30-schema.sql
+++ b/swh/scheduler/sql/30-schema.sql
@@ -11,7 +11,7 @@
 comment on column dbversion.description is 'Version description';
 
 insert into dbversion (version, release, description)
-       values (22, now(), 'Work In Progress');
+       values (23, now(), 'Work In Progress');
 
 create table task_type (
   type text primary key,
diff --git a/swh/scheduler/sql/40-func.sql b/swh/scheduler/sql/40-func.sql
--- a/swh/scheduler/sql/40-func.sql
+++ b/swh/scheduler/sql/40-func.sql
@@ -370,7 +370,7 @@
               adjustment_factor * cur_task.current_interval));
           update task
             set status = 'next_run_not_scheduled',
-                next_run = now() + new_interval,
+                next_run = new.ended + new_interval,
                 current_interval = new_interval,
                 retries_left = coalesce(cur_task_type.num_retries, 0)
             where id = cur_task.id;
@@ -379,7 +379,7 @@
       if cur_task.retries_left > 0 then
         update task
           set status = 'next_run_not_scheduled',
-              next_run = now() + coalesce(cur_task_type.retry_delay, interval '1 hour'),
+              next_run = new.ended + coalesce(cur_task_type.retry_delay, interval '1 hour'),
               retries_left = cur_task.retries_left - 1
           where id = cur_task.id;
       else -- no retries left
@@ -391,7 +391,7 @@
           when cur_task.policy = 'recurring' then
             update task
               set status = 'next_run_not_scheduled',
-                  next_run = now() + cur_task.current_interval,
+                  next_run = new.ended + cur_task.current_interval,
                   retries_left = coalesce(cur_task_type.num_retries, 0)
               where id = cur_task.id;
         end case;