В Linux временные файлы живут везде: в /tmp, /var/tmp, кэшах приложений, директориях сборки, каталогах загрузок и промежуточных папках веб-проектов. На рабочей станции это часто просто раздражает, а на VDS может быстро превратиться в инцидент: закончилось место, база не пишет WAL или binlog, PHP не может создать сессию, Nginx получает ошибку при буферизации, пакетный менеджер не обновляется.
Самый привычный ответ из старых инструкций — добавить в cron что-то вроде rm -rf /tmp/*. Работает? Иногда. Безопасно? Не очень. Современный systemd уже содержит инструмент, созданный именно для таких задач: systemd-tmpfiles. Он умеет создавать временные директории с правильными правами, чистить старые файлы по возрасту, учитывать исключения, запускаться при загрузке и по systemd timer. Разберёмся, как использовать его для /tmp и cache cleanup без опасных самодельных скриптов.
Почему cron с rm -rf — плохая идея для /tmp
Команда rm -rf сама по себе не зло. Проблема в том, что она слишком простая для задачи, где важны возраст файла, активные процессы, сокеты, lock-файлы, точки монтирования, права доступа и человеческие ошибки. Очистка временных каталогов кажется рутинной, но именно в таких местах чаще всего прячутся аварии уровня «удалили не то».
Типичный cron-скрипт выглядит коротко:
0 3 * * * root rm -rf /tmp/*
На первый взгляд всё понятно: каждую ночь удаляем содержимое /tmp. На практике возникают вопросы. Что если в /tmp лежит Unix socket сервиса, который сейчас работает? Что если приложение держит временный файл открытым несколько часов? Что если в скрипт позже добавят переменную, она окажется пустой, и путь станет не тем? Что если внутри каталога окажется отдельное монтирование? Что если удалённый открытый файл продолжит занимать место, а администратор решит, что очистка не помогла?
Главная проблема rm -rf в cron — он не знает, что для вас временное, а что временно нужное прямо сейчас.
Можно сделать cron аккуратнее: использовать find, ограничить глубину, добавить -xdev, удалять только старые файлы. Например:
find /tmp -xdev -mindepth 1 -mtime +7 -delete
Это уже лучше, но всё равно остаётся самодельная логика: нужно отдельно создавать директории, выставлять права, продумывать исключения, логировать результат, проверять запуск после перезагрузки. systemd-tmpfiles закрывает эти задачи штатным способом и хранит политику очистки в декларативных правилах tmpfiles.d.
Что делает systemd-tmpfiles
systemd-tmpfiles — утилита из состава systemd, которая применяет правила из каталогов tmpfiles.d. Правила описывают, какие пути нужно создать, какие права назначить, какие файлы очистить по возрасту, какие пути удалить, а какие исключить из обработки.
Важно: это не только «чистильщик /tmp». Через systemd-tmpfiles дистрибутивы и пакеты создают runtime-директории, socket-каталоги, lock-каталоги, кэши и временные структуры, которые должны существовать после загрузки с корректными владельцами и правами.
Обычно участвуют две системные единицы:
systemd-tmpfiles-setup.service— применяет правила создания при загрузке системы;systemd-tmpfiles-clean.timer— периодически запускает очистку старых файлов через соответствующий service.
На большинстве дистрибутивов Linux это уже включено из коробки. Проверить можно так:
systemctl status systemd-tmpfiles-clean.timer
systemctl list-timers systemd-tmpfiles-clean.timer
systemctl status systemd-tmpfiles-clean.service
Если timer отключён, его можно включить:
systemctl enable --now systemd-tmpfiles-clean.timer
Команды одинаковы для Debian/Ubuntu, AlmaLinux/Rocky Linux/CentOS Stream/Oracle Linux и Fedora, потому что управление выполняется через systemd. На минимальных контейнерных образах systemd может отсутствовать или работать не как PID 1; в таком случае tmpfiles обычно запускают снаружи, при сборке образа или через оркестратор, но для обычного VDS с полноценной Linux-системой штатный timer — нормальный путь.

Где лежат правила tmpfiles.d и как работает приоритет
Правила читаются из нескольких каталогов. В разных версиях systemd набор может немного отличаться, но базовая идея такая:
/usr/lib/tmpfiles.d/или/lib/tmpfiles.d/— правила, поставляемые пакетами дистрибутива;/run/tmpfiles.d/— временные правила, созданные во время работы системы;/etc/tmpfiles.d/— локальные правила администратора.
Локальные настройки администратора должны жить в /etc/tmpfiles.d/. Не правьте файлы в /usr/lib/tmpfiles.d/: при обновлении пакета изменения могут быть перезаписаны. Если нужно заменить правило, поставляемое дистрибутивом, обычно создают файл с тем же именем в /etc/tmpfiles.d/ и аккуратно вносят изменения туда.
Посмотреть итоговую конфигурацию удобно командой:
systemd-tmpfiles --cat-config
А для диагностики конкретного каталога можно ограничиться префиксом:
systemd-tmpfiles --clean --prefix=/tmp
Эта команда вручную запускает очистку по правилам, которые относятся к /tmp. Для проверки создания директорий используют --create:
systemd-tmpfiles --create --prefix=/var/cache/myapp
Если ваша версия systemd поддерживает пробный режим, полезно сначала выполнить проверку без изменений:
systemd-tmpfiles --dry-run --clean --prefix=/var/cache/myapp
На старых дистрибутивах параметра --dry-run может не быть. Тогда тестируйте правило на отдельном каталоге, а не сразу на боевом /tmp или каталоге приложения.
Синтаксис tmpfiles.d: минимум, который нужен администратору
Строка правила tmpfiles.d состоит из нескольких полей. В упрощённом виде:
# Type Path Mode User Group Age Argument
Самые полезные типы для задач очистки:
d— создать директорию, выставить права и владельца; если указанAge, содержимое может очищаться по возрасту;D— похоже наd, но при операции удаления содержимое директории может быть очищено более агрессивно;e— применить настройки и очистку к существующей директории, не создавая её, если она отсутствует;x— исключить путь из очистки;r— удалить файл или пустую директорию;R— удалить путь рекурсивно, используйте очень осторожно.
Поле Age задаёт возраст: например, 10d для десяти дней, 12h для двенадцати часов, 30min для тридцати минут. Не воспринимайте это как строгий TTL с момента создания файла. systemd-tmpfiles оценивает возраст по файловым метаданным; детали зависят от типа объекта и версии systemd. Для практики важно простое правило: если файл активно меняется или используется, он с меньшей вероятностью будет считаться старым кандидатом на удаление.
Пример отдельной политики для кэша приложения:
# /etc/tmpfiles.d/myapp-cache.conf
# Type Path Mode User Group Age Argument
d /var/cache/myapp 0750 myapp myapp 14d -
d /var/cache/myapp/tmp 0750 myapp myapp 2d -
x /var/cache/myapp/keep - - - - -
Здесь мы создаём основной каталог кэша и временный подкаталог, назначаем владельца myapp, а также исключаем /var/cache/myapp/keep из очистки. Такое правило проще ревьюить, чем shell-скрипт: намерение видно сразу.
Как настроена очистка /tmp по умолчанию
Во многих дистрибутивах базовые правила для /tmp и /var/tmp лежат в файле наподобие tmp.conf внутри каталога с пакетными правилами. Часто там указано, что /tmp очищается быстрее, а /var/tmp хранит файлы дольше.
Это соответствует традиционному смыслу каталогов:
/tmp— временные файлы, которые могут исчезнуть после перезагрузки или регулярной очистки;/var/tmp— временные файлы, которым иногда нужно пережить перезагрузку;/run— runtime-данные текущей загрузки, обычно в tmpfs и обычно не предназначены для ручной очистки.
Перед изменениями посмотрите, что уже задано системой:
systemd-tmpfiles --cat-config
Если нужно найти только строки, связанные с временными каталогами, можно использовать обычные инструменты фильтрации:
systemd-tmpfiles --cat-config | grep -E '/tmp|/var/tmp'
Не добавляйте второе конфликтующее правило для /tmp «на всякий случай». Лучше понять существующее правило и при необходимости заменить его через файл с тем же именем в /etc/tmpfiles.d/. Иначе можно получить неочевидный порядок применения и предупреждения в журнале.
Практический сценарий: хотим чистить /tmp через 3 дня
Допустим, на сервере много сборок, временных архивов и файлов загрузок, а /tmp регулярно разрастается. Вы хотите хранить временные файлы не дольше трёх дней. Сначала найдите исходное правило:
systemd-tmpfiles --cat-config | grep /tmp
Затем определите файл, который поставляет это правило. В выводе --cat-config обычно видны заголовки файлов. Если правило находится в пакетном tmp.conf, создайте локальный вариант в /etc/tmpfiles.d/tmp.conf. Не обязательно копировать весь файл бездумно: но если в исходном файле есть исключения для systemd-private каталогов или других служебных путей, их важно сохранить.
Пример локального файла:
# /etc/tmpfiles.d/tmp.conf
# Local tmp cleanup policy
q /tmp 1777 root root 3d -
q /var/tmp 1777 root root 14d -
x /tmp/systemd-private-* - - - - -
X /tmp/systemd-private-*/tmp - - - - -
Тип q часто встречается в системных правилах для /tmp: на файловых системах с поддержкой субтомов он может использовать дополнительные возможности, а в обычном случае ведёт себя близко к созданию директории. Если в вашем дистрибутиве исходное правило использует d, не меняйте тип просто ради красоты — лучше оставить подход, выбранный сопровождающими пакета, и изменить только возраст.
После создания файла проверьте синтаксис и примените очистку вручную:
systemd-tmpfiles --clean --prefix=/tmp
journalctl -u systemd-tmpfiles-clean.service -n 100 --no-pager
Если ошибок нет, дальше будет работать timer. Его расписание можно посмотреть так:
systemctl cat systemd-tmpfiles-clean.timer
systemctl list-timers systemd-tmpfiles-clean.timer
/tmp на tmpfs: удобно, но не всегда бесплатно
Отдельная тема — размещение /tmp в tmpfs. В этом режиме временный каталог находится в памяти и swap, а не на диске. Плюсы очевидны: быстрые временные операции, автоматическое исчезновение данных после перезагрузки, меньше мелкой записи на диск. Но на небольшом VDS tmpfs может внезапно начать конкурировать за RAM с базой, PHP-FPM, Redis или Java-приложением.
Проверить, где смонтирован /tmp, можно так:
findmnt /tmp
df -h /tmp
mount | grep ' /tmp '
Если /tmp уже в tmpfs, очистка по возрасту всё равно может быть полезна: не все серверы перезагружаются часто, а временные файлы могут копиться неделями. Но лимит tmpfs нужно задавать осознанно. Иначе большой upload, распаковка архива или временная таблица приложения может съесть значимую часть памяти.
Пример строки в /etc/fstab для отдельного tmpfs с лимитом:
tmpfs /tmp tmpfs mode=1777,nosuid,nodev,size=1G 0 0
Перед использованием noexec подумайте дважды. Это популярная рекомендация для усиления безопасности, но некоторые установщики, сборщики и старые приложения запускают временные исполняемые файлы из /tmp. На веб-сервере noexec может быть уместен, на CI-сборщике — стать источником странных ошибок.

Cache cleanup для приложений: лучше отдельные правила, а не общий пылесос
Хорошая практика — не складывать всё в общий /tmp, если у приложения есть собственный кэш. Для веб-проекта удобнее иметь отдельные каталоги: /var/cache/site, /var/lib/site/tmp, /srv/site/shared/cache. Тогда можно задать разные сроки хранения и исключения.
Например, для PHP-приложения можно разделить кэш шаблонов и временные файлы загрузок:
# /etc/tmpfiles.d/site-cache.conf
d /srv/example/shared/cache 0775 www-data www-data 30d -
d /srv/example/shared/tmp 0770 www-data www-data 1d -
x /srv/example/shared/cache/preload - - - - -
На RHEL-based системах пользователь веб-сервера часто называется apache или используется отдельный пользователь PHP-FPM-пула. Тогда правило будет отличаться владельцем:
# /etc/tmpfiles.d/site-cache.conf
d /srv/example/shared/cache 0775 apache apache 30d -
d /srv/example/shared/tmp 0770 apache apache 1d -
x /srv/example/shared/cache/preload - - - - -
Не копируйте имена пользователей механически. Проверьте, от кого работает процесс:
ps -eo user,group,comm | grep -E 'php-fpm|nginx|apache|httpd'
Для Node.js, Python, Java и Go-сервисов логика такая же: отдельный каталог, отдельный владелец, отдельный срок хранения. Это особенно удобно, когда сервис запускается через systemd и вы заранее знаете пользователя в параметре User unit-файла. Если у вас PHP-проект активно использует Redis или Memcached, отдельно посмотрите, как устроить кэширование PHP-приложений через Memcached и Redis: файловая очистка и in-memory кэш решают разные задачи и хорошо дополняют друг друга.
Как не удалить лишнее: правила безопасности
systemd-tmpfiles безопаснее случайного rm -rf, но это не магическая защита от неверной политики. Если вы напишете рекурсивное удаление важного пути, система постарается выполнить именно это. Поэтому подходите к правилам как к инфраструктурному коду.
- Не начинайте с
R. Для cache cleanup чаще достаточноdилиeс возрастом. - Не задавайте слишком маленький
Ageдля каталогов, где приложения держат долгие операции: загрузки, экспорт отчётов, временные файлы резервного копирования. - Не чистите каталоги баз данных и очередей через tmpfiles, если продукт сам управляет retention.
- Не меняйте права системного
/tmp: там почти всегда нужен режим1777со sticky bit. - Сначала тестируйте правило на отдельном каталоге с копией структуры.
Sticky bit в режиме 1777 важен: он позволяет разным пользователям создавать файлы в /tmp, но запрещает удалять чужие файлы обычным пользователям. Если случайно поставить 0777, вы ослабите изоляцию между пользователями. Если поставить слишком строгий режим, сломаются приложения, ожидающие стандартное поведение /tmp.
Проверить права можно так:
stat -c '%A %a %U %G %n' /tmp /var/tmp
Ожидаемый режим для классического /tmp — 1777. В символьном виде это часто выглядит как drwxrwxrwt, где последняя t и есть sticky bit.
Наблюдение и диагностика: почему место не освободилось
После очистки бывает неприятный сюрприз: файлы удалены, а df показывает прежнее использование диска. Частая причина — файл удалён из каталога, но процесс продолжает держать его открытым. Место освободится только после закрытия дескриптора или перезапуска процесса.
Найти такие файлы можно командой:
lsof +L1
Если lsof не установлен, поставьте пакет из репозитория.
Debian/Ubuntu
apt update
apt install lsof
AlmaLinux/Rocky Linux/CentOS Stream/Oracle Linux/Fedora
dnf install lsof
Также полезно сравнивать df и du. df показывает занятое место на файловой системе, а du суммирует видимые файлы. Большая разница часто указывает на удалённые, но открытые файлы, точки монтирования или особенности файловой системы.
df -h /tmp
du -shx /tmp
Журнал запусков tmpfiles смотрите через systemd:
journalctl -u systemd-tmpfiles-clean.service --since today --no-pager
Если правило не сработало, проверьте четыре вещи: включён ли timer, видит ли systemd ваш файл в /etc/tmpfiles.d, нет ли конфликтующего правила, действительно ли файлы старше заданного Age. В спорных случаях временно создайте тестовый каталог и файл с искусственно старым временем изменения:
mkdir -p /var/cache/tmpfiles-test
touch -d '10 days ago' /var/cache/tmpfiles-test/old-file
Затем добавьте отдельное правило для теста и запустите очистку только по этому префиксу. Такой подход безопаснее, чем экспериментировать на каталоге с реальными данными.
Когда всё-таки нужен cron или отдельный systemd timer
systemd-tmpfiles хорошо подходит для файловой политики: создать путь, назначить права, удалить старое содержимое. Но он не должен заменять всю бизнес-логику приложения. Если очистка требует запросов к базе, проверки статуса задач, отправки метрик, удаления связанных объектов в S3-совместимом хранилище или сложной сортировки по размеру, лучше писать отдельную утилиту и запускать её через systemd timer.
Граница простая: если правило можно выразить как «в этом каталоге удалять старше N дней, кроме этих путей» — используйте tmpfiles.d. Если нужно «удалить только завершённые экспорты, которые отмечены в базе и не скачивались пользователем» — это задача приложения.
Даже в таком случае лучше не возвращаться к голому cron. Systemd timer даёт журналирование, зависимости, ограничения ресурсов, единый способ диагностики и понятный статус. Cron остаётся рабочим инструментом, но для серверов на systemd всё чаще удобнее описывать периодические задачи в unit-файлах.
Короткий чек-лист внедрения
- Проверьте текущие правила:
systemd-tmpfiles --cat-config. - Проверьте timer:
systemctl list-timers systemd-tmpfiles-clean.timer. - Не правьте пакетные файлы в
/usr/lib/tmpfiles.dили/lib/tmpfiles.d. - Для своих приложений создавайте отдельные файлы в
/etc/tmpfiles.d. - Для
/tmpсохраняйте режим1777и системные исключения. - Сначала запускайте очистку с
--prefix, чтобы не затронуть лишние пути. - После очистки проверяйте журнал и открытые удалённые файлы через
lsof +L1.
В итоге systemd-tmpfiles даёт аккуратную, предсказуемую и документированную очистку временных файлов. Вместо ночного rm -rf, который живёт в crontab и пугает следующего администратора, вы получаете понятные правила tmpfiles.d, штатный systemd timer и нормальную диагностику через journal. Для VDS это особенно ценно: диск обычно ограничен, простой из-за переполненного /tmp неприятен, а хорошая политика cache cleanup экономит и место, и время на аварийные разборы.


