From 8aec5bdfd09bca35cb017219e08a389f85a4416c Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 30 Aug 2026 04:50:04 +0000 Subject: [PATCH 1/2] Minecraft: run the server as the letsbuilda user The itzg image defaults to UID/GID 1000, but /opt/letsbuilda/minecraft is owned by letsbuilda, so world data ended up owned by the wrong user. Look up the letsbuilda account's UID/GID at deploy time with getent and write them to the compose project's .env, which docker compose reads for ${UID:?}/${GID:?} interpolation into the container environment. The image's entrypoint then chowns /data and drops privileges to that user on startup. No numeric ID is hardcoded, and compose.yaml stays a plain static file so Dependabot keeps bumping the pinned image. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01NoJDrw76ZHLTcBG8JTWW2c --- ansible/roles/minecraft/files/compose.yaml | 2 ++ ansible/roles/minecraft/tasks/main.yaml | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/ansible/roles/minecraft/files/compose.yaml b/ansible/roles/minecraft/files/compose.yaml index d08e4bf..0c71232 100644 --- a/ansible/roles/minecraft/files/compose.yaml +++ b/ansible/roles/minecraft/files/compose.yaml @@ -15,5 +15,7 @@ services: OPS: | Shenanigander EXISTING_OPS_FILE: SYNCHRONIZE + UID: "${UID:?}" + GID: "${GID:?}" volumes: - "./data:/data" diff --git a/ansible/roles/minecraft/tasks/main.yaml b/ansible/roles/minecraft/tasks/main.yaml index 20262ce..1f6cf7f 100644 --- a/ansible/roles/minecraft/tasks/main.yaml +++ b/ansible/roles/minecraft/tasks/main.yaml @@ -1,4 +1,8 @@ --- +- name: Look up the letsbuilda account + ansible.builtin.getent: + database: passwd + key: letsbuilda - name: Create configuration directory ansible.builtin.file: path: /opt/letsbuilda/minecraft/ @@ -13,6 +17,15 @@ owner: letsbuilda group: letsbuilda mode: '0600' +- name: Create the .env file + ansible.builtin.copy: + content: | + UID={{ ansible_facts.getent_passwd['letsbuilda'][1] }} + GID={{ ansible_facts.getent_passwd['letsbuilda'][2] }} + dest: /opt/letsbuilda/minecraft/.env + owner: letsbuilda + group: letsbuilda + mode: '0600' - name: Run the containers become: true become_user: letsbuilda From 1f33b1d05d6bd3d637ac181ddb0c426efbbe3c9d Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 30 Aug 2026 04:56:24 +0000 Subject: [PATCH 2/2] Minecraft: use the compose user directive instead of UID/GID env vars Run the whole container as letsbuilda from the start rather than having the image's root entrypoint step down: no root process ever exists in the container, and nothing can chown the bind mount behind our back. /data ownership was already fixed on the host, and any future drift is a manual chown. The getent-to-.env lookup is unchanged; compose now interpolates the IDs into user: instead of the container environment. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01NoJDrw76ZHLTcBG8JTWW2c --- ansible/roles/minecraft/files/compose.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ansible/roles/minecraft/files/compose.yaml b/ansible/roles/minecraft/files/compose.yaml index 0c71232..e2cb44b 100644 --- a/ansible/roles/minecraft/files/compose.yaml +++ b/ansible/roles/minecraft/files/compose.yaml @@ -3,6 +3,7 @@ services: image: itzg/minecraft-server:2026.8.2@sha256:efa878ddb49cf5251b2e5f2ad71b08fd2f7236c1f7907433f6697258b31d2ce4 tty: true stdin_open: true + user: "${UID:?}:${GID:?}" ports: - "25565:25565" environment: @@ -15,7 +16,5 @@ services: OPS: | Shenanigander EXISTING_OPS_FILE: SYNCHRONIZE - UID: "${UID:?}" - GID: "${GID:?}" volumes: - "./data:/data"