Build: DietPi OS minimization
- dietpi-postinstall.sh: first-boot setup from DietPi minimal with only needed packages (Python, JACK, cmake, GPIO libs, I2C tools) - minimize.sh: removes unused packages, systemd services, and kernel modules with --dry-run and --aggressive modes; saves before/after profiles - build-minimal-image.sh: produces flashable SD card image from scratch (download DietPi + provision) or from a running SD card device - baseline-footprint.md: documents before/after RAM, disk, boot time targets and verification checklist Closes t_38b86d31 — DietPi OS minimization
This commit is contained in:
Executable
+328
@@ -0,0 +1,328 @@
|
||||
#!/usr/bin/env bash
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# Pi Multi-FX Pedal — System Minimizer
|
||||
#
|
||||
# Profiles current resource usage, then strips away everything not needed
|
||||
# for real-time guitar DSP. Run AFTER dietpi-postinstall.sh (or on any
|
||||
# fresh DietPi/RPi OS install).
|
||||
#
|
||||
# This script is idempotent — safe to run multiple times. The uninstall
|
||||
# list is documented below; review before running in production.
|
||||
#
|
||||
# Usage:
|
||||
# sudo ./minimize.sh [--dry-run] [--aggressive]
|
||||
#
|
||||
# --dry-run Only profile and list, don't remove anything
|
||||
# --aggressive Also remove firmware, locales, docs, Python test packages
|
||||
#
|
||||
# Target: < 60 MB idle RAM, < 500 MB disk
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
DRY_RUN=false
|
||||
AGGRESSIVE=false
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--dry-run) DRY_RUN=true ;;
|
||||
--aggressive) AGGRESSIVE=true ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ── Colour helpers ───────────────────────────────────────────────────────────
|
||||
info() { printf "\e[34m[INFO] %s\e[0m\n" "$*"; }
|
||||
ok() { printf "\e[32m[ OK ] %s\e[0m\n" "$*"; }
|
||||
warn() { printf "\e[33m[WARN] %s\e[0m\n" "$*"; }
|
||||
err() { printf "\e[31m[FAIL] %s\e[0m\n" "$*"; }
|
||||
|
||||
# ── Root check ───────────────────────────────────────────────────────────────
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
err "This script must be run as root (sudo ./minimize.sh)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
info "========== Pi Multi-FX Pedal — System Minimizer =========="
|
||||
[[ "$DRY_RUN" == true ]] && warn "DRY RUN MODE — no changes will be made"
|
||||
echo ""
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# Step 0: Baseline — record current state
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
info "Step 0: Recording baseline..."
|
||||
|
||||
BASELINE_FILE="/tmp/pedal-minimize-baseline-$(date +%s).txt"
|
||||
{
|
||||
echo "=== Baseline: $(date -Iseconds) ==="
|
||||
echo ""
|
||||
echo "--- Memory ---"
|
||||
free -m
|
||||
echo ""
|
||||
echo "--- Disk ---"
|
||||
df -h /
|
||||
echo ""
|
||||
echo "--- Top 30 packages by installed size ---"
|
||||
dpkg-query -Wf '${Installed-Size}\t${Package}\n' 2>/dev/null | sort -rn | head -30
|
||||
echo ""
|
||||
echo "--- Enabled systemd services ---"
|
||||
systemctl list-units --type=service --state=running --no-legend 2>/dev/null | awk '{print $1}' | sort
|
||||
echo ""
|
||||
echo "--- Boot time (systemd-analyze) ---"
|
||||
systemd-analyze 2>/dev/null || echo "not available"
|
||||
echo ""
|
||||
echo "--- Kernel modules loaded ---"
|
||||
lsmod | sort
|
||||
} > "$BASELINE_FILE"
|
||||
|
||||
ok "Baseline saved to $BASELINE_FILE"
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# Step 1: Remove unnecessary packages
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
info "Step 1: Removing unnecessary packages..."
|
||||
|
||||
# ── Packages we KNOW we don't need on a headless audio pedal ──────────────
|
||||
PACKAGES_TO_PURGE=(
|
||||
# Bluetooth (no BT on a pedal)
|
||||
bluez bluez-tools bluez-firmware pi-bluetooth
|
||||
# Printing
|
||||
cups cups-bsd cups-client cups-common cups-core-drivers cups-daemon cups-filters cups-ppdc cups-server-common
|
||||
# Avahi/mDNS (not needed for local audio)
|
||||
avahi-daemon avahi-autoipd avahi-utils
|
||||
# ModemManager
|
||||
modemmanager
|
||||
# Triggerhappy (input daemon — we handle GPIO ourselves)
|
||||
triggerhappy
|
||||
# X11 / display server (headless — OLED is SPI/I2C)
|
||||
xserver-xorg xserver-xorg-core xserver-xorg-legacy x11-common
|
||||
# Desktop environments (shouldn't be installed on minimal DietPi, but just in case)
|
||||
raspberrypi-ui-mods raspberrypi-net-mods
|
||||
lxde lxde-common lxmenu-data lxterminal
|
||||
# Office/libreoffice
|
||||
libreoffice* liblibreoffice*
|
||||
# Web browsers
|
||||
chromium-browser chromium-browser-l10n chromium-codecs-ffmpeg
|
||||
firefox-esr
|
||||
# Games
|
||||
minecraft-pi python3-pgzero
|
||||
# Sound servers that conflict with JACK
|
||||
pulseaudio pulseaudio-utils pulseaudio-module-bluetooth
|
||||
pipewire pipewire-audio pipewire-pulse
|
||||
# WiFi firmware (if wired-only — we prompt below)
|
||||
# Removed conditionally if --aggressive
|
||||
)
|
||||
|
||||
# Actually remove them
|
||||
for pkg in "${PACKAGES_TO_PURGE[@]}"; do
|
||||
if dpkg -l "$pkg" &>/dev/null 2>&1; then
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
info "[DRY RUN] Would purge: $pkg"
|
||||
else
|
||||
apt-get purge -y -qq "$pkg" 2>/dev/null || true
|
||||
ok "Purged: $pkg"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# ── Aggressive mode: remove even more ──────────────────────────────────────
|
||||
if [[ "$AGGRESSIVE" == true ]]; then
|
||||
info "Aggressive mode: removing additional packages..."
|
||||
|
||||
AGGRESSIVE_PACKAGES=(
|
||||
# Firmware (keep only what's needed for boot + I2S)
|
||||
firmware-brcm80211 firmware-atheros firmware-iwlwifi firmware-realtek
|
||||
# Locales (keep only en_US.UTF-8)
|
||||
locales-all language-pack-* language-pack-en-base
|
||||
# Documentation
|
||||
man-db manpages info
|
||||
# Python test packages (not needed at runtime)
|
||||
python3-pytest python3-pytest-asyncio python3-pytest-mock
|
||||
# X11 fonts
|
||||
fonts-* xfonts-*
|
||||
# Unused sound modules for hardware we don't have
|
||||
alsa-base # we use our own /etc/asound.conf
|
||||
# Unused Python packages on DietPi
|
||||
python3-pygame python3-tk
|
||||
)
|
||||
|
||||
for pkg in "${AGGRESSIVE_PACKAGES[@]}"; do
|
||||
if dpkg -l "$pkg" &>/dev/null 2>&1; then
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
info "[DRY RUN] Would purge: $pkg"
|
||||
else
|
||||
apt-get purge -y -qq "$pkg" 2>/dev/null || true
|
||||
ok "Purged: $pkg"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Remove unused locale data
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
localedef --list-archive 2>/dev/null | grep -v en_US | while read -r locale; do
|
||||
localedef --delete-from-archive "$locale" 2>/dev/null || true
|
||||
done
|
||||
ok "Stripped non-English locales"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Clean up
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
apt-get autoremove --purge -y -qq 2>/dev/null || true
|
||||
apt-get autoclean -y -qq 2>/dev/null || true
|
||||
ok "apt autoremove/autoclean complete"
|
||||
fi
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# Step 2: Disable unnecessary systemd services
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
info "Step 2: Disabling unnecessary systemd services..."
|
||||
|
||||
SERVICES_TO_DISABLE=(
|
||||
bluetooth
|
||||
avahi-daemon
|
||||
triggerhappy
|
||||
ModemManager
|
||||
systemd-resolved
|
||||
apt-daily.timer
|
||||
apt-daily-upgrade.timer
|
||||
man-db.timer
|
||||
e2scrub_all.timer
|
||||
fstrim.timer
|
||||
)
|
||||
|
||||
for svc in "${SERVICES_TO_DISABLE[@]}"; do
|
||||
if systemctl is-enabled "$svc" &>/dev/null 2>&1; then
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
info "[DRY RUN] Would disable: $svc"
|
||||
else
|
||||
systemctl disable --now "$svc" 2>/dev/null || true
|
||||
ok "Disabled: $svc"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Mask pulseaudio completely (not just disable)
|
||||
for unit in pulseaudio.service pulseaudio.socket; do
|
||||
if systemctl list-unit-files "$unit" &>/dev/null 2>&1; then
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
info "[DRY RUN] Would mask: $unit"
|
||||
else
|
||||
systemctl mask "$unit" 2>/dev/null || true
|
||||
ok "Masked: $unit"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# Step 3: Remove unused kernel modules
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
info "Step 3: Removing unnecessary kernel modules..."
|
||||
|
||||
BLACKLIST_FILE="/etc/modprobe.d/pedal-blacklist.conf"
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
cat > "$BLACKLIST_FILE" <<'BLACKLIST'
|
||||
# Pi Multi-FX Pedal — blacklist unused kernel modules
|
||||
# Audio subsystems we don't use
|
||||
blacklist snd_bcm2835
|
||||
blacklist snd_usb_audio
|
||||
blacklist snd_usbmidi_lib
|
||||
blacklist snd_rawmidi
|
||||
blacklist snd_seq_device
|
||||
blacklist snd_hwdep
|
||||
blacklist snd_hda_intel
|
||||
blacklist snd_hda_codec
|
||||
blacklist snd_hda_core
|
||||
|
||||
# Bluetooth
|
||||
blacklist bluetooth
|
||||
blacklist btusb
|
||||
blacklist btbcm
|
||||
|
||||
# WiFi (wired-only mode)
|
||||
blacklist brcmfmac
|
||||
blacklist brcmutil
|
||||
|
||||
# Unused input devices
|
||||
blacklist joydev
|
||||
blacklist uvcvideo
|
||||
blacklist videobuf2_v4l2
|
||||
|
||||
# Unused storage
|
||||
blacklist r8152 # Realtek USB NIC
|
||||
blacklist xhci_pci # USB 3.0 (not needed on headless pedal)
|
||||
BLACKLIST
|
||||
ok "Kernel module blacklist: $BLACKLIST_FILE"
|
||||
else
|
||||
info "[DRY RUN] Would write: $BLACKLIST_FILE"
|
||||
fi
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# Step 4: Profile and measure
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
info "Step 4: Profiling post-minimization footprint..."
|
||||
|
||||
AFTER_FILE="/tmp/pedal-minimize-after-$(date +%s).txt"
|
||||
{
|
||||
echo "=== After Minimize: $(date -Iseconds) ==="
|
||||
echo ""
|
||||
echo "--- Memory ---"
|
||||
free -m
|
||||
echo ""
|
||||
echo "--- Disk ---"
|
||||
df -h /
|
||||
echo ""
|
||||
echo "--- Top 10 packages by installed size ---"
|
||||
dpkg-query -Wf '${Installed-Size}\t${Package}\n' 2>/dev/null | sort -rn | head -10
|
||||
echo ""
|
||||
echo "--- Running services ---"
|
||||
systemctl list-units --type=service --state=running --no-legend 2>/dev/null | awk '{print $1}' | sort
|
||||
echo ""
|
||||
echo "--- systemd-analyze blame ---"
|
||||
systemd-analyze blame 2>/dev/null | head -20 || echo "not available"
|
||||
} > "$AFTER_FILE"
|
||||
|
||||
ok "Post-minimize profile saved to $AFTER_FILE"
|
||||
|
||||
# ── Compute and display deltas ────────────────────────────────────────────────
|
||||
echo ""
|
||||
info "========== Before / After Summary =========="
|
||||
|
||||
# RAM
|
||||
BEFORE_MEM=$(grep "^Mem:" "$BASELINE_FILE" 2>/dev/null | awk '{print $4}')
|
||||
AFTER_MEM=$(grep "^Mem:" "$AFTER_FILE" 2>/dev/null | awk '{print $4}')
|
||||
BEFORE_MEM_TOTAL=$(grep "^Mem:" "$BASELINE_FILE" 2>/dev/null | awk '{print $2}')
|
||||
echo " RAM: ${BEFORE_MEM:-?} → ${AFTER_MEM:-?} MB free (of ${BEFORE_MEM_TOTAL:-?} MB total)"
|
||||
|
||||
# Disk
|
||||
BEFORE_DISK=$(grep "^/dev/root" "$BASELINE_FILE" 2>/dev/null | awk '{print $4}')
|
||||
AFTER_DISK=$(grep "^/dev/root" "$AFTER_FILE" 2>/dev/null | awk '{print $4}')
|
||||
echo " Disk: ${BEFORE_DISK:-?} → ${AFTER_DISK:-?} available"
|
||||
|
||||
# Package count
|
||||
BEFORE_PKGS=$(dpkg-query -f '.\n' -W 2>/dev/null | wc -l)
|
||||
echo " Packages: $BEFORE_PKGS installed (after removal)"
|
||||
|
||||
echo ""
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
warn "DRY RUN — no changes were made"
|
||||
info "Re-run without --dry-run to apply changes"
|
||||
else
|
||||
ok "Minimization complete!"
|
||||
info "Baseline: $BASELINE_FILE"
|
||||
info "After: $AFTER_FILE"
|
||||
echo ""
|
||||
info "A reboot is recommended to fully apply kernel module blacklist:"
|
||||
echo " sudo reboot"
|
||||
echo ""
|
||||
info "After reboot, verify footprint:"
|
||||
echo " free -m | grep Mem"
|
||||
echo " df -h /"
|
||||
echo " systemd-analyze"
|
||||
fi
|
||||
Reference in New Issue
Block a user