This Gist contains a single Bash script that automates a deep cleanup of an Ubuntu system (Jammy-friendly).
The script includes an interactive safety prompt that adapts the cleanup strategy depending on how the system is used, allowing you to choose between a Safe and an Aggressive cleanup profile.
Everything is intended primarily for server / VPS / minimal Ubuntu systems.
-
clean_ubuntu.sh
The cleanup automation script. -
README.md
This documentation.
At runtime, the script asks:
Is this machine used for development or workflows where logs and caches should be preserved?
Based on your answer, it selects one of two profiles:
Designed to minimize the risk of removing useful logs or caches.
- Journald cleanup: moderate
journalctl --vacuum-time=7d
- BleachBit:
- Runs without
--shredby default - Shredding can still be forced via
--force-shred
- Runs without
- APT cleanup, Snap removal, kernel cleanup:
- Always executed (safe operations)
Matches the previously executed manual cleanup workflow.
- Journald cleanup:
journalctl --vacuum-time=1
- BleachBit:
- Runs with
--clean --all --shred
- Runs with
- APT lists wiped and rebuilt
- Maximum disk space recovery
apt-get autoremove --purgeapt-get cleanapt-get autoclean
- Purges all
linux-image-*packages inrcstate (already removed but with configuration remnants).
- Stops and disables
snapdservices and sockets (best effort) - Purges
snapd - Deletes Snap directories:
/snap/var/snap/var/lib/snapd
Creates an APT pin:
/etc/apt/preferences.d/nosnap
This blocks future installation of snapd.
Creates the following configuration files:
- Disable translations:
/etc/apt/apt.conf.d/99notranslations
Acquire::Languages "none";
- Disable recommends and suggests:
/etc/apt/apt.conf.d/99norecommends
APT::Install-Recommends "false";
APT::Install-Suggests "false";
These help prevent unnecessary growth of /usr and /var.
- Safe mode: keeps recent logs (7 days)
- Aggressive mode: almost complete reset (1 second)
-
Deletes:
/var/lib/apt/lists/* -
Rebuilds package metadata immediately:
apt-get update
This reclaims disk space while keeping APT functional.
The script finally runs:
clear
sudo bleachbit --clean --all [--shred depending on mode]
df -h
sudo du -xh --max-depth=1 / | sort -hr
If BleachBit is not installed, the script prints a warning and skips that step gracefully.
- Save the script as:
clean_ubuntu.sh
- Make it executable:
chmod +x clean_ubuntu.sh - Run it:
sudo ./clean_ubuntu.sh
--force-shred
Forces BleachBit shredding even when Safe mode is enabled.
Example:
sudo ./clean_ubuntu.sh --force-shred
- Ubuntu (tested workflow on 22.04 / Jammy)
- Root privileges (sudo)
- Optional:
bleachbit(required only for the BleachBit step)
Install BleachBit if needed:
sudo apt-get update
sudo apt-get install -y bleachbit
- BleachBit shredding is destructive and can take time.
- This is why it is disabled by default in Safe mode.
- Aggressive journald vacuuming removes almost all logs.
- Suitable for VPS / cloud images, not for audit-heavy or forensic systems.
- APT list wiping forces metadata re-downloads.
- The script rebuilds them automatically.
- Snap removal is permanent unless the APT pin is manually removed.
- VPS / cloud instances
- Minimal Ubuntu systems
- Lab or disposable environments
- Disk-constrained machines
- Users who want explicit control over cleanup aggressiveness
Use at your own risk. You are responsible for ensuring that this cleanup strategy aligns with your operational, auditing, and compliance requirements.
#!/usr/bin/env bash
set -euo pipefail
# ==============================================================================
# Ubuntu Cleanup Script (Safe / Aggressive Modes)
#
# Automates:
# - APT cleanup
# - Purge "rc" linux-image leftovers
# - Remove snapd + delete snap directories + APT pin to prevent reinstall
# - Journald vacuum (safe OR aggressive depending on interactive prompt)
# - Persistent APT optimizations (no translations, no recommends/suggests)
# - Final command chain:
# clear ; sudo bleachbit --clean --all --shred ; df -h ; sudo du -xh --max-depth=1 / | sort -hr
#
# Safe mode:
# - journald vacuum keeps 7 days
# - bleachbit runs without --shred (unless --force-shred is provided)
#
# Aggressive mode:
# - journald vacuum time = 1 second
# - bleachbit runs with --shred
# ==============================================================================
log() { echo -e "\n[$(date +'%F %T')] $*"; }
require_root() {
if [[ "${EUID}" -ne 0 ]]; then
echo "Please run as root: sudo $0"
exit 1
fi
}
has_cmd() { command -v "$1" >/dev/null 2>&1; }
ask_yes_no() {
# Usage: ask_yes_no "Question" "default"
# default: Y or N
local question="$1"
local def="${2:-N}"
local prompt ans
if [[ "$def" == "Y" ]]; then
prompt="[Y/n]"
else
prompt="[y/N]"
fi
while true; do
read -r -p "${question} ${prompt} " ans || true
ans="${ans:-$def}"
case "${ans}" in
Y|y|yes|YES) return 0 ;;
N|n|no|NO) return 1 ;;
*) echo "Please answer yes or no." ;;
esac
done
}
apt_update_best_effort() {
log "APT: updating package lists (best effort)"
apt-get update -y || true
}
apt_cleanup() {
log "APT: autoremove/purge + clean/autoclean"
apt-get autoremove --purge -y || true
apt-get clean -y || true
apt-get autoclean -y || true
}
purge_rc_kernels() {
log "DPKG: purging linux-image packages in 'rc' state (config leftovers)"
mapfile -t rc_kernels < <(dpkg -l | awk '/^rc linux-image/ {print $2}' || true)
if (( ${#rc_kernels[@]} > 0 )); then
apt-get purge -y "${rc_kernels[@]}" || true
else
echo "No 'rc' linux-image packages found."
fi
}
remove_snapd_and_cleanup_dirs() {
log "SNAP: removing snapd (if installed), stopping sockets/services, deleting snap dirs"
if dpkg -l | awk '{print $2}' | grep -qx 'snapd'; then
systemctl stop snapd.service 2>/dev/null || true
systemctl stop snapd.socket 2>/dev/null || true
systemctl disable snapd.service 2>/dev/null || true
systemctl disable snapd.socket 2>/dev/null || true
apt-get purge -y snapd || true
else
echo "snapd is not installed."
fi
rm -rf /snap /var/snap /var/lib/snapd 2>/dev/null || true
}
pin_snapd() {
log "APT: pinning snapd to prevent reinstall"
install -d /etc/apt/preferences.d
cat > /etc/apt/preferences.d/nosnap <<'EOF'
Package: snapd
Pin: release a=*
Pin-Priority: -10
EOF
}
apt_optimize_no_translations() {
log "APT: disabling translation downloads (Acquire::Languages \"none\")"
install -d /etc/apt/apt.conf.d
cat > /etc/apt/apt.conf.d/99notranslations <<'EOF'
Acquire::Languages "none";
EOF
}
apt_optimize_no_recommends() {
log "APT: disabling Recommends/Suggests installs"
install -d /etc/apt/apt.conf.d
cat > /etc/apt/apt.conf.d/99norecommends <<'EOF'
APT::Install-Recommends "false";
APT::Install-Suggests "false";
EOF
}
vacuum_journald() {
local safe_mode="$1" # "true" or "false"
if [[ "$safe_mode" == "true" ]]; then
log "JOURNALD: vacuuming logs (Safe mode: keeping 7 days)"
journalctl --vacuum-time=7d 2>/dev/null || true
else
log "JOURNALD: vacuuming logs (Aggressive mode: --vacuum-time=1s)"
journalctl --vacuum-time=1 2>/dev/null || true
fi
}
wipe_apt_lists_and_rebuild() {
log "APT: wiping /var/lib/apt/lists/* to reclaim space, then rebuilding with apt-get update"
rm -rf /var/lib/apt/lists/* 2>/dev/null || true
apt-get update -y || true
}
run_bleachbit() {
local safe_mode="$1" # "true" or "false"
local force_shred="$2" # "true" or "false"
if ! has_cmd bleachbit; then
echo "WARNING: 'bleachbit' is not installed. Skipping bleachbit step."
echo "Install it if needed, then re-run:"
echo " sudo apt-get update && sudo apt-get install -y bleachbit"
return 0
fi
if [[ "$safe_mode" == "true" && "$force_shred" != "true" ]]; then
echo "Safe mode is ON: running BleachBit without '--shred'."
echo "To force shredding, re-run with: --force-shred"
sudo bleachbit --clean --all
else
sudo bleachbit --clean --all --shred
fi
}
final_requested_chain() {
local safe_mode="$1"
local force_shred="$2"
log "FINAL: running requested command chain"
clear
run_bleachbit "$safe_mode" "$force_shred"
df -h
sudo du -xh --max-depth=1 / | sort -hr
}
main() {
require_root
local force_shred="false"
if [[ "${1:-}" == "--force-shred" ]]; then
force_shred="true"
fi
log "Interactive prompt: choose cleanup profile"
local safe_mode="false"
if ask_yes_no "Is this machine used for development/workflows where logs and caches should be preserved?" "Y"; then
safe_mode="true"
log "Mode selected: SAFE"
else
safe_mode="false"
log "Mode selected: AGGRESSIVE"
fi
apt_update_best_effort
apt_cleanup
purge_rc_kernels
remove_snapd_and_cleanup_dirs
pin_snapd
# Persistent APT slimming options (generally safe):
apt_optimize_no_translations
apt_optimize_no_recommends
vacuum_journald "$safe_mode"
wipe_apt_lists_and_rebuild
final_requested_chain "$safe_mode" "$force_shred"
}
main "$@"