fix(web-app-gitea): pin single writer and delegate DB patches to the hosting node

In swarm the gitea service defaulted to replicas = group size (3), so three replicas on manager + both workers bind-mounted the same NFS-shared /data and concurrently rewrote app.ini with no cross-node locking, leaving it NUL-filled and failing migrate. Gitea is a single-writer app (external MySQL, repos + app.ini on disk), so pin it to one replica; it stays on the shared NFS volume and remains node-mobile for failover.

The DB-patch sed tasks ran manager-local via GITEA_CONTAINER_ADDRESS, which cannot reach a replica scheduled on a worker. Resolve the hosting node + container id first (the same resolve_host_cid idiom 01_manager_ops.yml already uses) and delegate each patch to GITEA_HOST_NODE, so the patches follow the single replica wherever swarm places it. Compose is unaffected: replicas is swarm-only and the resolver short-circuits to localhost.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
kevinveenbirkenbachandClaude Opus 4.8 committed 2026-07-25 12:44:03 +02:00
1 parent f373fa0ec8
commit 6a5cf7b663
2 files changed
+20 -8

No files matched your search

+1
View File
@@ -50,6 +50,7 @@ gitea:
provides: gitea
image: gitea/gitea
version: 1.26.4
replicas: 1
min_storage: 10GB
backup:
no_stop_required: true
+19 -8
View File
@@ -2,11 +2,19 @@
- name: 🚿 Flush handlers to ensure Gitea is up before DB patch
meta: flush_handlers
- name: "🔍 Resolve {{ application_id }} hosting node + container id for DB patches"
ansible.builtin.include_tasks: "{{ lookup('path_absolute', 'roles/sys-svc-compose/tasks/utils/swarm/resolve_host_cid.yml') }}"
vars:
resolve_service_key: gitea
resolve_output_prefix: GITEA
resolve_compose_cid: "{{ GITEA_CONTAINER_ADDRESS }}"
- name: 🩹 Patch Gitea DB host in app.ini
shell: >
container exec -i --user {{ GITEA_USER }} {{ GITEA_CONTAINER_ADDRESS }}
shell: > # nocheck: container-exec-resolver runtime-resolved CID, delegated to the hosting node
container exec -i --user {{ GITEA_USER }} {{ GITEA_LOCAL_CID | quote }}
sed -ri "s|^(HOST\s*=\s*).*$|\1{{ lookup('database', application_id, 'host') | sed_escape('|') }}:{{ lookup('database', application_id, 'port') | sed_escape('|')
}}|" {{ GITEA_CONFIG }}
delegate_to: "{{ GITEA_HOST_NODE }}"
register: _gitea_patch_host
retries: 12
delay: 5
@@ -14,9 +22,10 @@
notify: compose up
- name: 🩹 Patch Gitea DB name in app.ini
shell: >
container exec -i --user {{ GITEA_USER }} {{ GITEA_CONTAINER_ADDRESS }}
shell: > # nocheck: container-exec-resolver runtime-resolved CID, delegated to the hosting node
container exec -i --user {{ GITEA_USER }} {{ GITEA_LOCAL_CID | quote }}
sed -ri "s|^(NAME\s*=\s*).*$|\1{{ lookup('database', application_id, 'name') | sed_escape('|') }}|" {{ GITEA_CONFIG }}
delegate_to: "{{ GITEA_HOST_NODE }}"
register: _gitea_patch_name
retries: 12
delay: 5
@@ -24,9 +33,10 @@
notify: compose up
- name: 🩹 Patch Gitea DB user in app.ini
shell: >
container exec -i --user {{ GITEA_USER }} {{ GITEA_CONTAINER_ADDRESS }}
shell: > # nocheck: container-exec-resolver runtime-resolved CID, delegated to the hosting node
container exec -i --user {{ GITEA_USER }} {{ GITEA_LOCAL_CID | quote }}
sed -ri "s|^(USER\s*=\s*).*$|\1{{ lookup('database', application_id, 'username') | sed_escape('|') }}|" {{ GITEA_CONFIG }}
delegate_to: "{{ GITEA_HOST_NODE }}"
register: _gitea_patch_user
retries: 12
delay: 5
@@ -34,11 +44,12 @@
notify: compose up
- name: 🩹 Patch Gitea DB password in app.ini
shell: >
shell: > # nocheck: container-exec-resolver runtime-resolved CID, delegated to the hosting node
container exec -i --user {{ GITEA_USER }}
-e PATCH_VALUE={{ lookup('database', application_id, 'password') | sed_escape('|') | quote }}
{{ GITEA_CONTAINER_ADDRESS }}
{{ GITEA_LOCAL_CID | quote }}
sh -c 'sed -ri "s|^(PASSWD\s*=\s*).*$|\1${PATCH_VALUE}|" {{ GITEA_CONFIG }}'
delegate_to: "{{ GITEA_HOST_NODE }}"
register: _gitea_patch_passwd
retries: 12
delay: 5