GitHub Actions 비용 줄이는 방법: 한도와 단가부터 워크플로 설정까지
GitHub Actions 사용 시간이 어디서 소모되는지 확인하고, 실행 횟수·job 수·러너·시간·저장소를 줄이는 설정을 적용 조건과 확인 방법까지 설명합니다. 이 사이트의 실제 CI 설정과 실행 시간을 예로 듭니다.

목차
GitHub Actions 사용량이 약 일주일 만에 1,620분까지 올라갔습니다. Pro로 업그레이드했지만, 같은 속도가 한 달 이어진다고 단순 환산하면 Pro의 월 포함량 3,000분으로도 모자랐습니다. 포함량을 늘리는 일과 사용 시간이 새는 곳을 찾는 일은 따로 해야 한다는 판단은 GitHub Free vs Pro에 적었습니다. 이 글은 그 뒤 단계입니다. 사용 시간이 어디서 소모되는지 확인하는 방법, 줄일 수 있는 설정, 그리고 줄었는지 확인하는 방법을 다룹니다.
예로 드는 workflow는 이 사이트(weftware-web)의 것입니다. 1,620분이 나온 앱 프로젝트 workflow의 변경 내용과 전후 수치는 이 글에 넣지 않았습니다. 이 사이트의 CI는 처음부터 지금 구성으로 만들었고(변경 이력이 한 건뿐입니다), 그래서 “이렇게 바꿔서 사용 시간이 이만큼 줄었다”는 전후 비교는 없어요. 있는 것은 지금 구성과 실제로 잰 실행 시간입니다.
사용 시간이 어디서 소모되는지 먼저 봅니다
설정을 바꾸기 전에 어떤 workflow가 사용 시간을 쓰는지부터 봐야 합니다. 두 군데를 봅니다.
- 계정 전체:
https://github.com/settings/billing을 열면 제품별 요약이 탭으로 나오고, View details나 Usage(Metered usage) 화면에서 필터, 그룹, 기간(Time Frame)을 바꿔 볼 수 있습니다. 표로 받고 싶다면 화면 위쪽의 Get usage report에서 Email me the report를 고르면 메일로 다운로드 링크가 옵니다. 링크는 24시간 뒤에 만료됩니다. - 실행 한 건: 저장소의 Actions에서 workflow를 고르고 실행 한 건을 열면, job 요약 아래에 실행 시간이 보입니다. 왼쪽 Run details의 Usage를 열면 청구 대상 시간(billable minutes)이 나옵니다.
실행 한 건의 청구 대상 시간에는 제약이 있습니다. 공식 문서에 따르면 이 값은 private 저장소에서 GitHub-hosted 러너로 돌린 job에만 표시되고, 다음 분으로 올림한 값이며, 러너 종류별 배수는 반영되지 않습니다. 배수까지 반영한 전체 사용량은 위의 계정 화면에서 봅니다. 대시보드가 시간 대신 금액(spend)으로 보여 줄 수도 있는데, 이 금액에는 이미 해당 사용 시간의 비용이 반영돼 있다고 청구 문서에 적혀 있습니다.
비용이 커지는 네 지점
Actions 비용은 한 숫자가 아니라 네 군데에서 따로 커집니다. 앞의 셋은 사용 시간을, 마지막은 저장소 용량을 키웁니다.
사용 시간 계산의 기본 규칙은 공식 문서에서 이렇게 확인했습니다.
- private 저장소에서 GitHub-hosted 러너를 쓰면 계정 플랜에 포함된 사용 시간을 먼저 쓰고, 넘으면 러너 종류별 분당 요금이 청구됩니다. 비용은 저장소 소유자에게 청구됩니다. 포함 사용 시간은 청구 주기가 시작될 때 전체 값으로 돌아갑니다.
- 러너 요금 문서는 job이 쓴 시간을 job마다 분 단위로 올림해 계산한다고 설명합니다. 40초짜리 job 하나도 1분입니다. 이어서 따져 보면, 40초짜리 job을 세 개로 나눠 돌리면 3분으로 계산되는 구조입니다(문서 문장에서 제가 계산한 결과이며, 문서에 이 예가 적혀 있는 것은 아닙니다).
- 실패한 실행도 쓴 시간만큼 청구되고, 고쳐서 다시 돌리면 그 시간이 추가됩니다. 청구 문서의 예시에서 10분짜리 workflow가 5분에 실패한 뒤 재실행으로 성공하면 합계는 15분입니다.
- 공개 저장소의 표준 GitHub-hosted 러너는 무료입니다. 반면 larger runners는 포함 사용 시간을 쓸 수 없고, 공개 저장소에서도 항상 요금이 청구됩니다.
- 유효한 결제 수단이 없으면 포함 사용 시간을 다 쓴 뒤 사용이 차단됩니다. 결제 수단이 있으면 예산(budget) 설정에 따라 청구되거나, Stop usage when budget limit is reached가 켜져 있으면 예산에 닿을 때 차단됩니다.
- 개인 계정의 월 포함 사용 시간은 Free 2,000분, Pro 3,000분입니다(private 저장소 기준). 플랜을 바꿀지는 GitHub Free vs Pro에서 따로 다룹니다.
러너 종류별 분당 요금은 Linux 2코어(x64) $0.006, Linux 2코어(arm64) $0.005, Windows 2코어(x64) $0.010, macOS 3~4코어 $0.062입니다. 2026-10-08에 공식 요금표에서 확인한 값이고, 포함량을 초과한 사용분에 적용됩니다.
macOS는 Linux 2코어(x64)의 약 10.3배(0.062 ÷ 0.006)입니다. 이 단가는 포함량을 초과한 사용량에 붙는 요금이라서, 초과분을 현재 분당 단가로 단순 계산하면 같은 1,000분은 Linux 2코어가 $6, macOS가 $62입니다. 포함 사용 시간을 쓰는 동안에는 러너 종류별 배수가 적용된다는 설명이 job 실행 시간 문서에 있는데, 배수 값은 이번에 확인하지 못해서 적지 않았습니다.
저장소 용량은 사용 시간과 따로 계산됩니다. 아티팩트는 시간 단위로 쌓여 월 단위로 청구되고, 포함량을 넘은 공유 저장소(아티팩트와 GitHub Packages)는 GB당 월 $0.25입니다. 캐시는 저장소당 10 GB가 포함돼 있고, 캐시 한도를 이보다 높게 설정해 넘긴 부분이 GB당 월 $0.07입니다. 로그와 job summary는 아티팩트 저장소 용량에 들어가지 않습니다.
이 사이트의 CI는 이렇게 돌고 있습니다
weftware-web의 .github/workflows/ci.yml 전체입니다. 2026-10-08 기준 main의 파일을 그대로 옮겼습니다.
name: CI
# Pull-request checks only. Deployment is handled by Cloudflare Workers Builds (see README), so main pushes
# do not spend Actions minutes. One job, one OS, no matrix.
on:
pull_request:
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
check:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
- run: npm run lint
- run: npm run typecheck
- run: npm test
- run: npm run build
- run: npm run check앞의 네 지점에 대응해 보면 이렇습니다.
- 실행 횟수:
pull_request에서만 돌고push는 걸지 않았습니다. 배포는 Cloudflare Workers Builds가 맡기 때문에 main에 합칠 때 Actions 사용 시간을 쓰지 않습니다.concurrency의 그룹 이름에github.ref를 넣어서, 같은 ref에 새 실행이 들어오면 진행 중이던 이전 실행을 취소합니다. - job 수: job 하나, matrix 없음. 경로 필터는 쓰지 않습니다.
- 러너와 시간:
ubuntu-latest한 종류,timeout-minutes: 10,actions/setup-node의cache: npm. - 저장소: 아티팩트를 올리는 step이 없습니다.
최근 성공한 PR CI 세 건에서 job과 step 시간을 GitHub의 실행 기록에서 읽었습니다(모두 2026-10-08, 단위는 초, 기록이 초 단위라 ±1초 정도의 오차가 있습니다).
실행 #57, #58, #59 순서로 단계별 시간(초)은 이렇습니다.
- job 전체: 36, 55, 43
- setup-node: 7, 7, 8
npm ci: 9, 17, 8npm run lint: 2, 3, 5npm run typecheck: 7, 11, 10npm run build: 3, 5, 5
표에 없는 나머지(checkout, npm test, npm run check, 마무리 단계 등)는 job 전체에서 위 단계를 뺀 값으로 #57, #58, #59 순서대로 8초, 12초, 7초였습니다. 세 건은 커밋 내용이 서로 달라서 같은 조건의 반복 측정이 아닙니다. 캐시가 적중했는지는 로그까지 열어 확인하지 않았기 때문에, npm ci 시간이 8초에서 17초까지 흔들린 이유는 말할 수 없습니다.
여기서 읽을 수 있는 사실은 하나예요. 세 건 모두 job이 1분 안에 끝났으니, private 저장소의 GitHub-hosted 러너에서는 한 건이 job당 1분으로 올림 계산됩니다. 그날 이런 실행이 몇 건 있었는지, 한 달 합계 사용 시간이 얼마인지는 계산하지 않았습니다. 월 합계는 위에서 본 계정 화면의 값이 정확합니다.
설정별로 언제 쓰고, 언제 쓰지 말아야 하나
같은 PR의 이전 실행 취소하기
PR에 커밋을 연달아 올리면 앞선 실행은 더 이상 의미가 없는 경우가 많아요. concurrency에 cancel-in-progress: true를 두면 같은 그룹의 진행 중인 실행이 취소됩니다. 공식 문서에 따르면 같은 그룹에는 실행 중인 job이나 workflow가 최대 하나이고, 대기(pending) 상태는 기본으로 최신 것 하나만 남습니다.
- 쓸 때: 최신 커밋의 결과만 필요한 PR 검사(lint, 타입 검사, 테스트, 빌드).
- 쓰지 말 때: 배포, 릴리스, DB 마이그레이션처럼 중간에 끊기면 상태가 어정쩡해지는 작업. 이런 workflow는 취소하지 않고 순서대로 기다리게 두는 편이 안전합니다.
cancel-in-progress를 표현식으로 줘서 조건에 따라 취소할 수도 있다고 문서에 적혀 있습니다. - 함정: 같은 저장소에서 workflow가 여러 개일 때 그룹 이름이 겹치면 다른 workflow의 실행까지 취소됩니다. 그룹 이름에 workflow 이름을 넣거나 workflow마다 겹치지 않게 지으세요.
- 확인: 같은 PR에 커밋을 연달아 올린 뒤 Actions 목록에서 앞선 실행이 취소된 것으로 표시되는지 봅니다. 취소된 실행이 취소 전까지 쓴 시간이 어떻게 청구되는지는 이번에 문서에서 확인하지 못했습니다.
이벤트를 하나로 정하기
push와 pull_request에 같은 검사를 걸어 두면, PR이 열린 브랜치에 커밋을 올릴 때 두 이벤트가 모두 실행을 시작할 수 있습니다. 검사를 PR 한쪽에만 두면 그만큼 줄어듭니다.
- 쓸 때: main에 합치기 전에 PR에서 이미 모두 검사하고, 합친 뒤에는 다른 곳에서 배포·검증이 이뤄질 때. 이 사이트가 그렇습니다.
- 쓰지 말 때: 합친 뒤 main에서도 검사를 돌려야 하는 프로젝트(배포를 Actions로 하거나, 합친 결과를 한 번 더 확인하는 정책). 이 사이트도 main에 합쳐진 결과를 Actions로는 검사하지 않는다는 점이 이 구성의 대가입니다.
경로 필터는 조건이 맞을 때만
paths나 paths-ignore로 문서만 바뀐 PR에서 실행을 건너뛸 수 있습니다. 이 사이트의 ci.yml에는 쓰지 않았습니다. 공식 문서에는 중요한 경고가 있습니다. 경로 필터, 브랜치 필터, 커밋 메시지 때문에 workflow가 건너뛰어지면 그 workflow의 검사는 Pending 상태로 남고, 그 검사를 필수로 요구하는 PR은 병합이 막힙니다.
- 쓸 때: 필수 검사로 지정하지 않은 보조 workflow.
- 쓰지 말 때: 브랜치 보호나 ruleset에서 필수 검사로 걸어 둔 workflow. 문서만 바꾼 PR이 병합 대기에서 멈춥니다.
- 확인: 문서만 바꾸는 테스트 PR을 하나 열어, 검사가 Pending으로 남지 않고 병합 가능한지 봅니다.
matrix는 꼭 필요한 조합만
matrix는 한 workflow 실행에서 최대 256개 job을 만듭니다(GitHub-hosted와 self-hosted 모두). job마다 올림 계산이 따로 붙으니 OS와 버전을 곱할수록 사용 시간이 곱으로 늘어납니다. 배포하는 환경이 하나라면 그 조합만 검사해도 충분할 수 있습니다. 라이브러리처럼 여러 버전을 지원해야 하면 줄일 수 없는 비용입니다.
러너는 job마다 따져 봅니다
macOS 러너는 Linux 2코어의 약 10배라서, macOS가 필요 없는 검사를 macOS에서 돌리고 있다면 가장 큰 낭비입니다. 반대로 iOS·macOS 앱 빌드처럼 macOS가 있어야 하는 job은 Linux로 옮길 수 없습니다. 그럴 때는 macOS가 정말 필요한 빌드만 macOS job에 두고, 필요하지 않은 린트나 일부 단위 테스트를 Linux job으로 나누는 방법을 검토합니다. 어느 테스트가 macOS 없이 돌아가는지는 프로젝트마다 다르니 job 하나씩 확인해야 합니다. 다만 job을 나누면 job마다 올림 계산이 붙는다는 점도 같이 따져야 합니다. 1분 안에 끝나는 검사를 둘로 쪼개면 2분이 됩니다.
larger runners는 Team·Enterprise Cloud 조직에서만 쓸 수 있고 포함 사용 시간을 쓸 수 없으며 항상 요금이 청구됩니다. 개인 계정의 포함 사용 시간 부족 문제를 larger runner로 풀 수는 없습니다.
self-hosted 러너는 GitHub 문서에 GitHub Actions 사용 자체는 무료로 나옵니다. 다만 같은 문서가 “러너 머신을 유지하는 비용은 사용자 몫”이라고 적고 있고, 운영체제와 소프트웨어 업데이트도 직접 책임져야 합니다. 분 요금이 사라지는 대신 머신 비용과 관리 부담이 생기는 것이라서, 비용을 줄이는 방법으로 권하지는 않습니다.
시간 상한과 캐시
timeout-minutes의 기본값은 360분(6시간)입니다. job이 멈춰서 몇 시간씩 도는 상황을 막으려면 평소 걸리는 시간보다 넉넉하게 낮춰 둡니다. 이 사이트는 10분으로 두었고, 위에서 본 평소 실행 시간은 36~55초였습니다. 너무 빡빡하게 두면 느린 날 멀쩡한 실행이 실패하니, 평소의 몇 배 정도가 무난합니다. 정확한 배수는 사이트마다 다릅니다.
의존성 캐시(cache: npm 같은 옵션)는 다운로드 시간을 줄여 주지만 항상 이득은 아닙니다. 복원과 저장에도 시간이 들고, 이 사이트에서는 효과를 따로 측정하지 않았습니다. 공식 문서 기준으로 7일 넘게 접근하지 않은 캐시는 지워지고, 저장소 캐시 용량의 기본 한도는 10 GB이며 넘으면 새 캐시를 저장하면서 오래된 캐시부터 지웁니다. 캐시가 쌓였다 지워지기를 반복한다면(cache thrashing) 캐시를 빼는 것도 방법이라고 문서가 설명합니다.
아티팩트와 보관 기간
아티팩트는 workflow 실행이 끝난 뒤에도 남겨야 하는 파일(빌드 결과, 테스트 결과 등)을 저장합니다. 워크플로 실행, 로그, 아티팩트는 기본 90일 보관되고, 저장소 설정에서 공개 저장소는 190일, private 저장소는 1400일 범위로 바꿀 수 있습니다. 바꾼 값은 새로 만드는 항목에만 적용되고 이미 쌓인 것에는 적용되지 않습니다. 특정 아티팩트에 보관 기간을 따로 정하는 방법도 문서에 있습니다.
지우면 앞으로의 저장 비용은 멈추지만, 이미 그 달에 쌓인 사용량은 청구에 남습니다. 이 사이트는 아티팩트를 올리지 않기 때문에 이 항목은 해당되지 않습니다.
바꾼 뒤 정말 줄었는지 확인하기
“다음 달 청구서를 기다리면” 알 수 있지만 너무 늦어요. 바꾸기 전과 후를 같은 종류의 실행으로 비교합니다.
- 바꾸기 전에 기록합니다. 대표 PR 두세 건의 Actions 실행 화면에서 job 실행 시간과 Run details › Usage의 청구 대상 시간을 적어 둡니다. 이 값은 올림한 값이고 배수가 반영되지 않은 값입니다.
- 한 번에 하나씩 바꿉니다. 취소 설정, 러너, 경로 필터를 한꺼번에 바꾸면 무엇이 효과였는지 알 수 없습니다.
- 바꾼 뒤 같은 종류의 PR로 다시 봅니다. 문서만 바꾸는 PR, 코드를 바꾸는 PR처럼 성격이 같은 PR끼리 실행 횟수와 job 시간, 청구 대상 시간을 비교합니다. 실행 시간은 이번 실행에서 보았듯 같은 구성에서도 36~55초로 흔들리니, 한 건이 아니라 몇 건을 봐야 합니다.
- 계정 화면에서 흐름을 봅니다.
https://github.com/settings/billing의 Metered usage(Usage)에서 Actions만 걸러 기간을 바꿔 가며 보고, 필요하면 Get usage report로 내려받습니다. 아티팩트 저장소 사용량은 갱신에 6~12시간이 걸리므로 바로 확인되지 않을 수 있습니다. - 한도 알림을 켜 둡니다. 포함 사용량이 90%와 100%에 닿을 때 메일을 받을 수 있고, 예산은 개인 계정 기준으로 75%, 90%, 100%에서 알림을 받을 수 있다고 문서에 적혀 있습니다.
그래도 부족하다면
위를 적용하고도 포함량이 모자라다면 두 가지를 갈라 봅니다.
- workflow 문제: 같은 PR에서 실행이 여러 번 겹치거나, 필요 없는 job이 있거나, macOS를 쓸 이유가 없는 검사가 macOS에서 돌고 있다면 위 설정으로 줄입니다.
- 필요한 사용량 문제: 불필요한 실행이 이미 없고 정말 그만큼의 검사가 필요하다면, 그때는 포함량을 늘리는 쪽이 맞습니다. 플랜을 바꿀지는 GitHub Free vs Pro의 기준으로 판단하세요.
먼저 https://github.com/settings/billing의 Metered usage에서 이번 달 Actions를 열고, 사용 시간을 가장 많이 쓰는 workflow의 실행 한 건에서 Run details › Usage 값을 적어 두세요. 그 값이 설정을 바꾸기 전의 기준선입니다. 기준선을 줄이고도 포함량이 모자란 것이 확인되면, 플랜 비교는 GitHub Free vs Pro에서 이어서 봅니다.
출처 및 참고자료
마지막 확인
- GitHub Docs · GitHub Actions billing
- GitHub Docs · Actions runner pricing
- GitHub Docs · Product usage included with each plan
출처 10개 더 보기출처 접기
- GitHub Docs · Viewing your usage of metered products and licenses
- GitHub Docs · Viewing job execution time
- GitHub Docs · Setting up budgets to control spending on metered products
- GitHub Docs · Control the concurrency of workflows and jobs
- GitHub Docs · Workflow syntax for GitHub Actions
- GitHub Docs · Actions limits
- GitHub Docs · Dependency caching reference
- GitHub Docs · Workflow artifacts
- GitHub Docs · Managing GitHub Actions settings for a repository
- GitHub Docs · Self-hosted runners
