서버에 SSH 로 들어갔다가 무심코 친 df -h 한 줄에 가슴이 철렁했다.
/dev/sda1 45G 34G used 12G avail 76%
Oracle Cloud Always Free 의 컴퓨트 인스턴스는 디스크가 45GB 가 전부다. 76% 면 임계점인 80% 까지 4GB 남짓. 다음 주에 docker 이미지 한두 개만 더 받아도 가용 공간이 모자라 컨테이너가 죽을 수 있는 구간이었다.
급한 마음에 du -sh /home/ubuntu/* 부터 돌렸는데 가장 큰 디렉토리가 273MB. 홈 디렉토리는 무죄였다.
그렇다면 누가 30GB 가 넘는 공간을 차지하고 있는 걸까.
이번 글은 그 범인을 찾아내고, 재부팅 없이 21GB 를 회수해서 디스크 사용량을 76% → 27% 로 떨어뜨린 과정을 그대로 기록한 것이다.
1. 진짜 범인 찾기 — docker system df
서버에서 n8n / mariadb / 작은 사이드카 컨테이너 몇 개가 24시간 돌고 있다. Docker 가 의심됐다.
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 7 3 20.71GB 15.84GB (76%)
Containers 3 3 57.26MB 0B (0%)
Local Volumes 1 1 218.3MB 0B (0%)
Build Cache 86 0 18.06GB 17.69GB
답이 나왔다.
- Build Cache 가 18GB, 그것도 98% 가 회수 가능한 상태
- Image 가 20.7GB, 그 중 15.8GB 는 사용 안 하는 dangling/old 이미지
Docker 만으로 약 33GB. 디스크 전체 사용량의 거의 전부였다. 코드도, 로그도, 백업도 아닌, 빌드 캐시와 옛 이미지가 범인이었던 것이다.
2. 가장 안전한 한 줄 — docker builder prune
빌드 캐시는 다음 docker build 때 다시 만들어진다. 즉, 지워도 동작 중인 컨테이너에 0의
영향을 준다. 가장 먼저 정리해야 할 1순위다.
$ docker builder prune -af
...
Total: 18.06GB
명령 한 줄에 18GB 가 사라졌다. df -h 를 다시 쳐 보니 디스크는 76% →
43%.
여기서 끝내도 사실 당장의 위기는 끝났지만, 이미지 prune 까지 가면 추가로 더 회수할 수 있다.
3. 옛 이미지 정리 — docker image prune -a
-a 플래그는 dangling 뿐 아니라 현재 컨테이너가 쓰지 않는 모든 이미지를 제거한다.
단 실행 중인 컨테이너의 이미지는 자동 보존이므로 안전하지만, 옛 버전을 롤백 용도로 남겨뒀다면 잃을 수 있다. 한 번
확인하고 진행.
$ docker image prune -af
Deleted Images:
- ghcr.io/openclaw/openclaw:latest (안 쓰는 옛 버전)
- my_youtube/youtube-helper:0.1.0 (현재 0.2.0 사용 중)
- alpine:latest, hello-world:latest (베이스/테스트용)
Total reclaimed space: 1.137GB
1.1GB 추가 회수. 남은 이미지는 실제 운영 중인 3개 뿐. 디스크는 43% → 33%.
4. 시스템 캐시도 차곡차곡 — journal / npm / apt
Docker 만큼은 아니지만 시스템 캐시도 야금야금 자란다. 27일 무중단 운영의 결과로 systemd journal 이 1.4GB 까지 쌓여 있었다.
$ sudo journalctl --vacuum-size=200M
Vacuuming done, freed 1.1G of archived journals.
$ npm cache clean --force
$ du -sh ~/.npm
66M # 직전 1.1GB → 66MB
$ sudo apt clean
$ du -sh /var/cache/apt/archives
32K # 직전 14MB → 32KB
합계 약 2.1GB 추가 회수. 디스크는 33% → 28%.
5. “_old” 라는 이름의 디렉토리는 거의 항상 정리 대상이다
마지막으로 홈 디렉토리에 openclaw_old/ 라는 273MB 짜리 폴더가 있었다. 이름이 _old 면
거의 99% 백업이다. 다만 확인 없이 삭제하면 안 된다.
$ ls /home/ubuntu/openclaw_old/.env
-rw------- 1 ubuntu ubuntu 471 Feb 21 .env # 자격증명 들어있음
.env 파일에 자격증명이 있어서 한 번 더 의심. 운영 중인 서비스가 이 자격증명을 쓰는지 비교해 봤더니, 토큰이 옛 값이었다. 운영본은 npm global 로 다른 경로에서 돌고 있었고 이 디렉토리와 무관했다.
$ rm -rf /home/ubuntu/openclaw_old
$ df -h /
/dev/sda1 45G 12G 33G 27%
27%. 처음의 12GB 가용에서 33GB 가용으로 바뀌었다.
6. 다음 정비 큐 — 재부팅 일정에 묶어둘 것
이번에 손대지 않고 남겨둔 항목들.
- 옛 커널
linux-image-6.8.0-1047-oracle— 현재 이 커널로 가동 중이라 못 지움. 신규
커널-1050이 설치만 돼 있고 부팅을 안 한 상태. 재부팅 일정이 잡힐 때sudo apt autoremove로 1047 제거하면 약 300MB 회수. - snap 옛 revision —
sudo snap set system refresh.retain=2로 기본 3 → 2 로 줄이면
점진적으로 자동 정리.
| 단계 | 명령 | 회수 |
|---|---|---|
| Docker 빌드 캐시 | docker builder prune -af | 18 GB |
| Docker 옛 이미지 | docker image prune -af | 1.1 GB |
| systemd journal | journalctl --vacuum-size=200M | 1.1 GB |
| npm 캐시 | npm cache clean --force | 1 GB |
| apt 캐시 | apt clean | 14 MB |
| openclaw_old/ | rm -rf | 273 MB |
| 합계 | ~22 GB |
디스크는 76% → 27%, 가용 공간은 12GB 에서 33GB 로 늘었다. 재부팅도, 컨테이너 재기동도 없이 운영 중에 끝낸 작업이다.
교훈 한 줄
디스크가 차오를 때 가장 먼저 의심할 곳은 /home 도 /var/log 도 아닌 Docker
다. docker system df 한 번이면 5초 안에 범인이 보인다. 그리고 docker builder prune
-af 는 운영 중 시스템에 언제 돌려도 안전한 한 줄짜리 청소 도구다. 이번처럼 빌드 캐시
하나로만 18GB 가 나오는 경우가 흔하다.
다른 자동화 노트들 — n8n 도메인 연결, Telegram 봇 충돌, YouTube 자막 차단 진단 — 도 시리즈로 차례로 정리 중이다.