diff --git a/layerindex/tasks.py b/layerindex/tasks.py
index 4af9466..b6aa0ae 100644
--- a/layerindex/tasks.py
+++ b/layerindex/tasks.py
@@ -42,7 +42,9 @@ def run_update_command(self, branch_name, update_command):
     from layerindex.models import Update
     updateobj = Update.objects.get(task_id=self.request.id)
     updateobj.started = datetime.now()
-    updateobj.save()
+    # Save only the fields set here: the update command may write to the
+    # same record (update.py --update)
+    updateobj.save(update_fields=['started'])
     output = ''
     shell = False
     if isinstance(update_command, str):
@@ -71,5 +73,5 @@ def run_update_command(self, branch_name, update_command):
         updateobj.log = output
         updateobj.finished = datetime.now()
         updateobj.retcode = retcode
-        updateobj.save()
+        updateobj.save(update_fields=['log', 'finished', 'retcode'])
     return {'retcode': retcode, 'output': erroutput}
diff --git a/layerindex/update.py b/layerindex/update.py
index ca68e3e..8a76084 100755
--- a/layerindex/update.py
+++ b/layerindex/update.py
@@ -183,6 +183,9 @@ def main():
     parser.add_option("-q", "--quiet",
             help = "Hide all output except error messages",
             action="store_const", const=logging.ERROR, dest="loglevel")
+    parser.add_option("-u", "--update",
+            help = "Record the update in this existing update record instead of creating a new one",
+            type="int", action="store", dest="update_id")
     parser.add_option("", "--keep-temp",
             help = "Preserve temporary directory at the end instead of deleting it",
             action="store_true")
@@ -267,14 +270,23 @@ def main():
     listhandler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
     logger.addHandler(listhandler)
 
-    update = Update()
-    update.started = datetime.now()
+    if options.update_id:
+        update = Update.objects.filter(id=options.update_id).first()
+        if not update:
+            logger.error("Specified update id %s does not exist in database" % options.update_id)
+            sys.exit(1)
+    else:
+        update = Update()
+        update.started = datetime.now()
     if options.fullreload or options.reload:
         update.reload = True
     else:
         update.reload = False
     if not options.dryrun:
-        update.save()
+        if options.update_id:
+            update.save(update_fields=['reload'])
+        else:
+            update.save()
     try:
         lockfn = os.path.join(fetchdir, "layerindex.lock")
         lockfile = utils.lock_file(lockfn, options.timeout, logger)
@@ -611,10 +623,14 @@ def main():
         logger.error(traceback.format_exc().rstrip())
         sys.exit(1)
     finally:
-        update.log = ''.join(listhandler.read())
-        update.finished = datetime.now()
-        if not options.dryrun:
-            update.save()
+        # With --update, whatever runs this command (e.g. the
+        # run_update_command task) records the log and when it finished,
+        # together with the exit status
+        if not options.update_id:
+            update.log = ''.join(listhandler.read())
+            update.finished = datetime.now()
+            if not options.dryrun:
+                update.save()
 
     if not options.dryrun:
         # Purge old update records
diff --git a/layerindex/views.py b/layerindex/views.py
index 592311f..9f238fd 100644
--- a/layerindex/views.py
+++ b/layerindex/views.py
@@ -147,7 +147,7 @@ def update_layer_view(request, template_name, branch='master', slug=None):
 
     from celery import uuid
 
-    cmd = 'layerindex/update.py --layer %s --branch %s;' % (layeritem.name, branch)
+    cmd = 'layerindex/update.py --layer %s --branch %s --update %%update%%;' % (layeritem.name, branch)
 
     task_id = uuid()
     # Create this here first, because inside the task we don't have all of the required info
diff --git a/tests/test_update_button.py b/tests/test_update_button.py
new file mode 100644
index 0000000..b518bc2
--- /dev/null
+++ b/tests/test_update_button.py
@@ -0,0 +1,62 @@
+# layerindex-web - tests for the Update Layer button and its task status page
+#
+# Licensed under the MIT license, see COPYING.MIT for details
+#
+# SPDX-License-Identifier: MIT
+
+from datetime import datetime
+
+import pytest
+from django.urls import reverse
+
+from layerindex import tasks, views
+from layerindex.models import Branch, LayerBranch, LayerItem, Update
+
+
+@pytest.fixture
+def staff_client(client, django_user_model):
+    user = django_user_model.objects.create_user('staff', 'staff@example.com', 'pw', is_staff=True)
+    client.force_login(user)
+    return client
+
+
+@pytest.mark.django_db
+def test_update_layer_creates_one_update(staff_client, monkeypatch):
+    branch, _ = Branch.objects.get_or_create(name='master', defaults={'bitbake_branch': 'master'})
+    layer = LayerItem.objects.create(name='meta-test', status='P', layer_type='A',
+                                     summary='test', description='test',
+                                     vcs_url='git://example.com/meta-test.git')
+    LayerBranch.objects.create(layer=layer, branch=branch)
+    queued = []
+    monkeypatch.setattr(views.tasks.run_update_command, 'apply_async',
+                        lambda args, task_id: queued.append((args, task_id)))
+    before = Update.objects.count()
+
+    response = staff_client.get(reverse('update_layer', args=('master', 'meta-test')))
+
+    assert response.status_code == 302
+    (branch_name, cmd), task_id = queued[0]
+    assert Update.objects.count() == before + 1
+    assert Update.objects.filter(task_id=task_id).exists()
+    # update.py records its work in the view's Update rather than a new one
+    assert '--update %update%' in cmd
+
+
+@pytest.mark.django_db
+def test_run_update_command_keeps_fields_set_by_command(settings, tmp_path, monkeypatch):
+    settings.TASK_LOG_DIR = str(tmp_path)
+    update = Update.objects.create(started=datetime.now(), task_id='test-task')
+
+    def fake_runcmd(cmd, destdir, outfile, shell):
+        # Stands in for update.py --update, writing to the same record
+        assert cmd == 'update.py --update %d' % update.id
+        Update.objects.filter(id=update.id).update(reload=True)
+        return 'output'
+    monkeypatch.setattr(tasks.utils, 'runcmd', fake_runcmd)
+
+    tasks.run_update_command.apply(('master', 'update.py --update %update%'), task_id='test-task')
+
+    update.refresh_from_db()
+    assert update.reload
+    assert (update.log, update.retcode) == ('output', 0)
+    assert update.finished is not None
