VPSSPark 블로그
← 개발 일기로

서버 디스크가 가득 찼나요? 자주 쓰는 캐시를 한 번에 정리하는 스크립트

서버 노트 · 2026.07.23 · 약 11분 읽기

자주 찾는 검색: 서버 디스크 가득 · Linux 디스크 정리 · VPS 디스크 · 일괄 캐시 정리 (Docker · journal · apt)

서버 랙 유지보수—Linux VPS 디스크 공간 및 운영 정리 장면
디스크 경고는 대개 로그, 컨테이너 레이어, 패키지 캐시가 쌓인 결과—진단 후 스크립트 정리가 당황한 rm보다 낫습니다.

새벽 3시, 모니터링 알림이 당직폰을 울렸다. / 파티션 사용률 98%, MySQL이 binlog를 쓰지 못하고, CI 배포는 docker pull 도중 no space left on device로 멈췄다. SSH로 접속해 df -h를 실행하니 루트에 412MB만 남아 있었다. 악성코드가 아니라 3개월간 방치된 로그, Docker 레이어, apt 캐시, journal이 40GB짜리 소형 VPS 디스크를 서서히 잠식한 것이었다.

중소형 VPS에서 이 장애 패턴은 매우 흔하다. 디스크는 작은데 컨테이너는 24시간 돌고, 이미지를 pull하고, 로그는 멈추지 않는다. 수동 rm -rf는 느리고 실수로 중요 파일을 지울 위험이 크다. 실용적인 절차는 먼저 용량을 많이 쓰는 위치를 찾고, 재생 가능한 디렉터리를 스크립트로 일괄 정리하는 것이다. 이 글은 프로덕션에서 반복 사용해 온 원클릭 정리 스크립트를 그대로 복사해 쓸 수 있게 정리하고, 자동화해도 안전한 작업과 재확인이 필요한 작업을 구분한다.

전제: SSH 로그인 권한과 sudo. 접속 자체가 안 되면(SSH 키를 쓸 수 없는 경우 등) 먼저 Linux 서버 SSH 연결 거부 트러블슈팅 가이드로 디스크 full인지 네트워크/방화벽 문제인지 가른다. UFW 규칙을 손보기 전에 공간부터 확보하라. 서비스가 로그를 쓸 수 있어야 정상 동작으로 돌아온다.

5분
위치 파악 + 안전 정리
8+
흔한 캐시 디렉터리
1개
복사 가능한 정리 스크립트

디스크가 가득 차면 무엇부터 깨질까?

루트 파일시스템이 꽉 차면 Linux 증상은 ‘이상한 불량’처럼 보인다. SSH는 되는데 vim 저장이 안 되고, apt install이 실패하며, Docker 컨테이너가 기동하지 않고, Nginx는 500을 반환하는데 error.log는 늘지 않는다. MySQL/PostgreSQL은 읽기 전용으로 바뀌거나 크래시하고, systemd 서비스는 pid 파일이나 socket을 쓸 수 없어 재시작을 반복한다. cron 로그에 append하지 못해 작업이 조용히 실패하기도 한다.

첫 단계는 항상 마운트 포인트 확인이지, ‘커 보이는 폴더’를 추측하는 것이 아니다. 별도 /data 볼륨에 여유가 있어도 루트가 막혀 있을 수 있다. df -hT 없이 ‘디스크 full’을 하나의 숫자로만 보면 잘못된 조사로 시간을 낭비한다:

빠른 진단
# 파티션별 여유 공간
                df -hT

                # / 아래 1단계에서 가장 큰 디렉터리 (느릴 수 있음)
                sudo du -xh --max-depth=1 / 2>/dev/null | sort -hr | head -20

                # inode 고갈도 '디스크 full'처럼 보임
                df -ih

파일이 수백만 개인 호스트에서는 du 스캔에 시간이 걸린다. 이미 느리다면 sudo du -sh /var/lib/docker /var/log /var/cache처럼 의심 경로만 점검한다. 공식 du(1) 매뉴얼-x(동일 파일시스템 내 유지)와 --apparent-size를 설명한다. bind mount나 별도 볼륨이 있을 때 크로스 마운트 합계에 속지 말 것.

서버 디스크 정리 4단계: df 진단, du로 대용량 디렉터리 찾기, 스크립트로 재생 가능 캐시 삭제, df 검증 후 logrotate 설정
권장 순서: 먼저 진단, 그다음 정리. 정리 직후 df로 확인하고 logrotate와 cron을 추가해 3개월 뒤 재발을 막는다.

자주 디스크를 잡아먹는 디렉터리 표

스택마다 다르지만 Ubuntu/Debian VPS에서는 아래 경로가 자주 상위에 온다. Web + Docker + CI를 40GB 디스크 한 대에 올리는 구성은 흔하고, journal과 컨테이너 레이어는 조용히 커지며 애플리케이션 로그는 트래픽 급증 시 급증한다. 표의 자동 정리 가능 열에 ‘주의’가 있으면 의존성을 확인한 뒤 건드린다. 확실치 않으면 du로 dry-run하고 lsof +D /path로 프로세스가 파일을 잡고 있는지 본 뒤 삭제한다.

경로 / 출처 일반적 크기 자동 정리 설명
/var/lib/docker 수 GB~수십 GB 일부 가능 댕글링 이미지, 중지된 컨테이너, 미사용 volume. 실행 중 컨테이너 데이터 volume은 삭제 금지
/var/log/journal 1~10+ GB 가능 systemd journal은 상한 미설정 시 빠르게 팽창
/var/log/*.log 수백 MB~GB 주의 truncate 또는 logrotate. 직접 rm하면 fd가 남아 inode가 해제되지 않을 수 있음
/var/cache/apt 수백 MB~수 GB 가능 apt-get clean은 안전. 다운로드된 .deb만 삭제
/tmp, /var/tmp 가변 일부 가능 7일간 접근 없는 파일 삭제. 업로드 임시 저장소로 쓰는지 확인
Nginx proxy_cache 수 GB 가능 캐시는 재구축 가능. Nginx 리버스 프록시 튜닝 가이드로 cache를 썼다면 정리 후 일시적으로 MISS 증가
구 커널 /boot 수백 MB 가능 apt autoremove --purge로 구 kernel 삭제. 현재와 이전 버전 유지
core dump, 크래시 덤프 가변 가능 /var/crash, 앱 디렉터리의 core.*
먼저 '진짜 데이터'를 제외
/home, /var/lib/mysql, 객체 스토리지 마운트, 백업 디렉터리가 최대면 캐시 정리로는 해결되지 않는다. 아카이브, 디스크 확장, 콜드 데이터 이전이 필요하다. 스크립트는 재생 가능한 공간만 회수하며 용량 계획을 대체하지 못한다.

원클릭 정리 스크립트 (복사 가능)

아래 스크립트는 기본적으로 드라이런이다. 확보될 공간과 실행될 명령을 출력하고, --apply를 붙였을 때만 실제 삭제한다. 반드시 dry-run 후 출력을 확인하고 apply할 것. dry-run을 건너뛰고 유일하게 남은 구형 베이스 이미지를 prune해 배포 파이프라인을 처음부터 다시 짠 팀도 봤다. 모듈별로 나뉘어 Docker 미설치 시 Docker 블록, proxy_cache 미사용 시 Nginx 블록을 주석 처리할 수 있다.

disk-cleanup.sh — /usr/local/sbin/disk-cleanup.sh 로 저장
#!/usr/bin/env bash
                # 서버 디스크 일괄 정리 — 재생 가능 캐시만
                # 사용: sudo ./disk-cleanup.sh          # 미리보기
                #       sudo ./disk-cleanup.sh --apply  # 실행
                set -euo pipefail

                APPLY=false
                [[ "${1:-}" == "--apply" ]] && APPLY=true

                log() { echo "[$(date '+%F %T')] $*"; }
                run() {
                  if $APPLY; then
                    log "EXEC: $*"
                    eval "$@"
                  else
                    log "DRY-RUN: $*"
                  fi
                }

                before=$(df -h / | awk 'NR==2 {print $3 " used, avail " $4}')
                log "Before: $before"

                # 1. apt 캐시
                if command -v apt-get >/dev/null; then
                  run "apt-get clean -y"
                  run "apt-get autoremove -y --purge"
                fi

                # 2. systemd journal — 최근 7일 또는 500MB 유지
                if command -v journalctl >/dev/null; then
                  run "journalctl --vacuum-time=7d"
                  run "journalctl --vacuum-size=500M"
                fi

                # 3. Docker 미사용 리소스 (명명 volume 내 업무 데이터 제외)
                if command -v docker >/dev/null && docker info >/dev/null 2>&1; then
                  run "docker system prune -af --filter 'until=168h'"
                  run "docker builder prune -af --filter 'until=168h'"
                fi

                # 4. 임시 디렉터리 — 7일간 미접근
                run "find /tmp /var/tmp -type f -atime +7 -print 2>/dev/null | head -20"
                if $APPLY; then
                  find /tmp /var/tmp -type f -atime +7 -delete 2>/dev/null || true
                fi

                # 5. Nginx proxy_cache (cache_path를 환경에 맞게 수정)
                NGINX_CACHE="/var/cache/nginx"
                if [[ -d "$NGINX_CACHE" ]]; then
                  run "du -sh '$NGINX_CACHE'"
                  run "find '$NGINX_CACHE' -type f -delete 2>/dev/null || true"
                fi

                # 6. 크래시 덤프
                [[ -d /var/crash ]] && run "rm -rf /var/crash/*"

                # 7. pip / npm 캐시 (있을 경우)
                [[ -d /root/.cache/pip ]] && run "rm -rf /root/.cache/pip/*"
                for u in /home/*; do
                  [[ -d "$u/.npm" ]] && run "npm cache clean --force --cache '$u/.npm' 2>/dev/null || rm -rf '$u/.npm/_cacache'"
                done

                after=$(df -h / | awk 'NR==2 {print $3 " used, avail " $4}')
                log "After:  $after"
                $APPLY || log "미리보기 완료. 확인 후: sudo $0 --apply"

실행 권한: sudo chmod 750 /usr/local/sbin/disk-cleanup.sh. 첫 실행은 --apply 없이 DRY-RUN 출력을 저장한다. Docker 섹션에서 대량 이미지 삭제가 보이면, 레지스트리에서 이미 지운 태그의 유일한 로컬 사본이 아닌지 확인한다.

Docker prune은 만능이 아니다
docker system prune -a미사용 이미지를 모두 삭제한다. 다음 배포에서 전체 re-pull이 필요해 대역폭이 좁은 VPS에서는 복구가 더뎌진다. --filter until=168h로 1주일 미사용 레이어만 지우거나, 정기적으로 docker image prune -f로 댕글링만 정리하는 편이 안전하다. Docker 공식 pruning 문서 참고.

모듈별: 지울 것과 지우지 말 것

journalctl: --vacuum-time--vacuum-size는 함께 쓸 수 있다. systemd는 journalctl 문서 규칙에 따라 바이너리 로그를 잘라낸다. 영구 상한은 /etc/systemd/journald.conf에서 SystemMaxUse=500M 설정 후 systemctl restart systemd-journald.

로그 파일: 프로세스가 아직 연 로그가 크면 truncate -s 0 /var/log/nginx/access.logrm보다 안전하다. 프로세스는 fd를 유지하고, 파일만 지우면 inode가 즉시 해제되지 않는다. 장기적으로 logrotate로 Nginx·앱 로그를 일 단위로 돌리고 N세대 보관한다.

apt: apt-get clean/var/cache/apt/archives만 정리한다. autoremove --purge는 구 kernel과 고아 의존성을 삭제한다. kernel 업그레이드 후에는 현재와 이전 버전을 남겨 새 kernel 부팅 실패 시 롤백할 수 있게 한다. 프로덕션에서는 uname -rdpkg -l 'linux-image-*'를 대조한 뒤 autoremove하는 것이 안전하다.

업무 데이터는 건드리지 않는다: /var/lib/mysql, /var/lib/postgresql, Redis RDB, 사용자 업로드, 백업 마운트는 스크립트 범위 밖이다. du에서 이들이 최상위면 아카이브, 디스크 확장, 콜드 스토리지 이전이 필요하며 캐시 정리로는 부족하다.

정리 후: 디스크가 다시 조용히 가득 차는 것을 막기

루트 사용률을 98%에서 60%로 내린 것은 시작일 뿐이다. 가드레일 없이는 journal이 6주 만에 다시 8GB가 되고, 배포마다 Docker 레이어가 쌓이며, 트래픽 급증이 같은 사고를 재현한다. 소형 VPS 최소 예방 스택은 세 가지:

  • 알림 임계값: 클라우드 콘솔 또는 Prometheus 80% 경고, 85% SMS/페이지. 95%까지 기다리지 말 것.
  • 주간 cron: 0 4 * * 0 root /usr/local/sbin/disk-cleanup.sh --apply >> /var/log/disk-cleanup.log 2>&1
  • journald + logrotate 상한: 무한 증가를 ‘감당 가능한 천장’으로 바꾼다.

Docker Compose에서는 json-file 로그에 max-sizemax-file을 설정한다. 컨테이너 stdout 하나가 디스크를 채우는 경우는 고아 이미지만큼 흔하고, 자주 놓친다.

소용량 VPS 용량 마인드셋
40GB 플랜에 Web + DB + Docker를 올리면 빠듯하다. 정적 자산은 객체 스토리지나 CDN으로, DB binlog는 정기 아카이브, CI 빌드는 전용 Runner로. 매달 새벽 3시 긴급 대응보다 낫다. 디스크는 싸지만 운영자 시간은 그렇지 않다.

대응 전 체크리스트

다음 알림 때 순서대로 확인하려면 runbook에 복사해 두자:

  • df -hTdf -ih — 용량 부족 vs inode 고갈
  • du -sh로 docker, journal, log, cache 점검
  • 스크립트 dry-run → 육안 확인 → --apply
  • 정리 후 df -h 검증, DB·Nginx·Docker spot-check
  • journald 제한, logrotate, cron, 80% 알림 추가

디스크 full은 ‘파일 몇 개 삭제’로 끝나는 일이 아니라 용량·로그 정책 건강검진이다. 스크립트는 새벽 3시의 당황한 rm에서 반복 가능한 표준 절차로 옮긴다. 무엇을 지웠고 df가 얼마나 움직였는지 기록하면 빠진 logrotate나 로테이션 없는 컨테이너 로그가 드러난다. 나머지는 예방으로 페이지를 조용히 유지하는 것이다.

빌드 산출물이 디스크를 잡아먹나? 역할 분리 검토

VPS에서 프로덕션과 xcodebuild, 다단 Docker 빌드, Ollama 모델 캐시를 동시에 돌리면 소형 디스크는 컴파일 주에 DerivedData와 이미지 레이어로 금방 찬다. 프로덕션 로그와 같은 SSD를 두고 싸운다.

더 나은 분리: 프로덕션 VPS는 lean하게 두고 무거운 빌드는 별도 환경으로——클라우드 Mac에서 iOS 패키징, 전용 Runner에서 CI, 프로덕션은 최종 산출물만 pull.

Mac mini M4의 통합 메모리와 낮은 유휴 전력은 ‘빌드 전용, 24시간 트래픽 미부하’ 노드에 적합하다. Linux VPS와 역할을 나누면 컴파일 캐시가 프로덕션 로그와 디스크를 다투지 않는다. 프로덕션 슬림화와 빌드 환경 분리를 계획 중이라면 무거운 작업을 먼저 클라우드 Mac으로——VPSSpark 요금제 보기. 새벽 3시 df -h와의 씨름을 줄이자.

한정 혜택

빌드가 디스크를 잡아먹나요? 프로덕션과 분리하세요

클라우드 Mac에서 빌드 · Linux VPS는 런타임 전용 · 새벽 3시 구출 감소

홈으로
한정 혜택 요금제 보기