Коли диск раптово заповнюється, система починає гальмувати, сервіси падають, а ви витрачаєте час на ручне прибирання. Давайте автоматизуємо це: зробимо акуратний bash-скрипт, який щодня перевіряє використання диска, надсилає короткий звіт і, за потреби, виконує безпечну очистку місця на диску у /tmp, /var/tmp та стискає журнали. Все це — через systemd service + systemd timer. Це проста й надійна автоматизація задач з використанням cron та systemd timers (але ми оберемо systemd, бо він крутіший 🧹).

Що ми побудуємо

Скрипт робить таке:

  • міряє відсоток зайнятого місця для вказаних розділів (за замовчуванням "/");
  • якщо поріг перевищено — прибирає старі файли з /tmp і /var/tmp (старші за 7 днів), вакуумує journald (наприклад, до 7 днів),
  • пише звіт у системний журнал і в лог-файл, а за наявності mail — надсилає e-mail на root;
  • працює щодня за розкладом через systemd timer.

Покроково: How-to

1) Створюємо bash-скрипт

Скрипт безпечний: зупиняється на помилках, має DRY_RUN для тестів, не чіпає нічого поза /tmp і /var/tmp, та акуратно чистить журнали.

sudo tee /usr/local/bin/disk-report-clean.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

# Налаштування (можна перевизначити через змінні середовища)
THRESHOLD="${THRESHOLD:-85}"       # % заповнення, після якого чистимо
PARTITIONS=(${PARTITIONS:-/})       # які точки монтування перевіряти
J_VACUUM="${J_VACUUM:-7d}"         # як довго зберігати журнали systemd
LOG="/var/log/disk-auto-maint.log"
DRY_RUN="${DRY_RUN:-0}"            # 1 = лише показати дії, не виконувати

run() {
  if [[ "$DRY_RUN" -eq 1 ]]; then
    echo "[DRY] $*"
  else
    eval "$@"
  fi
}

pct_used() { df -P "$1" | awk 'NR==2 {gsub("%","",$5); print $5}'; }
kb_used()  { df -kP "$1" | awk 'NR==2 {print $3}'; }

notify() {
  local msg="$1"
  printf '%s\n' "$msg" | tee -a "$LOG" >/dev/null || true
  logger -t disk-maintenance -- "$msg" || true
  if command -v mail >/dev/null 2>&1; then
    printf '%s\n' "$msg" | mail -s "Disk report on $(hostname -f 2>/dev/null || hostname)" root || true
  fi
}

cleanup_once() {
  # Прибираємо /tmp і /var/tmp (старше 7 днів і >1M), порожні каталоги
  for d in /tmp /var/tmp; do
    [[ -d "$d" ]] || continue
    run "find '$d' -xdev -type f -mtime +7 -size +1M -delete"
    run "find '$d' -xdev -mindepth 1 -type d -empty -delete"
  done
  # Вакуумуємо журнали systemd до заданого періоду
  if command -v journalctl >/dev/null 2>&1; then
    run "journalctl --vacuum-time=$J_VACUUM"
  fi
  # Якщо є systemd-tmpfiles — дозачищаємо згідно політик
  if command -v systemd-tmpfiles >/dev/null 2>&1; then
    run "systemd-tmpfiles --clean"
    run "systemd-tmpfiles --remove"
  fi
}

main() {
  [[ $EUID -eq 0 ]] || { echo "Цей скрипт слід запускати від root"; exit 1; }

  local over=0
  local report="Disk maintenance report $(date -Is)\n"
  report+="Threshold: ${THRESHOLD}% | Partitions: ${PARTITIONS[*]} | DRY_RUN=${DRY_RUN}\n\n"

  for mp in "${PARTITIONS[@]}"; do
    local p u1 u2
    p=$(pct_used "$mp")
    u1=$(kb_used "$mp")
    report+="Before: $mp used=$(printf '%.1f' "$(echo "$u1/1024" | bc -l)") MB (${p}%)\n"

    if (( p >= THRESHOLD )); then
      over=1
      cleanup_once
      u2=$(kb_used "$mp")
      local freed=$(( u1 - u2 ))
      report+="Cleanup on $mp: freed=$(printf '%.1f' "$(echo "$freed/1024" | bc -l)") MB\n"
      p=$(pct_used "$mp")
      report+="After:  $mp now ${p}%\n\n"
    else
      report+="No cleanup needed for $mp\n\n"
    fi
  done

  if (( over == 0 )); then
    report+="All partitions below ${THRESHOLD}% — just reporting."
  fi

  notify "$report"
}

main
EOF
sudo chmod +x /usr/local/bin/disk-report-clean.sh

2) systemd service і timer

Сервіс запускає скрипт, а таймер — по розкладу (щодня о 09:00 з невеличкою випадковою затримкою ⏰).

# Сервіс
sudo tee /etc/systemd/system/disk-maintenance.service >/dev/null <<'EOF'
[Unit]
Description=Disk report and cleanup (bash)
After=local-fs.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/disk-report-clean.sh
Nice=10
IOSchedulingClass=best-effort
IOSchedulingPriority=6

[Install]
WantedBy=multi-user.target
EOF

# Таймер
sudo tee /etc/systemd/system/disk-maintenance.timer >/dev/null <<'EOF'
[Unit]
Description=Run disk report/cleanup daily

[Timer]
OnCalendar=09:00
Persistent=true
RandomizedDelaySec=10m
Unit=disk-maintenance.service

[Install]
WantedBy=timers.target
EOF

# Активуємо
sudo systemctl daemon-reload
sudo systemctl enable --now disk-maintenance.timer

# Перевіряємо
systemctl status disk-maintenance.timer
systemctl list-timers --all | grep disk-maintenance

3) Тестуємо вручну

# Пробний запуск (без видалення)
sudo DRY_RUN=1 /usr/local/bin/disk-report-clean.sh

# Реальний запуск
sudo /usr/local/bin/disk-report-clean.sh

# Логи і звіт
journalctl -u disk-maintenance.service -n 50 --no-pager
sudo tail -n 100 /var/log/disk-auto-maint.log

Альтернативні способи

Варіант через cron

Якщо любите cron — теж ок. Але systemd timers надійніші та мають Persistent=true (завдання не пропускається, якщо ПК був вимкнений).

# Щодня о 09:00 від root
echo "0 9 * * * root /usr/local/bin/disk-report-clean.sh" | sudo tee /etc/cron.d/disk-maintenance
sudo systemctl restart cron || sudo systemctl restart crond

Точніше керування /tmp через tmpfiles.d

Для більш керованого прибирання використайте конфіг systemd-tmpfiles:

sudo tee /etc/tmpfiles.d/my-tmp.conf >/dev/null <<'EOF'
# Очищати вміст /tmp старший за 7 днів, права та власники збережено
q /tmp     1777 root root 7d
q /var/tmp 1777 root root 30d
EOF

# Застосувати негайно
sudo systemd-tmpfiles --clean
sudo systemd-tmpfiles --remove

GUI-спосіб (через Cockpit)

Якщо вам зручніше у веб-інтерфейсі, поставте Cockpit (є в репозиторіях більшості дистрибутивів). У веб-UI є менеджер systemd: ви зможете створити service і timer, запускати вручну, дивитися логи і статуси. Суть та сама, що й у нашому how-to, просто без терміналу.

FAQ

Чому пошта не приходить?

Скрипт надсилає e-mail лише якщо команда mail доступна. Встановіть пакет з mailx або налаштуйте локальний MTA (наприклад, msmtp-mta). Навіть без пошти звіт завжди лежить у journal і у /var/log/disk-auto-maint.log.

"Permission denied" або не видаляються файли

Сервіс має виконуватись від root. Переконайтеся, що /usr/local/bin/disk-report-clean.sh має +x, а unit-файл не змінює користувача.

Таймер не спрацьовує

Перевірте: systemctl status disk-maintenance.timer, чи є наступний запуск у systemctl list-timers, і чи вірно заданий OnCalendar. Для дебагу: systemd-analyze calendar "09:00". Не забудьте daemon-reload після змін.

Можна змінити поріг і розділи?

Так: змінні THRESHOLD і PARTITIONS. Наприклад: PARTITIONS="/ /var" THRESHOLD=80. Додайте їх у Environment= у service або експортуйте перед запуском.

Це безпечно для журналів?

Так, journalctl --vacuum-time=7d зберігає журнали не старші за 7 днів. Для постійної межі розміру можна вказати SystemMaxUse у /etc/systemd/journald.conf і перезапустити systemd-journald.

А якщо хочу чистити тільки тимчасові файли й ніколи не чіпати журнали?

Виставте J_VACUUM=0 або закоментуйте рядок з journalctl у скрипті.

Як зробити користувацький (не root) режим?

Запускайте як user timer: systemctl --user enable --now my.timer і приберіть дії, що потребують root. Але /tmp і /var/tmp системні — без root не почистите все.

Порада від Kernelka

Почніть з DRY_RUN=1 на кілька днів — подивіться, що саме буде видалено і скільки місця звільняється. Не ставте поріг нижче 75% і не намагайтеся чистити все підряд — цільова очистка місця на диску у /tmp, /var/tmp та журнали — це оптимально. І, звісно, резервні копії — ваші найкращі друзі 💾.

Підсумок

  • Маєте надійну автоматизацію задач для контролю місця.
  • systemd cron та systemd timers забезпечують стабільний розклад і логування.
  • Скрипт відправляє звіти і робить безпечне прибирання /tmp, /var/tmp, журналів.
  • Гнучкі змінні: THRESHOLD, PARTITIONS, J_VACUUM, DRY_RUN.
  • Альтернативи: cron та tmpfiles.d; за бажанням — GUI через Cockpit.