- Published on
GitLab CI 413 Request Entity Too Large 해결법
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
GitLab CI를 돌리다 보면 갑자기 413 Request Entity Too Large 에러가 터질 때가 있습니다. 대부분은 “업로드하려는 요청 본문이 너무 크다”는 의미인데, 문제는 이 요청이 무엇인지(아티팩트 업로드인지, 패키지 업로드인지, LFS 푸시인지, Runner 로그인지)와 어느 프록시/컴포넌트가 제한을 걸었는지가 환경마다 다르다는 점입니다.
이 글에서는 GitLab CI에서 413이 나는 대표 경로를 분류하고, Nginx/Ingress/GitLab(Omnibus)/Runner/캐시·아티팩트 구성까지 단계적으로 해결하는 방법을 정리합니다.
또한 “일단 제한만 올리기”가 아니라, 아티팩트 크기 자체를 줄이거나 업로드 방식을 바꿔 재발 확률을 낮추는 설계도 함께 다룹니다.
관련해서 프록시 뒤에서 설정 불일치/제한으로 장애가 나는 패턴은 비슷합니다. 프록시 계층을 쓰는 환경이라면 Proxy 뒤 Nginx에서 OAuth 리다이렉트 URI 불일치 해결 글도 함께 참고하면 진단 흐름을 잡는 데 도움이 됩니다.
413이 발생하는 “지점” 먼저 특정하기
413은 대개 다음 중 한 곳에서 발생합니다.
- GitLab 앞단 Nginx(리버스 프록시):
client_max_body_size제한 - Kubernetes Ingress Controller(Nginx Ingress 등): annotation 또는 ConfigMap 제한
- GitLab Omnibus 내장 Nginx: Omnibus 설정의 body size 제한
- Cloud LB / WAF / API Gateway: provider별 request size 제한
- GitLab 자체 업로드 제한(애플리케이션 레벨): 일부 업로드/아티팩트 정책
가장 빠른 방법은 응답 헤더/로그로 누가 413을 냈는지 보는 겁니다.
1) 브라우저/CLI에서 응답 헤더 확인
예를 들어 Job 로그나 아티팩트 업로드 단계에서 413이 났다면, 해당 요청 URL로 재현이 어렵더라도 Runner 로그나 프록시 로그에서 server 헤더를 확인할 수 있습니다.
curl -I https://gitlab.example.com
응답에 server: nginx가 찍힌다고 해서 “어느 nginx인지”까지 확정되진 않지만, 최소한 프록시 계층이 있다는 단서가 됩니다.
2) Nginx/Ingress 로그에서 413 메시지 찾기
Nginx는 보통 에러 로그에 아래 같은 문구를 남깁니다.
client intended to send too large body
Kubernetes Ingress라면 Ingress Controller Pod 로그에서 동일 문구가 보입니다.
kubectl -n ingress-nginx logs deploy/ingress-nginx-controller | grep -n "too large body"
3) GitLab Rails/Workhorse 로그도 확인
GitLab은 업로드 경로에서 gitlab-workhorse가 관여합니다. 프록시가 아니라 GitLab 내부 제한이라면 Workhorse/Rails 쪽에서 단서를 얻을 수 있습니다.
- Omnibus:
/var/log/gitlab/gitlab-workhorse/current - Omnibus:
/var/log/gitlab/gitlab-rails/production.log
가장 흔한 원인: Nginx client_max_body_size
GitLab CI에서 413이 가장 자주 발생하는 상황은 다음입니다.
- Job이 큰 아티팩트(예:
dist/전체,node_modules/, 빌드 산출물 압축)를 업로드 - Job이 패키지 레지스트리로 큰 파일을 업로드
- Git LFS를 사용해 큰 바이너리를 푸시
이때 GitLab 앞단 Nginx가 client_max_body_size로 요청을 잘라버리면 413이 납니다.
리버스 프록시 Nginx 설정 예시
http 블록 또는 해당 server 블록에 설정합니다.
server {
listen 443 ssl;
server_name gitlab.example.com;
client_max_body_size 2g;
location / {
proxy_pass http://gitlab_upstream;
proxy_read_timeout 3600;
proxy_connect_timeout 3600;
proxy_send_timeout 3600;
}
}
적용 후 재시작:
sudo nginx -t && sudo systemctl reload nginx
주의할 점
- 단순히 413만 해결하려다
proxy_read_timeout이 짧아 504/499로 바뀌는 경우가 흔합니다. 대용량 업로드/압축/스캔이 있다면 타임아웃도 함께 점검하세요. client_max_body_size를 무작정 올리면, WAF나 LB 제한에 걸릴 수 있습니다. “가장 바깥 계층”부터 제한을 맞춰야 합니다.
Kubernetes Ingress에서의 413 해결
GitLab을 쿠버네티스에서 운영하거나, GitLab 앞단에 Nginx Ingress가 있다면 annotation으로 해결하는 경우가 많습니다.
Nginx Ingress annotation
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gitlab
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "2g"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
spec:
rules:
- host: gitlab.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: gitlab-webservice
port:
number: 8080
적용:
kubectl apply -f ingress.yaml
ConfigMap으로 전역 설정하는 경우
Ingress Controller의 ConfigMap에서 proxy-body-size를 전역으로 올릴 수도 있습니다. 다만 다른 서비스까지 영향이 갈 수 있어, 가능하면 먼저 Ingress 단위로 적용하는 편이 안전합니다.
GitLab Omnibus 내장 Nginx를 쓰는 경우
외부 프록시 없이 Omnibus GitLab 자체 Nginx를 사용한다면 gitlab.rb에서 설정합니다.
# /etc/gitlab/gitlab.rb
nginx['client_max_body_size'] = '2048m'
적용:
sudo gitlab-ctl reconfigure
sudo gitlab-ctl restart
환경에 따라 외부 Nginx와 Omnibus Nginx를 둘 다 쓰는 구성도 있습니다. 이 경우 “바깥 Nginx”가 낮으면 여전히 413이 납니다. 즉, 업로드 경로의 모든 홉에서 제한이 일관되어야 합니다.
GitLab CI에서 특히 자주 터지는 케이스 4가지
1) 아티팩트 업로드가 너무 큰 경우
Job 마지막에 Uploading artifacts...가 뜬 뒤 413이 나면 거의 이 케이스입니다.
해결 A: 아티팩트를 줄이는 게 1순위
node_modules/,.venv/,target/같은 디렉터리를 아티팩트로 올리지 않기- 산출물만 선별해서 업로드
- 압축률이 좋은 포맷 사용(단, CPU 비용 증가)
예시 .gitlab-ci.yml:
build:
stage: build
script:
- npm ci
- npm run build
artifacts:
expire_in: 7 days
paths:
- dist/
exclude:
- dist/**/*.map
- dist/cache/
exclude를 적극적으로 써서 용량을 줄이는 게 효과적입니다.
해결 B: 캐시와 아티팩트를 구분
캐시는 “재사용을 위한 의존성”이고, 아티팩트는 “다음 스테이지로 넘길 산출물”입니다. 둘을 섞으면 아티팩트가 비대해집니다.
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .npm/
build:
script:
- npm ci --cache .npm --prefer-offline
- npm run build
artifacts:
paths:
- dist/
이렇게 하면 의존성은 캐시로, 산출물은 아티팩트로 분리됩니다.
2) Docker 이미지 레이어/컨텍스트가 큰 경우
Docker build에서 413이 나는 경우는 보통 GitLab이 아니라 레지스트리 업로드(또는 프록시)에서 제한에 걸립니다. 하지만 CI 관점에서는 다음이 핵심입니다.
- Docker build context가 너무 큼(불필요 파일 포함)
.dockerignore미구성
.dockerignore 예시:
.git
node_modules
dist
coverage
*.log
.env
컨텍스트를 줄이면 레이어도 줄고, 업로드/푸시 실패(413 포함) 확률이 크게 내려갑니다.
3) Git LFS 푸시/풀에서 413
대용량 바이너리를 LFS로 다루는 경우, LFS 엔드포인트 업로드에서 413이 발생할 수 있습니다. 이때도 결국은 프록시의 body size 제한인 경우가 많습니다.
- 앞단 Nginx/Ingress의 제한 상향
- LFS 객체를 무작정 GitLab에 쌓기보다, 조직 정책에 따라 별도 오브젝트 스토리지도 검토
4) 패키지 레지스트리 업로드에서 413
예: npm/pypi/maven 패키지를 GitLab Package Registry에 publish할 때 tarball이 크면 413이 납니다.
- 프록시 제한 상향
- 패키지에 불필요 파일이 포함되지 않도록 ignore 설정 적용
예: npm의 경우 .npmignore 또는 files 필드로 포함 파일을 제한합니다.
“제한 상향” 외에 꼭 해야 하는 운영 체크리스트
413을 없애려고 상한만 올리면, 결국 저장소/스토리지 비용과 백업 시간이 폭증합니다. 아래를 같이 점검하는 편이 장기적으로 안전합니다.
아티팩트 보관 정책
expire_in을 반드시 지정- 브랜치/태그별로 보관 기간 차등
artifacts:
expire_in: 3 days
로그/산출물 과다 업로드 방지
- 테스트 리포트/커버리지 파일이 과도하게 커지는지 확인
- 바이너리 덤프/코어 덤프가 아티팩트에 섞이지 않는지 확인
프록시 계층별 제한 일치
- Cloud LB의 최대 요청 크기 제한
- WAF의 body inspection 제한
- Ingress 제한
- GitLab 내장 Nginx 제한
한 곳이라도 낮으면 413이 재발합니다.
트러블슈팅 시나리오: 어디를 올려야 할지 헷갈릴 때
다음 순서로 접근하면 빠릅니다.
- Runner 로그에서 413이 난 시점의 URL/단계 확인
- 가장 바깥(사용자 요청이 처음 닿는) 계층부터 로그 확인
client intended to send too large body가 찍히는 컴포넌트를 찾기- 해당 계층의 body size와 timeout을 함께 조정
- 아티팩트/캐시 전략으로 업로드 크기 자체를 줄이기
프록시 체인이 길수록 “겉보기 증상은 413인데 원인은 프록시 설정 불일치”가 많습니다. 이런 류의 문제는 원리 자체가 비슷하니, 프록시 환경에서의 진단 관점은 Proxy 뒤 Nginx에서 OAuth 리다이렉트 URI 불일치 해결도 같이 읽어두면 재사용할 수 있습니다.
재발 방지: CI 산출물 설계를 바꾸는 팁
- 아티팩트는 “다음 스테이지에 필요한 최소”만 남긴다
- 의존성은 캐시로 돌리고, 캐시 키 전략을 명확히 한다
- Docker는
.dockerignore로 컨텍스트를 줄인다 - 대용량 파일은 Git LFS/오브젝트 스토리지 등 목적에 맞는 저장소를 사용한다
- 보관 기간(
expire_in)을 강제해 스토리지 폭증을 막는다
마무리
413 Request Entity Too Large는 단순히 값을 키우면 끝나는 에러처럼 보이지만, 실제로는 “업로드 경로의 어느 계층이 제한을 걸었는지”를 찾는 게 핵심입니다. Nginx/Ingress/Omnibus/LB 제한을 일관되게 맞추고, 동시에 아티팩트·캐시·Docker 컨텍스트를 정리하면 해결과 재발 방지를 함께 달성할 수 있습니다.
만약 조정 후에도 413이 계속된다면, 같은 요청이 실제로는 다른 엔드포인트(예: 패키지 레지스트리, LFS, 아티팩트)로 가고 있는지, 그리고 가장 바깥쪽 프록시/LB가 별도의 상한을 가지고 있는지부터 다시 확인해보세요.