From 563dec6d9546168f0699f769ca08964d91014bac Mon Sep 17 00:00:00 2001 From: Emil Simeonov Date: Thu, 6 Aug 2026 21:01:13 +0200 Subject: [PATCH] fix: pin containerd.io to compatible version for Docker 28.x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Docker 28.x vendors containerd v2.1.x internally. containerd.io >= 2.3 ships a shim that returns a protobuf BootstrapResult which the vendored 2.1.x code cannot parse, causing every container start to fail with: "failed to create TTRPC connection: unsupported protocol" The role now: - Detects the installed Docker and containerd.io versions - If Docker < 29 and containerd.io >= 2.3, downgrades to the latest compatible 2.2.x from the Docker APT repository - Holds the containerd.io package to prevent future auto-upgrades This was triggered by containerd.io 2.2.6 → 2.3.3 auto-upgrading via apt, which broke all CI runners simultaneously. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ansible/roles/gitea_runner/defaults/main.yml | 9 +++ .../gitea_runner/tasks/rootless_docker.yml | 66 +++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/ansible/roles/gitea_runner/defaults/main.yml b/ansible/roles/gitea_runner/defaults/main.yml index 92f8111..184a1bd 100644 --- a/ansible/roles/gitea_runner/defaults/main.yml +++ b/ansible/roles/gitea_runner/defaults/main.yml @@ -66,6 +66,15 @@ gitea_runner_log_level: "info" gitea_runner_container_label: "gitea-runner=true" gitea_runner_file: ".runner" +# Containerd version pinning — Docker 28.x vendors containerd v2.1.x internally. +# containerd.io >= 2.3 ships a shim that returns a protobuf BootstrapResult which +# Docker 28.x's vendored containerd code cannot parse, causing: +# "failed to create TTRPC connection: unsupported protocol: \b\x03\x12Yunix" +# When Docker 29+ is installed (it vendors containerd 2.3+), this pin is not needed. +# Set to "" to skip the compatibility check and allow any containerd.io version. +gitea_runner_containerd_max_compatible_major: 2 +gitea_runner_containerd_max_compatible_minor: 2 + # Docker installation (for rootless dependencies) gitea_runner_docker_gpg_key_path: "/etc/apt/keyrings/docker.gpg" gitea_runner_docker_apt_arch: "{{ 'amd64' if ansible_facts['architecture'] == 'x86_64' else ansible_facts['architecture'] }}" diff --git a/ansible/roles/gitea_runner/tasks/rootless_docker.yml b/ansible/roles/gitea_runner/tasks/rootless_docker.yml index 8f087dc..42ae55f 100644 --- a/ansible/roles/gitea_runner/tasks/rootless_docker.yml +++ b/ansible/roles/gitea_runner/tasks/rootless_docker.yml @@ -53,6 +53,72 @@ register: gitea_runner_docker_install when: ansible_facts['os_family'] == 'Debian' +# Docker 28.x vendors containerd v2.1.x. containerd.io >= 2.3 ships a shim that +# returns a protobuf BootstrapResult the vendored 2.1.x code cannot parse, causing +# "failed to create TTRPC connection: unsupported protocol" on every container start. +# Detect the mismatch and downgrade containerd.io to the latest compatible 2.2.x. +- name: Check installed Docker CE version (Debian/Ubuntu) + ansible.builtin.command: dpkg-query -W -f='${Version}' docker-ce + register: gitea_runner_docker_version_check + changed_when: false + when: ansible_facts['os_family'] == 'Debian' + +- name: Check installed containerd.io version (Debian/Ubuntu) + ansible.builtin.shell: "set -o pipefail; dpkg-query -W -f='${Version}' containerd.io 2>/dev/null | cut -d: -f2 | cut -d- -f1" + args: + executable: /bin/bash + register: gitea_runner_containerd_version_check + changed_when: false + when: ansible_facts['os_family'] == 'Debian' + +- name: Determine if containerd.io is incompatible with installed Docker + ansible.builtin.set_fact: + gitea_runner_containerd_needs_downgrade: >- + {{ + gitea_runner_containerd_version_check.stdout.split('.')[0] | int > gitea_runner_containerd_max_compatible_major + or ( + gitea_runner_containerd_version_check.stdout.split('.')[0] | int == gitea_runner_containerd_max_compatible_major + and gitea_runner_containerd_version_check.stdout.split('.')[1] | int > gitea_runner_containerd_max_compatible_minor + ) + }} + gitea_runner_docker_major: "{{ gitea_runner_docker_version_check.stdout.split('.')[0] | default('0') | int }}" + when: ansible_facts['os_family'] == 'Debian' + +- name: Find latest compatible containerd.io version (Debian/Ubuntu) + ansible.builtin.shell: | + set -o pipefail + apt-cache madison containerd.io \ + | awk -F'|' '{print $2}' \ + | tr -d ' ' \ + | grep -E '^{{ gitea_runner_containerd_max_compatible_major }}\.{{ gitea_runner_containerd_max_compatible_minor }}\.' \ + | head -1 + args: + executable: /bin/bash + register: gitea_runner_containerd_compatible_version + changed_when: false + when: + - ansible_facts['os_family'] == 'Debian' + - gitea_runner_containerd_needs_downgrade | default(false) + - gitea_runner_docker_major | int < 29 + +- name: Downgrade containerd.io to compatible version (Debian/Ubuntu) + ansible.builtin.apt: + name: "containerd.io={{ gitea_runner_containerd_compatible_version.stdout }}" + state: present + allow_downgrades: true + register: gitea_runner_containerd_downgrade + when: + - ansible_facts['os_family'] == 'Debian' + - gitea_runner_containerd_needs_downgrade | default(false) + - gitea_runner_docker_major | int < 29 + - gitea_runner_containerd_compatible_version.stdout | length > 0 + +- name: Hold containerd.io package to prevent auto-upgrade (Debian/Ubuntu) + ansible.builtin.dpkg_selections: + name: containerd.io + selection: hold + when: ansible_facts['os_family'] == 'Debian' + - name: Update pacman cache (Arch Linux) community.general.pacman: update_cache: true