Files
computer-playbook/roles/web-app-gitlab/templates/config/shell-config.yml.j2
kevinveenbirkenbachandClaude Opus 5 6e93862dd5 refactor(gitlab): ship the config trees as swarm configs, not host binds
0bf1e8ef8 replaced Omnibus with the Cloud Native composition and brought nine
bind-mounted config trees with it. Their source directories are created by
tasks/01_config.yml, undelegated, so only on the play host; the peer-replication
branches in sys-svc-compose/tasks/utils/_render_one_template.yml:36-56 are gated
on `not IS_STACK_HOST` and skip entirely when the play targets the manager
alone. Under swarm any task scheduled on a worker therefore found no bind
source, was Rejected permanently, and stack_ready.sh reported "stuck:
gitlab_shell". The nextcloud-6 job dies there, before its own role runs.

Pinning the role to managers would fix the symptom and give up the distribution
the CNG split exists for. Swarm already solves this: it hands a config or secret
to every node that runs a task, so nothing has to pre-exist anywhere. The nine
trees become 26 per-file entries, and gitaly, shell, workhorse, webservice,
sidekiq and migrations are schedulable again with no placement constraint.

One mount moves on purpose. The gitaly image sets CONFIG_TEMPLATE_DIRECTORY=
/etc/gitaly and ships config.toml.tpl there, and entrypoint.sh calls
`set-config "$CONFIG_TEMPLATE_DIRECTORY" "${CONFIG_DIRECTORY:=$CONFIG_TEMPLATE_DIRECTORY}"`
under `set -eo pipefail` -- so a read-only config at /etc/gitaly/config.toml
would be overwritten and crash-loop the container. Pointing the variable at
/etc/gitaly/templates leaves no .tpl for set-config to expand (nullglob) and
gitaly still finds the file, because process-wrapper derives
GITALY_CONFIG_FILE="${GITALY_CONFIG_FILE:-$CONFIG_TEMPLATE_DIRECTORY/config.toml}".
Verified against registry.gitlab.com/gitlab-org/build/cng/gitaly:v19.2.1.

Every other mount lands where it did before: of the 32 config/secret mounts, 5
match an old target exactly and 27 sit inside the directory an old bind
provided. No mount moved unintentionally.

Repeated targets are anchored once and aliased after, so a rename is one edit.
gitaly-config.toml.j2 and shell-config.yml.j2 read their paths from the registry
through the volume lookup instead of repeating them.

Costs, both deliberate: swarm materialises configs and secrets as root:root and
neither emitter supports uid/gid, while GitLab CNG runs as uid 1000 -- so
secrets.yml, both .gitlab_*_secret files and the SSH host keys are 0444 and
readable by any process inside the container, not just GitLab. And a config
carries a content hash, so a one-character edit to gitlab.yml now rolls three
services instead of rewriting a file in place.

Checked without a deploy: validate_volumes_meta reports no violation; both
emitters produce identical configs: blocks under compose and swarm; a file
mounted inside a named-volume mountpoint survives (measured in a container); the
host keys are exactly the static pair 01_config.yml generates; no script or
binary in gitlab-shell:v19.2.1 refuses a permissive host key, and SSH_DAEMON is
gitlab-sshd rather than OpenSSH. make test-lint: 327 passed.

Not verified: nothing here has been deployed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 02:02:29 +02:00

16 lines
556 B
Django/Jinja

---
user: git
gitlab_url: "http://{{ GITLAB_WORKHORSE_SERVICE_NAME }}:{{ GITLAB_WORKHORSE_PORT }}/"
secret_file: /srv/gitlab-secrets/.gitlab_shell_secret
auth_file: "/home/git/.ssh/authorized_keys"
log_file: "/dev/stdout"
log_level: INFO
audit_usernames: false
sshd:
listen: "[::]:{{ GITLAB_SSHD_PORT }}"
web_listen: "0.0.0.0:{{ GITLAB_SHELL_METRICS_PORT }}"
concurrent_sessions_limit: 100
host_key_files:
- {{ lookup('volume', 'web-app-gitlab', 'hostkey_rsa').target }}
- {{ lookup('volume', 'web-app-gitlab', 'hostkey_ed25519').target }}