임베디드 리눅스에서 /var 경로는 무엇을 하나?

먼저 답부터 드리면: /var는 시스템이 돌아가면서 내용이 바뀌는 데이터(로그, 캐시, 서비스 상태, 처리 대기 작업, 크래시 덤프)를 모아 두는 곳입니다. /usr가 “배포 후에는 바뀌지 않는 프로그램과 데이터”라면, /var는 “운영 중에 쓰이는 데이터”입니다. 임베디드에서 이 구분이 중요한 이유는 단순합니다. 루트를 읽기 전용으로 두고, 쓰기는 /var 쪽으로 몰아서 관리하면 플래시 마모·전원 차단·업데이트 문제를 한 곳에서 다룰 수 있기 때문입니다.

다만 /var 전체를 한 가지 방식으로 다루면 안 됩니다. /var/log와 /var/cache는 휘발성(tmpfs)으로 둬도 되는 경우가 많지만, /var/lib는 재부팅 후에도 살아남아야 하는 상태가 들어가는 경우가 대부분입니다. 이 글은 FHS와 systemd의 표준 경로를 기준으로, 보드에서 /var 하위 경로를 “휘발성으로 둘 것”과 “영속 저장소에 둘 것”으로 나누는 기준을 정리합니다.

근거: Filesystem Hierarchy Standard 3.0, file-hierarchy(7), journald.conf(5), systemd.exec(5).

/var는 /usr, /tmp와 무엇이 다른가?

한 줄 답: FHS는 파일을 “공유 가능/불가”와 “정적/가변” 두 축으로 나누고, /var는 가변 데이터를, /usr는 공유 가능한 정적 데이터를 담당합니다. /tmp는 재부팅 시 지워져도 되는 임시 파일 자리입니다.

FHS 3.0은 /var를 “가변 데이터 파일(variable data files)“을 위한 디렉터리로 정의합니다. 스풀 디렉터리, 관리·로그 데이터, 일시적인 파일이 여기에 들어갑니다. 그리고 /var가 존재하는 이유 자체를 /usr를 읽기 전용으로 마운트할 수 있게 하기 위해서라고 설명합니다. 운영 중에 쓰이는 것을 /usr에서 빼내 /var로 옮겨 두면, /usr(나아가 루트 전체)를 읽기 전용으로 둘 수 있습니다.

경로성격재부팅 후임베디드에서의 의미
/usr정적, 공유 가능유지(이미지에 포함)펌웨어 이미지의 본체. 읽기 전용이 기본
/etc호스트별 설정유지보드별 설정. 읽기 전용 루트에서는 overlay나 별도 파티션이 필요할 수 있음
/var가변 데이터하위 경로마다 다름운영 중 쓰기가 몰리는 곳. 설계의 핵심
/tmp임시 파일지워져도 됨보통 tmpfs
/var/tmp임시 파일유지되는 것이 원칙FHS상 재부팅 사이에 보존. 임베디드에선 정책을 명시해야 함
/run런타임 상태항상 지워짐tmpfs. PID 파일, 소켓, 잠금

여기서 헷갈리기 쉬운 것이 /tmp와 /var/tmp입니다. FHS는 /var/tmp를 “재부팅 사이에 보존되는 임시 파일” 자리로 정의합니다. 데스크톱에서는 큰 차이가 없지만, 임베디드에서 /var/tmp를 tmpfs로 바꾸면 이 가정을 깨는 셈입니다. 바꾸는 것 자체는 흔한 선택이지만, 그 사실을 이미지 문서에 적어 두어야 나중에 /var/tmp에 중요한 것을 넣는 애플리케이션을 걸러낼 수 있습니다.

/var 아래 주요 하위 경로는 각각 무엇을 담나?

한 줄 답: 이름이 곧 수명을 알려 줍니다. log와 cache는 잃어도 시스템이 다시 만들 수 있고, lib는 잃으면 안 되며, run과 lock은 원래부터 휘발성입니다.

하위 경로FHS/systemd상 의미잃으면?보드에서의 기본 배치
/var/log로그 파일진단 정보가 사라짐. 동작은 계속됨tmpfs 또는 크기 제한을 둔 영속 파티션
/var/cache재생성 가능한 캐시느려질 뿐 데이터 손실 없음(FHS 요구 사항)tmpfs 가능. 크면 제외 대상
/var/lib애플리케이션·서비스의 상태 정보설정·페어링·DB·패키지 DB가 사라질 수 있음영속 저장소
/var/spool나중에 처리할 작업 대기열보내지 못한 작업이 사라짐요구 사항에 따라 영속
/var/run런타임 데이터원래 휘발성/run으로 가는 심볼릭 링크
/var/lock잠금 파일원래 휘발성/run/lock으로 가는 심볼릭 링크
/var/tmp재부팅 사이에 보존되는 임시 파일정책에 따라 다름영속 또는 tmpfs(문서화 필수)
/var/crash시스템 크래시 덤프(FHS 선택 항목)사후 분석 자료가 사라짐영속, 크기 제한 필수

몇 가지를 조금 더 풀어 보겠습니다.

  • /var/cache: FHS는 여기의 데이터를 애플리케이션이 지워도 데이터 손실 없이 다시 만들 수 있어야 한다고 요구합니다. 그래서 tmpfs에 두거나, 공간이 부족할 때 통째로 지워도 되는 영역으로 취급할 수 있습니다. 반대로 말하면, 지우면 안 되는 것은 /var/cache에 두면 안 됩니다.
  • /var/lib: 패키지 매니저 DB(/var/lib/dpkg, /var/lib/opkg 등), systemd의 서비스 상태(/var/lib/systemd), 네트워크 매니저 연결 정보, 에이전트의 로컬 DB 같은 것이 들어갑니다. 임베디드에서 “재부팅하면 설정이 초기화된다”는 버그의 상당수는 이 경로가 tmpfs에 놓여 있어서 생깁니다.
  • /var/run, /var/lock: FHS 3.0은 /run을 도입하면서 /var/run의 역할을 /run으로 옮겼고, 호환을 위해 /var/run을 /run으로 가는 심볼릭 링크로 둘 수 있게 했습니다. systemd 기반 배포판과 Yocto 기본 이미지도 이 방식을 씁니다. 아직 /var/run에 PID 파일을 쓰는 오래된 데몬도 이 링크 덕에 동작합니다.
  • /var/spool: 메일, 출력, cron/at 작업처럼 “지금 처리 못 한 것을 나중에 처리”하는 대기열입니다. 임베디드에서는 네트워크가 끊겼을 때 텔레메트리를 쌓아 두는 용도로 쓰는 경우가 많습니다. 이 데이터가 전원이 꺼져도 살아남아야 하는지가 배치를 결정합니다.

임베디드에서는 왜 /var를 따로 신경 써야 하나?

한 줄 답: 데스크톱에서는 /var가 그냥 디스크의 한 디렉터리지만, 보드에서는 쓰기 수명이 한정된 플래시, 작은 용량, 갑작스러운 전원 차단, 읽기 전용 루트라는 제약이 모두 /var로 몰리기 때문입니다.

플래시 마모와 쓰기 증폭

NAND와 eMMC는 블록마다 지우고 쓸 수 있는 횟수에 한계가 있습니다. eMMC는 컨트롤러가 웨어 레벨링을 해 주지만, 작은 쓰기가 계속 반복되면 쓰기 증폭 때문에 실제 플래시에 쓰이는 양은 애플리케이션이 쓴 양보다 커질 수 있습니다. 한 줄씩 자주 fsync하는 로그, 몇 초마다 갱신되는 상태 파일이 대표적인 원인입니다. 이런 쓰기 대부분이 /var/log와 /var/lib에서 일어납니다.

작은 루트파일시스템

루트 파티션을 이미지 크기에 딱 맞춰 잡는 경우가 많아서, /var가 루트와 같은 파티션에 있으면 로그 몇 MB만으로도 루트가 가득 찰 수 있습니다. 루트가 가득 차면 로그만 멈추는 게 아니라 /var/lib에 상태를 저장하는 서비스까지 함께 실패합니다.

읽기 전용 루트와 쓰기 영역 분리

루트를 읽기 전용으로 두면 전원 차단 시 루트파일시스템이 깨질 위험이 줄고, A/B 업데이트나 이미지 서명 검증도 단순해집니다. 대신 쓰기가 필요한 곳을 명시적으로 열어 줘야 합니다. 흔한 방식은 세 가지입니다.

  1. /var 일부를 tmpfs로 마운트 — 휘발성 데이터(/var/log, /var/cache, /var/tmp)를 RAM에 둡니다.
  2. /var 또는 /var/lib를 별도 rw 파티션으로 마운트 — 영속 상태를 전용 데이터 파티션에 둡니다.
  3. overlayfs로 루트 위에 쓰기 레이어를 올림 — 읽기 전용 하위 레이어 위에 tmpfs 또는 영속 상위 레이어를 둡니다. 선택 기준은 overlayroot vs A/B 업데이트 글에서 다뤘습니다.

Yocto는 기본적으로 VOLATILE_LOG_DIR을 켜서 /var/log를 tmpfs인 /var/volatile/log로 연결하고, read-only-rootfs 이미지 기능으로 루트를 읽기 전용으로 만들 수 있습니다. Buildroot도 부팅 시 루트를 rw로 다시 마운트할지를 설정으로 고를 수 있습니다. 어느 빌드 시스템이든 기본값이 우리 제품의 요구 사항과 맞는지 한 번은 확인해야 합니다.

휘발성과 영속성의 경계

결국 설계의 핵심은 /var 하위 경로마다 **“전원이 꺼지면 사라져도 되는가”**를 정하는 일입니다. 이 질문에 답하지 않은 채 /var 전체를 tmpfs로 두면 상태가 사라지고, 전체를 플래시에 두면 마모와 용량 문제가 생깁니다.

로그, 저널, 크래시 덤프는 어디에 어떻게 두나?

한 줄 답: 로그는 기본적으로 RAM에 두고 필요한 만큼만 영속화합니다. journald는 Storage=와 크기 제한으로, 텍스트 로그는 logrotate로, 크래시 덤프는 개수와 크기 상한으로 제어합니다.

journald

journald는 Storage= 설정으로 저널 위치를 정합니다. volatile이면 /run/log/journal(RAM)에만, persistent이면 /var/log/journal(필요하면 디렉터리를 만들어서)에 씁니다. 기본값 auto는 /var/log/journal 디렉터리가 이미 있으면 영속, 없으면 휘발성으로 동작합니다. 그래서 이미지에 /var/log/journal이 들어 있는지 여부만으로 동작이 바뀝니다.

작은 플래시에서 영속 저널을 쓸 때는 크기 상한을 반드시 둡니다.

# /etc/systemd/journald.conf.d/10-embedded.conf
[Journal]
Storage=persistent
SystemMaxUse=16M
SystemMaxFileSize=4M
RuntimeMaxUse=8M
MaxRetentionSec=7day

SystemMaxUse=는 영속 저널(/var/log/journal)의 상한, RuntimeMaxUse=는 휘발성 저널(/run/log/journal)의 상한입니다. 이미 쌓인 저널을 줄이는 방법은 journald vacuum 글에 정리해 두었습니다. 모든 로그를 영속화하기보다, 평소에는 volatile로 두고 현장 디버깅용 이미지나 특정 조건에서만 persistent로 바꾸는 구성도 흔합니다.

텍스트 로그와 logrotate

syslog 계열이나 애플리케이션이 직접 쓰는 /var/log/*.log는 logrotate로 크기 기준 회전을 겁니다. 임베디드에서는 날짜 기준(daily)보다 **크기 기준(size, maxsize)과 보관 개수(rotate)**가 더 예측 가능합니다. logrotate 자체가 cron이나 systemd 타이머로 주기적으로 돌아야 한다는 점도 잊기 쉽습니다. /var/log를 tmpfs로 둘 때의 fstab 예시와 주의점은 /var/log를 tmpfs로 두는 법 글을 참고하십시오.

크래시 덤프

systemd-coredump는 코어 덤프를 기본적으로 /var/lib/systemd/coredump에 저장하고, coredump.conf의 MaxUse=, ProcessSizeMax= 등으로 크기를 제한할 수 있습니다. 큰 프로세스의 코어 덤프 하나가 수십~수백 MB가 될 수 있으므로, 작은 데이터 파티션에서는 저장을 끄거나(Storage=none) 상한을 아주 낮게 잡는 것이 안전합니다. 커널 패닉 정보는 /var가 아니라 pstore(/sys/fs/pstore)로 남기고, 부팅 후 이를 /var/lib나 /var/log 쪽으로 옮겨 수집하는 방식도 있습니다.

서비스와 에이전트 상태는 어디에 두나?

한 줄 답: 서비스가 재부팅 후에도 기억해야 하는 것은 /var/lib/<서비스>에, 다시 만들 수 있는 것은 /var/cache/<서비스>에, 런타임에만 필요한 것은 /run/<서비스>에 둡니다.

systemd는 이 구분을 유닛 파일 옵션으로 제공합니다. StateDirectory=는 /var/lib/ 아래, CacheDirectory=는 /var/cache/ 아래, LogsDirectory=는 /var/log/ 아래, RuntimeDirectory=는 /run/ 아래에 디렉터리를 만들고 소유권까지 맞춰 줍니다.

[Service]
ExecStart=/usr/bin/fleet-agent
User=fleet
StateDirectory=fleet-agent
CacheDirectory=fleet-agent
RuntimeDirectory=fleet-agent
ProtectSystem=strict

ProtectSystem=strict를 쓰면 파일시스템 대부분이 해당 서비스에 읽기 전용으로 보이지만, 위 옵션으로 선언한 디렉터리는 쓰기가 허용됩니다. 루트가 읽기 전용인 보드에서 이 패턴을 쓰면 서비스가 어디에 쓰는지가 유닛 파일에 드러나므로, 어떤 경로를 영속 파티션에 둘지 결정하기가 훨씬 쉬워집니다.

OTA 에이전트, 디바이스 관리 에이전트, 컨테이너 런타임(/var/lib/docker, /var/lib/containerd)처럼 상태가 크고 중요한 소프트웨어는 특히 주의해야 합니다. 이런 경로가 tmpfs에 있으면 재부팅할 때마다 디바이스 재등록이나 이미지 재다운로드가 일어나고, 루트와 같은 작은 파티션에 있으면 용량이 부족해집니다.

패키지 매니저도 마찬가지입니다. opkg나 dpkg를 현장에서 쓴다면 패키지 DB(/var/lib/opkg, /var/lib/dpkg)는 반드시 영속이어야 하고, 내려받은 패키지 캐시(/var/cache/...)는 휘발성이어도 됩니다. 반대로 이미지 단위로만 업데이트하는 제품이라면 패키지 DB를 아예 읽기 전용 이미지 안에 두는 편이 일관성이 좋습니다.

실무 체크리스트: 무엇을 tmpfs에, 무엇을 영속 저장소에 두나?

한 줄 답: 경로마다 “잃어도 되는가”, “얼마나 커질 수 있는가”, “얼마나 자주 쓰는가”를 표로 정리하고, 그 결과를 fstab·journald.conf·유닛 파일로 고정합니다.

경로기본 권장영속이 필요한 경우확인할 것
/var/logtmpfs현장 사후 분석이 필수인 제품크기 상한, 회전 주기, 영속 시 쓰기 빈도
/var/log/journal없음(= volatile)부팅 간 로그 추적 필요Storage=, SystemMaxUse=
/var/cachetmpfs재생성 비용이 매우 큰 캐시크기, 비밀 정보가 섞여 있지 않은지
/var/lib영속 파티션거의 항상전원 차단 내성, 파일시스템 선택
/var/spool요구 사항에 따라오프라인 대기열 손실 불가상한과 오래된 항목 폐기 정책
/var/tmp정책을 정해 문서화앱이 재부팅 간 보존을 가정할 때실제로 쓰는 애플리케이션 목록
/var/run, /var/lock/run 링크없음링크가 제대로 있는지
코어 덤프끄거나 작은 상한디버그 빌드coredump.conf의 MaxUse=

점검 순서 예시는 다음과 같습니다.

1. 이미지에서 /var 하위 경로마다 마운트 방식을 확인: findmnt -R /var
2. 운영 중 실제로 쓰기가 많은 경로 찾기: 일정 시간 동안 /proc/diskstats 또는 iotop 관찰
3. 재부팅 후 반드시 남아야 하는 파일 목록을 작성하고 실제로 남는지 확인
4. journald Storage= 값과 /var/log/journal 존재 여부 확인
5. logrotate가 실제로 주기 실행되는지 확인 (타이머 또는 cron)
6. 데이터 파티션을 일부러 가득 채운 상태에서 부팅과 주요 서비스가 버티는지 확인
7. 전원을 강제로 끊은 뒤 /var/lib 상태가 일관적인지 확인

6번과 7번은 빼먹기 쉽지만 현장에서 가장 자주 터지는 부분입니다. 데이터 파티션이 가득 찼을 때 부팅이 멈추면 원격 복구가 불가능해질 수 있습니다.

자주 하는 실수는 무엇인가?

한 줄 답: 대부분 “/var는 항상 쓸 수 있고, 넉넉하고, 안전하다”는 데스크톱식 가정에서 나옵니다.

  1. 디버그 로그를 플래시에 그대로 쓰기 — 개발 중 켜 둔 상세 로그가 양산 이미지까지 넘어가면, 수개월 뒤 플래시 마모나 파티션 가득 참으로 돌아옵니다. 로그 수준과 저장 위치를 빌드 설정으로 분리하십시오.
  2. /var/cache에 비밀 정보 넣기 — 토큰, 인증서 개인 키, 디바이스 자격 증명을 “캐시”라는 이름으로 /var/cache에 두는 경우가 있습니다. FHS상 캐시는 언제 지워져도 되는 데이터이고, 백업·디버그 덤프·지원용 로그 수집 스크립트가 통째로 가져가기 쉬운 경로이기도 합니다. 자격 증명은 /var/lib/<서비스> 아래 권한을 좁힌 경로나, 가능하다면 보안 저장소(TPM, secure element, TEE)에 두십시오.
  3. /var가 항상 쓰기 가능하다고 가정하기 — 읽기 전용 루트에서 /var의 일부만 열어 둔 구성이라면, 애플리케이션이 /var/opt나 임의의 /var/<이름>에 쓰려다 실패합니다. 쓰기 경로를 유닛 파일의 StateDirectory= 등으로 선언하거나, 이미지 설계 문서에 쓰기 가능한 경로 목록을 명시하십시오.
  4. /var/lib까지 tmpfs로 올리기 — “로그 때문에 /var를 통째로 tmpfs로 바꿨더니 재부팅할 때마다 Wi-Fi 설정과 디바이스 ID가 사라진다”는 패턴입니다. tmpfs는 하위 경로 단위로 적용하십시오.
  5. tmpfs 크기를 정하지 않기 — size=를 지정하지 않은 tmpfs는 기본적으로 RAM의 절반까지 커질 수 있습니다. RAM이 작은 보드에서 로그 폭주가 메모리 부족으로 이어집니다.
  6. /var/run을 실제 디렉터리로 두기 — /run과 /var/run이 서로 다른 디렉터리가 되면, 한쪽에 쓴 PID 파일이나 소켓을 다른 쪽에서 찾지 못합니다.

FAQ

질문짧은 답
/var 전체를 tmpfs로 둬도 되나요?권장하지 않습니다. /var/lib처럼 재부팅 후 유지돼야 하는 상태가 사라집니다. 하위 경로 단위로 나누십시오.
/tmp와 /var/tmp는 무엇이 다른가요?FHS상 /tmp는 재부팅 때 지워져도 되고, /var/tmp는 재부팅 사이에 보존되는 것이 원칙입니다.
/var/run과 /run은 같은 곳인가요?현재 표준 구성에서는 /var/run이 /run을 가리키는 심볼릭 링크입니다.
journald 로그가 재부팅 후 사라집니다. 왜 그런가요?Storage=auto인데 /var/log/journal이 없거나, Storage=volatile로 설정돼 있기 때문일 가능성이 큽니다.
/var/cache는 지워도 되나요?FHS상 애플리케이션이 다시 만들 수 있어야 하므로 지워도 데이터 손실은 없어야 합니다. 그렇지 않다면 애플리케이션이 경로를 잘못 쓰고 있는 것입니다.
읽기 전용 루트에서 /etc는 어떻게 하나요?/var와 별개 문제입니다. 설정을 이미지에 고정하거나, overlay 또는 데이터 파티션으로 필요한 파일만 옮기는 방식을 씁니다.

한 줄 정리: /var는 “쓰기가 일어나는 곳”을 한데 모아 둔 경로입니다. 임베디드에서는 이 경로를 하위 디렉터리 단위로 휘발성과 영속성으로 나누고, 각 영역에 크기 상한을 두는 것이 설계의 전부라고 해도 과언이 아닙니다.

출처 (Sources)