diff --git a/playbooks/roles/gitea-git-repos/README.rst b/playbooks/roles/gitea-git-repos/README.rst
index 6770339df7..a94905cace 100644
--- a/playbooks/roles/gitea-git-repos/README.rst
+++ b/playbooks/roles/gitea-git-repos/README.rst
@@ -1 +1,17 @@
 Create git repos on a gitea server
+
+**Role Variables**
+
+.. zuul:rolevar:: gitea_url
+   :default: https://localhost:3000
+
+   The gitea to talk to. This is relative to the remote ansible node which
+   means localhost:3000 is on the ansible inventory node.
+
+.. zuul:rolevar:: gitea_always_update
+   :default: false
+
+   Whether or not all projects should be coerced to the configured and
+   desired state. This defaults to false because it can be expensive to run,
+   but if project attributes like issue trackers or descriptions update this
+   is used to make those changes.
diff --git a/playbooks/roles/gitea-git-repos/library/gitea_create_repos.py b/playbooks/roles/gitea-git-repos/library/gitea_create_repos.py
index d0f099e9c2..a87d2f05b8 100755
--- a/playbooks/roles/gitea-git-repos/library/gitea_create_repos.py
+++ b/playbooks/roles/gitea-git-repos/library/gitea_create_repos.py
@@ -243,12 +243,6 @@ class Gitea(object):
                 # TODO: use threadpool when we're running with
                 # https://github.com/go-gitea/gitea/pull/7493
                 self.make_gitea_project(project, csrf_token)
-            else:
-                # We don't need to create it but lets update descriptions
-                # since humans like that.
-                futures.append(settings_thread_pool.submit(
-                    self.update_gitea_project_description,
-                    project, csrf_token))
             if create or self.always_update:
                 futures.append(settings_thread_pool.submit(
                     self.update_gitea_project_settings,
@@ -256,6 +250,12 @@ class Gitea(object):
                 futures.append(branches_thread_pool.submit(
                     self.update_gitea_project_branches,
                     project, csrf_token))
+            if self.always_update:
+                # If we are not creating, but are trying to always update
+                # then we update the project description.
+                futures.append(settings_thread_pool.submit(
+                    self.update_gitea_project_description,
+                    project, csrf_token))
 
     def run(self):
         futures = []
diff --git a/playbooks/test-manage-projects.yaml b/playbooks/test-manage-projects.yaml
new file mode 100644
index 0000000000..2a08e8c3f7
--- /dev/null
+++ b/playbooks/test-manage-projects.yaml
@@ -0,0 +1,5 @@
+- hosts: "gitea:!disabled"
+  name: "Create repos on gitea servers"
+  roles:
+    - role: gitea-git-repos
+      gitea_always_update: true
diff --git a/zuul.d/system-config-run.yaml b/zuul.d/system-config-run.yaml
index 508a8a0f5a..52c095935c 100644
--- a/zuul.d/system-config-run.yaml
+++ b/zuul.d/system-config-run.yaml
@@ -539,8 +539,9 @@
         # Run twice to ensure that we noop properly when
         # all projects are created in gitea. We also update
         # zuul's description to ensure that descriptions are
-        # updated
-        - playbooks/manage-projects.yaml
+        # updated. This uses a test specific playbook to set
+        # the always_update flag.
+        - playbooks/test-manage-projects.yaml
       run_test_playbook: playbooks/test-gitea.yaml
     host-vars:
       gitea99.opendev.org: