< Back to all hacks

#29 Deep Rootfs Diet (550 -> 498 MB)

Debloat
Problem
Still 50+ MB of unused system libraries after first diet.
Solution
Removed gconv (21 MB), perl (6.6 MB), systemd (12 MB), PAM (3.7 MB). Total diet: 741 -> 498 MB (-33%).
Lesson
Shared libraries hide enormous bloat. gconv alone was 21 MB of charset converters Node.js never calls.

Context

After the first rootfs diet (Hack #28) brought the proot Ubuntu rootfs from 741 MB down to 550 MB by removing Node.js headers, Python, locales, and man pages, a deeper audit with du -sh /usr/lib/* revealed multiple system libraries that OpenClaw never touches. The rootfs is an Ubuntu 25.10 ARM installation running inside proot on the Moto E2 — it provides glibc, libssl, and a POSIX environment for Node.js, but the vast majority of Ubuntu's system libraries exist to support desktop features, authentication stacks, and services that a headless proot container will never use.

The goal was to identify every safe-to-remove component without breaking Node.js, npm, apt, or TLS. The critical keep-list: libc, libssl, libz, libstdc++, ca-certificates, and apt with its dependencies.

LEGACY: This hack documents the proot rootfs era. The native gateway migration (Feb 15, 2026) eliminated proot entirely, making this rootfs unused. The rootfs is scheduled for deletion.

Implementation

Each removal target was identified by checking whether Node.js or apt linked against it, then testing that the gateway still booted after removal.

# Inside proot Ubuntu rootfs

# 1. gconv charset conversion modules — 21 MB
# Node.js uses ICU for charset handling, never calls glibc gconv
rm -rf /usr/lib/arm-linux-gnueabihf/gconv/
# Verify Node still works:
node -e "console.log('gconv removed OK')"

# 2. perl-base — 6.6 MB
# Only used by some dpkg scripts, not needed at runtime
dpkg --purge --force-depends perl-base
# Alternative if dpkg complains:
rm -rf /usr/share/perl/
rm -rf /usr/lib/arm-linux-gnueabihf/perl/

# 3. systemd libraries and units — 12 MB
# proot doesn't run systemd (no PID 1, no cgroups, no dbus)
rm -rf /lib/systemd/
rm -rf /usr/lib/systemd/
rm -rf /etc/systemd/

# 4. PAM (Pluggable Authentication Modules) — 3.7 MB
# proot doesn't do user authentication
rm -rf /usr/lib/arm-linux-gnueabihf/security/
rm -rf /etc/pam.d/
rm -rf /etc/pam.conf

# 5. GStreamer, PackageKit, polkit — ~7 MB
# Multimedia framework, package management daemon, privilege manager
rm -rf /usr/lib/arm-linux-gnueabihf/gstreamer-1.0/
rm -rf /usr/lib/packagekit/
rm -rf /usr/lib/polkit-1/

After each removal, the gateway was started to verify nothing broke:

# Quick smoke test after each deletion
node22-icu --max-old-space-size=112 -e "
  const https = require('https');
  const fs = require('fs');
  console.log('crypto:', !!require('crypto').randomBytes);
  console.log('fs:', !!fs.readFileSync);
  console.log('TLS:', https.globalAgent.protocol);
  console.log('All OK');
"

Full removal script for repeatability:

#!/bin/bash
# deep-diet.sh — second-pass rootfs cleanup
# Run inside proot rootfs
set -e

TARGETS=(
  "/usr/lib/arm-linux-gnueabihf/gconv"    # 21 MB - charset converters
  "/usr/share/perl"                         # 6.6 MB - perl modules
  "/usr/lib/arm-linux-gnueabihf/perl"       # perl arch libs
  "/lib/systemd"                            # 12 MB - systemd
  "/usr/lib/systemd"                        # systemd libs
  "/etc/systemd"                            # systemd config
  "/usr/lib/arm-linux-gnueabihf/security"   # 3.7 MB - PAM
  "/etc/pam.d"                              # PAM config
  "/usr/lib/arm-linux-gnueabihf/gstreamer-1.0"  # multimedia
  "/usr/lib/packagekit"                     # package daemon
  "/usr/lib/polkit-1"                       # privilege manager
)

for target in "${TARGETS[@]}"; do
  if [ -d "$target" ]; then
    size=$(du -sh "$target" 2>/dev/null | cut -f1)
    echo "Removing $target ($size)"
    rm -rf "$target"
  fi
done

echo "Deep diet complete. New rootfs size:"
du -sh /

Verification

# Check rootfs size after cleanup:
du -sh $PREFIX/var/lib/proot-distro/installed-rootfs/ubuntu/
# Expected: ~498 MB (down from 550 MB)

# Verify Node.js still works with all critical modules:
proot-distro login ubuntu -- node22-icu -e "
  require('crypto');
  require('https');
  require('fs');
  require('path');
  require('zlib');
  console.log('All core modules OK');
"

# Verify apt still works (ca-certificates intact):
proot-distro login ubuntu -- apt update
# Expected: successful repository fetch

# Verify TLS works (libssl + ca-certs):
proot-distro login ubuntu -- node22-icu -e "
  const https = require('https');
  https.get('https://api.github.com', r => {
    console.log('TLS status:', r.statusCode);
    r.destroy();
  });
"
# Expected: TLS status: 200 (or 403 for rate-limited)

Gotchas

  • Do NOT remove /usr/lib/arm-linux-gnueabihf/libc*, libssl*, libz*, or libstdc++*. Node.js dynamically links against all of these. Removing any one causes immediate segfault on startup
  • Removing perl-base may break dpkg post-install scripts that use perl. Future apt install commands might fail on packages with perl-based maintainer scripts. The workaround is to reinstall perl-base temporarily if needed
  • The gconv directory is the single biggest win (21 MB). It contains charset conversion modules for obscure encodings (EBCDIC, Shift-JIS, Big5, etc.). Node.js handles all charset conversion through its built-in ICU library, making gconv completely redundant
  • Do NOT remove /etc/apt/ or /var/lib/apt/ — apt is needed for occasional dependency installs
  • This entire hack is LEGACY. The native gateway migration (Hack #12) runs Node.js directly on Termux without proot, making the rootfs unnecessary. The 498 MB rootfs is scheduled for deletion to free ~967 MB of disk space (rootfs + overhead)

Result

MetricFirst DietDeep DietTotal Savings
Rootfs size550 MB498 MB243 MB from 741 MB
gconv removed-21 MBcharset converters
perl removed-6.6 MBscripting runtime
systemd removed-12 MBinit system
PAM removed-3.7 MBauth modules
Other (gst, polkit)-~7 MBdesktop services
Total reduction26%33%from original 741 MB