Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save michele-tn/4418155343e8ea6a1bd08c666758513c to your computer and use it in GitHub Desktop.

Select an option

Save michele-tn/4418155343e8ea6a1bd08c666758513c to your computer and use it in GitHub Desktop.
Bash script for deep Ubuntu system cleanup with an interactive safety prompt. Supports Safe and Aggressive profiles to balance disk recovery and log preservation. Removes Snap permanently, purges leftover kernels, optimizes APT behavior, vacuums journald logs, and optionally runs BleachBit with shredding. Designed for Ubuntu 22.04 (Jammy), VPS, …

Ubuntu Cleanup Script (Safe / Aggressive Modes)

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.


Contents of this Gist

  • clean_ubuntu.sh
    The cleanup automation script.

  • README.md
    This documentation.


High-level behavior

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:

Safe mode = YES

Designed to minimize the risk of removing useful logs or caches.

  • Journald cleanup: moderate
    • journalctl --vacuum-time=7d
  • BleachBit:
    • Runs without --shred by default
    • Shredding can still be forced via --force-shred
  • APT cleanup, Snap removal, kernel cleanup:
    • Always executed (safe operations)

Aggressive mode = NO

Matches the previously executed manual cleanup workflow.

  • Journald cleanup:
    • journalctl --vacuum-time=1
  • BleachBit:
    • Runs with --clean --all --shred
  • APT lists wiped and rebuilt
  • Maximum disk space recovery

What the script does (step by step)

1) APT cleanup

  • apt-get autoremove --purge
  • apt-get clean
  • apt-get autoclean

2) Purge leftover kernel configuration (rc)

  • Purges all linux-image-* packages in rc state (already removed but with configuration remnants).

3) Remove Snap completely

  • Stops and disables snapd services and sockets (best effort)
  • Purges snapd
  • Deletes Snap directories:
    • /snap
    • /var/snap
    • /var/lib/snapd

4) Prevent Snap from returning

Creates an APT pin: /etc/apt/preferences.d/nosnap

This blocks future installation of snapd.


5) Persistent APT size optimizations

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.


6) Journald cleanup (profile-dependent)

  • Safe mode: keeps recent logs (7 days)
  • Aggressive mode: almost complete reset (1 second)

7) APT list wipe and rebuild

  • Deletes: /var/lib/apt/lists/*

  • Rebuilds package metadata immediately: apt-get update

This reclaims disk space while keeping APT functional.


8) Final command chain (always executed)

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.


Usage

  1. Save the script as:
    clean_ubuntu.sh
  2. Make it executable:
    chmod +x clean_ubuntu.sh
    
    
  3. Run it:
    sudo ./clean_ubuntu.sh
    
    

Optional flags

--force-shred Forces BleachBit shredding even when Safe mode is enabled.

Example:

sudo ./clean_ubuntu.sh --force-shred  

Requirements

  • 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

Important notes

  • 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.

Intended use cases

  • VPS / cloud instances
  • Minimal Ubuntu systems
  • Lab or disposable environments
  • Disk-constrained machines
  • Users who want explicit control over cleanup aggressiveness

Disclaimer

Use at your own risk. You are responsible for ensuring that this cleanup strategy aligns with your operational, auditing, and compliance requirements.

Script: clean_ubuntu.sh

#!/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 "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment