필요한 정보 모아보기
테크

GitHub Actions self-hosted runner 버전 강제, 9월 29일로 변경 — 2.329.0 기준과 30일 업데이트 규칙 정리

#GitHub Actions#self-hosted runner#ARC#CI/CD#DevOps

GitHub Actions의 self-hosted runner(셀프 호스티드 러너) 최소 버전 강제가 9월 29일(화)부터 GitHub Enterprise Cloud에 전면 적용됐다. 원래 6월 공지에서 잡았던 날짜는 9월 25일(금)이었는데, GitHub이 9월 28일 체인지로그에서 "기존 공지 대신 9월 29일부터 전면 강제한다"고 날짜를 나흘 미뤘다. 기준 자체는 바뀌지 않았다. 2.329.0 미만 러너는 새로 등록하거나 다시 등록할 수 없고, 이미 등록된 러너라도 작업 실행용 최소 버전에 못 미치면 잡을 받지 못한다.

self-hosted runner는 GitHub이 빌려주는 가상 머신 대신 우리 서버·VM·쿠버네티스 파드에 러너 프로그램(actions/runner)을 깔아 워크플로 잡을 돌리는 방식이다. 러너 프로그램이 오래되면 GitHub 쪽 서비스와 대화가 안 되는 구조라, 이번 강제는 결국 "러너 소프트웨어를 최신으로 유지하라"는 요구다. 오늘(10월 9일) 기준으로는 이미 강제가 시작된 뒤라, 9월 말부터 잡이 대기열에서 안 빠지는 러너가 있다면 버전부터 의심할 차례다.

날짜는 어떻게 바뀌어 왔나

이번 일정은 6월 12일 체인지로그에서 처음 나왔다. GitHub은 잡 실행·러너 통신을 맡는 백엔드를 새 아키텍처로 옮기는 중이고, 새 플랫폼과 호환되지 않는 옛 러너는 더 지원할 수 없다는 게 강제의 이유였다. 6월 공지는 적용 대상을 github.com 전체(GitHub Enterprise Cloud, 데이터 레지던시 포함)로 밝혔고, GitHub Enterprise Server는 "현재로서는" 영향이 없다고 적었다.

구분6월 12일 공지9월 28일 변경 후
GitHub Enterprise Cloud(데이터 레지던시)7월 31일 전면 강제변동 없음(이미 시작)
GitHub Enterprise Cloud9월 25일(금) 전면 강제9월 29일(화) 전면 강제
GitHub Enterprise Server영향 없음영향 없음

6월 공지는 강제 전에 '브라운아웃'을 돌리겠다고 예고했다. 일부러 몇 시간씩 미지원 버전을 막아 보는 예행연습이다. 공지된 계획은 GitHub Enterprise Cloud 기준 8월 24일부터 9월 18일까지 4주였다. 1·2주차는 등록만 막고, 3주차에는 9월 9일 하루를 등록과 잡 실행을 함께 막는 날로 섞고, 4주차(9월 14·16·18일)에는 사흘 모두 둘 다 막는 식이다. 시간대는 미 동부 기준 오전 11시~오후 3시, 한국 시간으로는 다음 날 0시~4시로 잡혀 있었다. 다만 이 일정이 계획대로 시행됐는지 확인해 준 공식 출처는 찾지 못했다. 9월 28일 공지도 강제 날짜만 다룬다. 계획대로였다면 한국 팀은 새벽 배치 잡에서만 증상을 봤을 수도 있다.

기준은 두 개다 — 등록 2.329.0, 실행은 '30일 안 업데이트'

많이 헷갈리는 지점이 여기다. 2.329.0은 영구적인 최소 버전이 아니다. 6월 공지는 요구 사항을 이렇게 둘로 나눴다.

  1. 등록·재등록: 러너가 2.329.0 이상이어야 한다. 새 아키텍처가 러너를 인식하고 연결을 허용하는 최소 버전이다.
  2. 잡 실행 유지: 새 러너 릴리스가 나오면 공개 후 30일 안에 설치해야 한다. 원래 있던 규칙인데, 일부 경우에 일관되게 적용되지 않았던 것을 이번에 제대로 적용한다.

그래서 2.329.0에 고정해 두고 다시는 업데이트하지 않는 러너는 등록은 되더라도 잡을 받지 못한다. 메이저·마이너·패치 구분 없이 어떤 릴리스든 '사용 가능한 업데이트'로 친다. 30일 안에 업데이트하지 않으면 GitHub Actions가 그 러너에 잡을 넣지 않고, 치명적인 보안 업데이트가 나오면 업데이트가 적용될 때까지 잡 배정을 멈춘다. 자동 업데이트를 켜 둔 러너는 업데이트 서비스에 닿을 수만 있으면 이 조건을 저절로 맞춘다.

GitHub이 정리한 미준수 시 증상은 세 가지다. 새 러너 등록 실패, 기존 러너가 잡을 받지 않거나 실행하지 않음, 미지원 러너를 지정한 워크플로가 대기 상태로 남거나 실패함. 러너가 멀쩡히 온라인인데 잡이 대기열에서 안 빠진다면 이 경우를 먼저 떠올리면 된다.

지금 내 러너 버전은 몇이고, 언제까지 쓸 수 있나

이 글을 쓰는 2026년 10월 9일 기준 최신 러너는 v2.338.0(10월 6일 공개)이다. gh CLI 2.101.0(macOS)으로 공개 릴리스 API를 읽기 전용으로 조회한 결과다.

gh api repos/actions/runner/releases/latest --jq '{tag_name, published_at}'
# {"published_at":"2026-10-06T13:55:11Z","tag_name":"v2.338.0"}

등록 하한선인 v2.329.0은 2025년 10월 14일에 나온 버전이다. 1년 가까이 업데이트를 안 한 러너만 등록에서 막힌다는 뜻이고, 실행 쪽 기준이 훨씬 빡빡하다. 내 러너를 점검하는 순서는 이렇게 잡으면 된다.

  1. 러너 머신에서 직접 확인 — 러너 설치 폴더에서 ./bin/Runner.Listener --version을 실행하면 버전 문자열만 출력하고 끝난다(actions/runner 소스의 --version 처리 기준). ./config.sh --version도 같은 프로그램을 불러 버전을 보여 주지만, root가 아닌 사용자로 실행해야 한다. root로 실행하면 RUNNER_ALLOW_RUNASROOT 환경 변수가 없는 한 "Must not run with sudo"를 출력하고 끝난다. root로 도는 컨테이너라면 Runner.Listener 쪽을 쓰는 게 낫다.
  2. API로 전체 목록 확인 — 저장소·조직 단위 러너 목록 API 응답에 version 필드가 있다. 문서상 이 필드의 타입은 'string or null'이라 값이 비어서 올 수도 있다.
  3. 버전별 지원 종료일 조회 — 9월 3일 추가된 GET /repos/{owner}/{repo}/actions/runners/deprecations/{version}(조직·엔터프라이즈 단위도 있음)이 그 버전의 등록·실행 지원 종료일을 돌려준다. 저장소 관리자 권한이 필요하다.
  4. 감사 로그로 등록 이력 보기 — 엔터프라이즈 소유자는 org.register_self_hosted_runner, repo.register_self_hosted_runner, enterprise.register_self_hosted_runner 이벤트에서 등록 당시 러너 버전을 볼 수 있다. 다만 등록 시점 기록이라 연결된 러너 전체 목록은 아니다.
# 1) 러너 목록과 버전 (조직 단위)
gh api orgs/ORG/actions/runners --jq '.runners[] | [.name, .version, .status] | @tsv'

# 3) 특정 버전의 지원 종료일
gh api repos/OWNER/REPO/actions/runners/deprecations/2.336.0

3번 API는 직접 조회해 봤다. 10월 9일, 러너가 한 대도 없는 개인 저장소(github.com)에 관리자 권한으로 호출한 결과다.

러너 버전runtime_deprecates_at (UTC)
2.328.02025-12-16 17:40
2.329.02026-01-23 17:10
2.334.02026-08-10 17:08
2.335.12026-09-24 15:30
2.336.02026-11-05 08:04
2.337.0null(정해지지 않음)
2.338.0404 Not Found

눈여겨볼 점이 셋 있다. 하나, 등록 하한선인 2.329.0조차 잡 실행 지원은 1월에 이미 끝났다 — 등록 기준과 실행 기준이 얼마나 다른지 숫자로 보인다. 둘, 이 값대로라면 조회 시점에 잡을 계속 받을 수 있는 건 2.336.0 이상이었고, 그 2.336.0도 11월 5일(목) 이후엔 기한이 지난다. 셋, 문서상 응답에는 registration_deprecates_at도 있지만 실제 응답엔 이 필드가 아예 없었고, 막 나온 2.338.0은 404였다. 조직·엔터프라이즈 단위에서는 다르게 나올 수 있으니 결과를 이 표로 일반화하지는 않는다. 그래도 "30일 규칙"을 손으로 계산하는 것보다 이 API 값을 기준으로 알림을 거는 쪽이 정확하다.

자동 업데이트를 끈 러너, 컨테이너 이미지, ARC는 손이 더 간다

자동 업데이트를 켠 상시(persistent) 러너는 대개 신경 쓸 게 적다. 문제는 일부러 업데이트를 끈 경우다.

--disableupdate로 등록한 러너. GitHub 문서는 컨테이너로 일회용(ephemeral) 러너를 돌리면 새 버전이 나올 때마다 업데이트가 반복될 수 있어서, 자동 업데이트를 끄고 컨테이너 이미지의 러너 버전을 직접 올리는 방법을 안내한다. 대신 그 경우에도 새 버전이 나온 뒤 30일 안에 업데이트해야 한다고 못 박는다. 문서가 권하는 방법은 actions/runner 저장소 릴리스 알림 구독이다.

직접 만든 러너 이미지·VM 템플릿. 6월 공지의 할 일 목록이 가장 구체적이다. 모든 러너를 최신 지원 버전으로 올리고, 설치 스크립트·VM 이미지·컨테이너 이미지·배포 자동화를 고치고, 옛 캐시 이미지나 템플릿으로 만든 러너는 다시 만든다. ARC 문서의 예시 Dockerfile도 ARG RUNNER_VERSION에 최신 릴리스 버전을 넣어 tar.gz를 내려받는 구조라, 이 값이 하드코딩돼 있으면 이미지를 다시 빌드하기 전까지 러너는 그 버전에 묶인다.

ARC(Actions Runner Controller). ARC는 쿠버네티스에서 러너를 자동으로 늘렸다 줄이는 GitHub 공식 컨트롤러다. 소스를 보면 스케일 세트를 만들 때 러너 설정에 DisableUpdate: true를 넣는다. 러너가 스스로 업데이트하지 않으니, 러너 버전은 사실상 파드 템플릿의 이미지 태그가 정한다. GitHub은 러너 바이너리가 나올 때마다 ghcr.io/actions/actions-runner 이미지를 새로 올리고, 최신 이미지엔 버전 태그와 latest가 함께 붙는다. 10월 9일 레지스트리를 익명으로 조회해 보니 latest와 2.338.0 태그가 같은 다이제스트를 가리켰다.

ARC 운영자가 볼 것은 결국 values 파일이다. image: ghcr.io/actions/actions-runner:2.33x.x처럼 버전을 고정해 뒀다면 30일 주기로 올리는 절차가 있어야 하고, 사내 레지스트리에 미러링한 이미지라면 미러 갱신 주기도 같이 봐야 한다. latest를 쓰면 새 파드는 최신을 받지만, 노드에 캐시된 옛 이미지를 계속 쓰는지(imagePullPolicy)는 따로 확인할 부분이다.

보안 쪽에서 같이 볼 것

이번 강제 규칙에는 보안 조항이 숨어 있다. 치명적 보안 업데이트가 나오면 그 업데이트가 적용될 때까지 잡 배정이 멈춘다 — 30일 규칙과는 별개 조항이다. 자동 업데이트를 끈 운영이라면 보안 릴리스가 곧 장애가 될 수 있다는 얘기다.

러너 자체의 공식 보안 권고도 있었다. 2022년 10월 공개된 GHSA-2c6m-6gqh-6qg3(CVE-2022-39321)은 러너가 컨테이너 액션·잡 컨테이너·서비스 컨테이너를 띄울 때 환경 변수 인코딩 버그로 신뢰할 수 없는 입력이 docker 명령을 조작할 수 있던 문제다. 2.296.2 등 여러 패치 버전으로 고쳐졌고, 지금 강제 기준(2.329.0 이상)을 지키는 러너는 모두 그 범위 밖이다.

더 현실적인 위협은 러너를 악용하는 쪽이다. 보안 업체 Sysdig는 2026년 1월 글에서 Shai-Hulud 웜이 감염된 시스템에 공식 러너 바이너리를 숨김 폴더로 설치하고 SHA1HULUD라는 이름으로 등록해 백도어로 쓴 사례를 분석했다. RUNNER_TRACKING_ID를 0으로 바꿔 잡이 끝나도 프로세스가 살아남게 하는 수법도 함께 소개했다. 탐지 방법으로 권한 것 중 하나가 바로 repo.register_self_hosted_runner 감사 로그 확인이다. 버전 점검용으로 감사 로그를 볼 때, 모르는 이름의 러너 등록이 섞여 있는지도 같이 훑으면 일이 한 번에 끝난다.

GitHub 문서의 기본 경고도 그대로 유효하다. self-hosted runner는 깨끗한 일회용 VM이라는 보장이 없어서 워크플로 속 신뢰할 수 없는 코드에 지속적으로 오염될 수 있고, 그래서 공개 저장소에는 거의 쓰지 말라고 한다. 대안으로 잡 하나만 처리하고 자동 삭제되는 JIT(just-in-time) 러너를 안내한다.

운영하는 입장이라면 이렇게 정리하겠다

개인적으로는 이번 일을 "날짜가 나흘 밀렸다"보다 "러너가 이제 30일짜리 유통기한을 달았다"로 받아들이는 게 맞다고 본다. 2.329.0만 넘기면 된다고 생각하고 이미지를 한 번 올린 팀이 가장 위험하다. 등록은 되는데 잡이 안 들어오니 원인을 찾는 데 시간이 걸린다.

자동 업데이트를 끄는 이유가 분명하지 않다면 켜 두는 게 가장 싸다. 끄는 게 맞는 구조(ARC, 일회용 컨테이너 러너)라면 사람이 기억하는 방식 말고 기계가 챙기는 방식이 필요하다. 예를 들면 이렇다.

  • 러너 이미지의 RUNNER_VERSION이나 ARC values의 이미지 태그를, actions/runner 새 릴리스를 감지해 자동으로 PR을 올리는 방식으로 관리한다. 사람이 할 일은 PR 승인만 남긴다.
  • 매일 도는 작은 워크플로에서 러너 목록 API로 버전을 모으고, deprecations API의 runtime_deprecates_at이 2주 안으로 들어온 버전이 있으면 알림을 보낸다.
  • 오프라인·폐쇄망 러너는 업데이트 서비스에 닿지 못해 자동 업데이트가 켜져 있어도 조건을 못 맞출 수 있다. 이런 러너는 따로 목록을 만들어 둔다.

GitHub Enterprise Server는 이번 강제와 무관하다. 다만 6월 공지는 GHES가 "현재로서는" 영향이 없다고 적었다. GHES를 쓰는 팀도 러너 업데이트 절차를 미리 자동화해 두면 손해 볼 일은 없다.

출처 github.blog/…/2026-09-28-self-hosted-runner-version-enforcement-date-has-movedgithub.blog/…/2026-06-12-github-actions-minimum-version-enforcement-timeline-for-self-hosted-runnersgithub.blog/…/2026-09-03-github-actions-early-september-2026-updatesdocs.github.com/…/self-hosted-runnersdocs.github.com/…/self-hosted-runnersdocs.github.com/…/actions-runner-controllerdocs.github.com/…/secure-usegithub.com/…/v2.338.0github.com/…/Runner.csgithub.com/…/autoscalingrunnerset_controller.gogithub.com/…/GHSA-2c6m-6gqh-6qg3sysdig.comgithub.com/…/v2.329.0github.com/…/config.shgithub.com/…/actions-runner

내용이 틀렸나요? 정정 요청

관련 글