Docker: no space left on device
안녕하세요, BlueByte입니다. Docker의 "no space left on device"는 대개 오래된 이미지와 빌드 캐시가 조용히 쌓인 것이지만 — 항상 그렇진 않으니, 지우기 전에 무엇이 실제로 찼는지부터 확인합시다. "공간 없음"엔 세 가지 뜻이 있고, prune으로 고쳐지는 건 그중 하나뿐입니다.
이 오류가 뜻하는 것
빌드나 pull이 실패합니다:
write /var/lib/docker/...: no space left on deviceDocker의 저장 영역이 찼습니다. 이미지, 멈춘 컨테이너, 빌드 캐시, 안 쓰는 볼륨이 쌓이며 자동으로 안 지워져 바쁜 빌드 호스트에선 공간이 바닥납니다. prune 전에 무엇이 실제로 찼는지 확인하는 게 좋습니다.
"찰" 수 있는 세 가지
Docker가 만드는 모든 것이 /var/lib/docker 아래 공간을 씁니다: 모든 이미지 레이어, 종료된 컨테이너, 빌드 캐시, 고아 볼륨. 실제로 "찰" 수 있는 세 가지는 각각 다른 해결이 필요합니다:
- 회수 가능한 Docker 데이터 — 오래된 이미지·멈춘 컨테이너·빌드 캐시. 흔한 경우.
/var/lib/docker가 놓인 호스트 파일시스템이 바이트 소진, Docker와 무관.- 바이트는 남는데 inode 소진 — 작은 파일 다수, 레이어 이미지에 흔함.
지우기 전에 어느 쪽인지 확인
Docker 자체 회계부터:
docker system df이미지·컨테이너·로컬 볼륨·빌드 캐시를 회수 가능 열과 함께 분해합니다. 그다음 호스트를 바이트·inode 둘 다 확인:
df -h /var/lib/docker
df -i /var/lib/dockerdf -h는 여유인데 df -i가 100%면 바이트가 아니라 inode 소진입니다. docker system df의 회수 가능이 적은데 df -h가 꽉 찼으면 Docker 밖의 무언가가 디스크를 먹는 것입니다.
필요 없는 것을 prune 하기
- 멈춘 컨테이너, 안 쓰는 네트워크, 댕글링 이미지, 빌드 캐시 제거:
docker system prune- 더 회수하려면 어떤 컨테이너도 안 쓰는 이미지와 안 쓰는 볼륨도 제거 — 데이터를 지우니 안내를 읽으세요:
docker system prune -a --volumes- 호스트 디스크 자체가 찼으면(Docker만이 아니라) 볼륨을 키우거나 다른 곳을 비우세요 — Docker prune은 소용없습니다.
실제 사례: 빌드 캐시로 가득 찬 CI 러너
CI 러너의 빌드가 "no space left on device"로 실패합니다. docker system df가 수 주간 작업의 회수 가능 빌드 캐시·댕글링 이미지 40GB를 보이고, df -h가 /var/lib/docker가 98% 참을 확인하며 df -i는 정상이라 바이트 문제입니다. docker system prune -af(여기선 안전 — 러너에 영속 상태 없음)로 40GB를 회수하니 다음 빌드가 성공합니다. 이후 야간 docker system prune -af를 예약해 작업 사이에 디스크가 다시 안 차게 합니다.
공간이 돌아왔는지 확인
docker system df와 df -h /var/lib/docker를 다시 돌려 공간이 돌아왔는지 확인하고, 실패하던 빌드·pull을 재시도합니다. 오류 없이 완료돼야 합니다.
다시 겪지 않으려면
CI 러너에선 주기적 docker system prune -af를 예약해 작업 사이에 디스크가 안 차게 하고, df -h /var/lib/docker에 알림을 걸고, 어떤 볼륨을 남길지 의도적으로 정해 --volumes prune이 놀래지 않게 하세요.
포트·메모리 오류와의 구분
no space left on device는 저장 문제입니다. port is already allocated(네트워크 충돌)나 OOM 킬(메모리)과 다릅니다. "no space left"를 컨테이너 자신이 보고하면 호스트가 아니라 컨테이너 자체 파일시스템 한계일 수 있으니 컨테이너 안도 확인하세요. Docker가 공간이 없다고 하면 prune 전에 docker system df와 df -i를 돌리세요.
관련 질문
docker system prune이 돌던 컨테이너나 데이터를 지우나요?
아니요. 기본 prune은 멈춘 컨테이너·댕글링 이미지·안 쓰는 빌드 캐시만 제거합니다. 돌던 컨테이너와 볼륨은 --volumes를 붙이고 볼륨이 붙어있지 않은 경우가 아니면 건드리지 않습니다.
df는 디스크가 안 찼다는데 Docker는 공간이 없다고 합니다. 왜죠?
바이트가 아니라 inode가 바닥났거나, Docker 데이터가 더 작은 별도 파일시스템에 있을 수 있습니다. /var/lib/docker에 대해 df -i와 df -h를 확인하세요.
--volumes는 얼마나 지우나요?
현재 컨테이너에 붙지 않은 볼륨을 모두 — 멈췄지만 남기려던 데이터베이스 포함. 확실치 않으면 먼저 docker volume ls로 확인하세요.
안전한 자동 정리가 있나요?
영속 상태가 없는 CI 러너엔 예약된 docker system prune -af가 안전합니다. 데이터 볼륨이 있는 호스트에선 --volumes를 빼세요.
빌드 캐시가 쓰는 공간을 제한할 수 있나요?
네 — 캐시 크기 한도를 둔 빌더(docker buildx)를 쓰거나 keep-storage 상한과 함께 docker builder prune을 돌려 캐시가 디스크가 찰 때까지 자라지 않고 한정되게 하세요.
참고 자료
Haneul Seo
Infrastructure engineer · 10+ years running Linux fleets
같은 카테고리 다른 글
Git: fatal: detected dubious ownership in repository
Git 2.35.2의 CVE-2022-24765 수정 이후, Git은 작업 트리나 .git 디렉터리의 소유자가 명령을 실행한 사용자와 다르면 저장소를 읽지 않습니다. 컨테이너, CI 작업, sudo 세션, 공유 드라이브에서 주로 나타납니다. 저장소가 내 것이어야 한다면 소유권을 바로잡고, 아니라면 global 설정의 safe.directory에 정확한 경로를 추가하세요 — Git은 이 설정을 저장소 자신의 설정에서는 무시합니다.
Kubernetes: Internal error occurred: failed calling webhook
admission webhook이 쓰기 요청 앞에 서 있는데 API server가 응답을 받지 못했고, 기본값인 failurePolicy: Fail이 그 침묵을 거부로 바꾼 것입니다. 메시지 끝부분이 곧 진단입니다 — context deadline exceeded는 호출이 도달하지 못한 것, no endpoints available은 떠 있는 게 없는 것, x509 줄은 API server가 webhook 인증서를 신뢰하지 않는 것입니다. 원인마다 해결이 다르고, 어느 것도 매니페스트 문제가 아닙니다.
MySQL: ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
다른 트랜잭션이 쥐고 있는 행 잠금을 innodb_lock_wait_timeout 만큼 기다리다 포기한 것입니다. sys.innodb_lock_waits 가 막고 있는 세션을 지목하고 KILL 문까지 만들어 주며, blocking_query 가 NULL 이면 블로커가 열린 트랜잭션 위에서 놀고 있다는 뜻입니다. 재시도 로직이 가장 자주 틀리는 지점은 따로 있습니다. 기본값에서는 타임아웃된 문장 하나만 롤백되므로, 트랜잭션은 그대로 열린 채 앞서 잡은 잠금을 전부 쥐고 있습니다.
Redis: MISCONF Redis is configured to save RDB snapshots, but it's currently unable to persist to disk
읽기는 되는데 모든 쓰기가 거부됩니다. 마지막 백그라운드 저장이 실패했고 stop-writes-on-bgsave-error 기본값이 yes 이기 때문입니다. 진짜 원인은 로그에 적혀 있습니다 — 디스크 공간 부족, redis 사용자가 쓸 수 없는 dir, rename 시점의 read-only 마운트, 또는 fork 가 Cannot allocate memory 로 실패하는 경우입니다. 원인을 고치고 BGSAVE 한 번만 돌리면 재시작 없이 쓰기가 돌아옵니다. rdb_last_bgsave_status 가 err 에서 ok 로 바뀝니다. stop-writes-on-bgsave-error no 는 쓰기를 즉시 되살리지만 스냅샷은 여전히 실패한 상태로 두므로, 해결이 아니라 의도한 맞교환으로 다뤄야 합니다.
Node.js: FATAL ERROR: Reached heap limit — JavaScript heap out of memory (종료 코드 134)
V8 힙에는 시스템 메모리와 Node 릴리스에서 유도되는 자기만의 천장이 있고, 가진 RAM 보다 훨씬 낮은 경우가 흔합니다. 빌드나 서버가 거기 닿으면 V8 은 FATAL ERROR: Reached heap limit 과 종료 코드 134 로 스스로 중단합니다. v8.getHeapStatistics().heap_size_limit 으로 실제 한도를 읽은 뒤, 큰 작업은 --max-old-space-size(MiB)나 NODE_OPTIONS 로 올리고, 컨테이너 안에서는 cgroup 한도보다 낮게 잡고, 오래 도는 프로세스의 누수는 --heapsnapshot-near-heap-limit 으로 잡으세요. FATAL ERROR 줄 없이 137 로 끝나면 컨테이너 kill 이지 이 오류가 아닙니다.
Docker: 컨테이너 시작 직후 "exec format error" — 플랫폼이 다른 이미지, 에뮬레이터 없음, 셔뱅 없는 스크립트
컨테이너가 첫 명령에서 exec format error 로 끝납니다. 커널의 ENOEXEC, 즉 파일은 있지만 여기서는 실행할 수 없다는 뜻입니다. 실제로는 한 CPU 아키텍처에서 빌드한 이미지(Apple 실리콘 Mac은 linux/arm64 를 만듭니다)를 binfmt_misc 에 QEMU 핸들러가 없는 다른 아키텍처(x86_64 서버)에서 돌리거나, 엔트리포인트 스크립트의 첫 줄이 셔뱅이 아닌 경우입니다. uname -m, docker image inspect, ls /proc/sys/fs/binfmt_misc 로 원인을 가르고, docker buildx build --platform 을 명시(또는 두 플랫폼 매니페스트 목록 발행)하거나, 에뮬레이션이 목적이면 QEMU 등록과 --platform, 스크립트라면 #!/bin/sh 한 줄로 해결합니다.