feat(uninstall): --skip-docker-images keeps the docker layer for fast reinstall

A full uninstall tears down the rootless daemon and removes the
docker-install user's home, which destroys the WebUI image AND the build
cache — so every reinstall's `docker build` runs from scratch (slow,
re-pulls the base image + reinstalls deps). On a slow local box that
dominates the iteration loop.

--skip-docker-images on `init.sh ... uninstall` preserves the rootless
docker layer: it still removes stale containers, the control plane,
manager user, footprint and /docker, but keeps the daemon running, the
docker-install user + home (image/layer cache), and the rootless sysctl
drop-in. The following reinstall then finds rootless already set up and
rebuilds the WebUI image from cache — fast. No effect on install.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
librelad 2026-05-24 22:07:56 +01:00
parent a42f2c6618
commit 50d11a7728

46
init.sh
View File

@ -10,11 +10,17 @@
# --unattended Run in unattended mode (skip confirmations)
# --skip-os-update Skip operating system update
# --skip-prereqs Skip installing prerequisite apps
# --skip-docker-images On UNINSTALL: keep the rootless docker daemon, the
# docker-install user, and the image/build cache instead
# of tearing them down — so a following reinstall rebuilds
# the WebUI image from cache (fast) instead of from
# scratch. (No effect on install.)
#
# Examples:
# ./init.sh --random-password --local init
# ./init.sh --random-password --local --unattended init
# ./init.sh --random-password --local --skip-os-update --skip-prereqs init
# ./init.sh --unattended --skip-docker-images uninstall # keep docker layer
# ./init.sh init mypassword myuser mytoken https://github.com/user/repo.git
#
@ -90,6 +96,7 @@ init_local_install=false
init_unattended_mode=false
init_skip_os_update=false
init_skip_prereqs=false
init_skip_docker_images=false
install_param="init"
sudo_user_name=libreportal
@ -144,6 +151,13 @@ for ((i=1; i<=$#; i++)); do
init_skip_prereqs=true
((init_shift_count++))
;;
--skip-docker-images)
# On uninstall: preserve the rootless docker layer (daemon +
# docker-install user + image/build cache) so the next reinstall's
# `docker build` is cache-fast. Honored in runFullUninstall.
init_skip_docker_images=true
((init_shift_count++))
;;
esac
done
@ -1305,6 +1319,13 @@ runFullUninstall()
iuser=$(grep -h '^CFG_DOCKER_INSTALL_USER=' /docker/configs/general/general_docker_install 2>/dev/null | head -1 | cut -d= -f2 | awk '{print $1}')
iuser="${iuser:-dockerinstall}"
# --skip-docker-images: keep the rootless docker layer (the daemon, the
# "$iuser" user, the image/build cache + the rootless sysctl drop-in) instead
# of tearing it down, so a following reinstall's `docker build` is cache-fast
# instead of from-scratch. Everything else (control plane, manager, footprint,
# /docker) is still removed.
local keep_docker="${init_skip_docker_images:-false}"
isHeader "LibrePortal — FULL Uninstall"
isError "This PERMANENTLY removes EVERYTHING — there is no undo:"
echo " - all containers + images + the rootless docker setup"
@ -1317,6 +1338,10 @@ runFullUninstall()
isNotice "LEFT IN PLACE: the docker engine, docker-compose, apt-installed deps,"
isNotice "and your SSH config (so you can't get locked out)."
echo ""
if [[ "$keep_docker" == "true" ]]; then
isNotice "--skip-docker-images: KEEPING the rootless docker daemon, the '$iuser' user, and the image/build cache (for a faster reinstall)."
echo ""
fi
if [[ "$init_unattended_mode" == true ]]; then
isNotice "Unattended mode — proceeding without the DELETE LIBREPORTAL prompt."
else
@ -1340,9 +1365,15 @@ runFullUninstall()
# user's session (stops the rootless dockerd + any survivors).
local uid; uid=$(id -u "$iuser" 2>/dev/null)
if [[ -n "$uid" ]]; then
# Remove stale containers either way — they bind-mount the about-to-be-
# wiped /docker. The images + build cache live in the user's home and are
# untouched by `docker rm`.
sudo -u "$iuser" env XDG_RUNTIME_DIR="/run/user/$uid" \
DOCKER_HOST="unix:///run/user/$uid/docker.sock" \
bash -c 'docker ps -aq | xargs -r docker rm -f' >/dev/null 2>&1 || true
if [[ "$keep_docker" == "true" ]]; then
isSuccessful "Removed containers; kept the rootless docker daemon + images for '$iuser'"
else
sudo -u "$iuser" env XDG_RUNTIME_DIR="/run/user/$uid" \
dockerd-rootless-setuptool.sh uninstall >/dev/null 2>&1 || true
loginctl disable-linger "$iuser" >/dev/null 2>&1 || true
@ -1350,13 +1381,19 @@ runFullUninstall()
pkill -9 -u "$iuser" >/dev/null 2>&1 || true
isSuccessful "Stopped containers + rootless docker for '$iuser'"
fi
fi
# 3. Remove the out-of-/docker footprint (see FOOTPRINT.md).
rm -f /usr/local/bin/libreportal
rm -rf /usr/local/lib/libreportal
rm -f "/etc/sudoers.d/$mgr"
# Keep the sysctl drop-ins when preserving docker — removing + reloading them
# would reset ip_unprivileged_port_start etc. out from under the still-running
# rootless daemon. (The reinstall re-adds them idempotently.)
if [[ "$keep_docker" != "true" ]]; then
rm -f /etc/sysctl.d/99-libreportal-hardening.conf /etc/sysctl.d/99-libreportal-rootless.conf
sysctl --system >/dev/null 2>&1
fi
rm -f /usr/local/bin/restic /usr/local/bin/kopia /usr/local/bin/ufw-docker
rm -f /root/init.sh
isSuccessful "Removed the system-integration footprint"
@ -1370,15 +1407,22 @@ runFullUninstall()
# `userdel -r` leaves the home behind ("user currently used"); rm -rf the
# home afterwards as a backstop.
local u
for u in "$mgr" "$iuser"; do
local users_to_remove=("$mgr")
[[ "$keep_docker" == "true" ]] || users_to_remove+=("$iuser")
for u in "${users_to_remove[@]}"; do
loginctl disable-linger "$u" >/dev/null 2>&1 || true
loginctl terminate-user "$u" >/dev/null 2>&1 || true
pkill -9 -u "$u" >/dev/null 2>&1 || true
userdel -r "$u" >/dev/null 2>&1 || true
[[ -n "$u" ]] && rm -rf "/home/$u"
done
if [[ "$keep_docker" == "true" ]]; then
sed -i "/^${mgr}:/d" /etc/subuid /etc/subgid 2>/dev/null || true
isSuccessful "Removed user '$mgr' (+ home); kept '$iuser' + its docker image store"
else
sed -i "/^${mgr}:/d;/^${iuser}:/d" /etc/subuid /etc/subgid 2>/dev/null || true
isSuccessful "Removed users '$mgr' + '$iuser' (+ home dirs)"
fi
isHeader "LibrePortal uninstalled"
isNotice "Left in place: docker engine, docker-compose, apt deps, SSH config."