Run one safe storage cleanup pass after the first interactive Bash shell of each day.
The service manages OpenCode session data, system package updates, selected user caches, and journal size reporting. It supports user settings in an environment file.
On macOS the service is named
macos-cleanup-serviceand installs a launchd job instead of a systemd unit. See the macOS port.
Run:
./scripts/installThe installer:
- Installs a systemd user service.
- Adds a small startup hook to
~/.bashrc. - Creates
~/.config/linux-cleanup-service/environment. - Installs a root journald drop-in with
sudo. - Installs a root sudoers drop-in that allows the daily update commands without a password.
- Rotates and vacuums existing system journal files.
The installer asks for the sudo password when it needs it. Open a new Bash shell after installation. The first shell starts the job in the background. The shell prints the last result.
On macOS the installer instead installs the launchd job
com.user.macos-cleanup-service, adds the hook to ~/.zshrc, and creates
~/Library/Application Support/macos-cleanup-service/environment from
config/macos.env. It skips the journal and sudoers drop-ins.
Edit:
~/.config/linux-cleanup-service/environment
The default values include:
SESSION_RETENTION_DAYS=7
TEMP_ROOT=/tmp/opencode
TEMP_RETENTION_DAYS=7
UV_CACHE_MAX_SIZE=1GiB
BRAVE_CACHE_MAX_SIZE=2GiB
JOURNAL_WARN_SIZE=100MiB
OPENCODE_DB_WARN_SIZE=2GiB
APT_UPDATES_ENABLED=true
SNAP_UPDATES_ENABLED=true
The service reads this file once at the start of each run. Process environment variables take precedence over the file.
The size parser accepts B, KB, KiB, MB, MiB, GB, and GiB.
The service performs these actions once per calendar day:
- Run
sudo -n apt-get update. - Run
sudo -n apt-get upgrade -ywhen the APT update succeeds. - Run
sudo -n snap refresh. - Upgrade OpenCode with its standalone installer.
- Measure the Brave, UV, and npm caches.
- Run
uv cache pruneabove the UV threshold. - Clear the Brave cache above its threshold when Brave is stopped.
- Skip Brave cleanup and report a warning when Brave is running.
- Back up the OpenCode database before session deletion.
- Delete session trees inactive for seven days.
- Checkpoint SQLite and vacuum free pages when safe.
- Rotate OpenCode backups and logs.
- Measure system and user journal usage.
On macOS, the daily pass also checks Node.js 24.x and Bun stable releases. It
waits seven days before it accepts a release and records each result under
runtime_updates. It also runs brew cleanup -s to remove stale downloads and
old package versions. It does not change runtime links until signature, hash,
and version checks pass.
APT and Snap updates use non-interactive sudo. The installer creates the
root file /etc/sudoers.d/90-linux-cleanup-service, which allows exactly
these commands and the system journal rotate and vacuum operations without a
password. If sudo still needs a password, the service records a warning and
continues the remaining cleanup.
Regenerate or inspect the sudoers rule:
python3 ~/.local/libexec/linux-cleanup-service/config.py sudoers-configIf the installer could not use sudo, apply the rule manually:
tmp="$(mktemp)"
python3 "$HOME/.local/libexec/linux-cleanup-service/config.py" sudoers-config > "$tmp"
sudo install -D -m 440 -o root -g root "$tmp" /etc/sudoers.d/90-linux-cleanup-service
rm -f "$tmp"The rule names the current user and grants no shell. It covers only
apt-get update, apt-get upgrade -y, snap refresh, journalctl --rotate,
and journalctl --vacuum-size=<limit>. Regenerate the rule after changing
JOURNAL_SYSTEM_MAX_USE, because the vacuum size is part of the command.
Account-level runs skip tasks and metrics that the account cannot inspect or
execute, such as system journal details or root-only package updates. These
tasks appear in the skipped field of the JSON status record and do not make a
successful cleanup a warning. Failures on resources owned by the account still
appear as warnings or errors.
The service never deletes OpenCode sessions without a successful database
backup. It uses opencode session delete, not direct event-table deletion.
Print the current status:
python3 ~/.local/libexec/linux-cleanup-service/status.pyInspect the full record:
python3 ~/.local/libexec/linux-cleanup-service/status.py --jsonRun a non-mutating inspection:
python3 ~/.local/libexec/linux-cleanup-service/cleanup.py --dry-run --forceRun a manual cleanup outside the daily gate:
systemctl --user start linux-cleanup.serviceThe default root drop-in uses:
[Journal]
Compress=yes
SystemMaxUse=80M
SystemMaxFileSize=8M
RuntimeMaxUse=16M
RuntimeMaxFileSize=8MThe 80 MiB persistent limit keeps journal usage below the requested 100 MiB target. Active files can cause a small temporary excess.
If the installer cannot use sudo, apply the root setting manually:
tmp="$(mktemp)"
python3 "$HOME/.local/libexec/linux-cleanup-service/config.py" journal-config > "$tmp"
sudo install -D -m 644 "$tmp" /etc/systemd/journald.conf.d/90-linux-cleanup-service.conf
rm -f "$tmp"
sudo systemctl restart systemd-journald
sudo journalctl --rotate
vacuum_size="$(python3 "$HOME/.local/libexec/linux-cleanup-service/config.py" journal-vacuum-size)"
sudo journalctl --vacuum-size="$vacuum_size"Run:
./scripts/uninstallThe command removes installed code, the user unit, and the Bash hook. It keeps configuration, state, and backups.
Run the local checks:
./scripts/verifyDo not place live database backups or status files in this repository.