From 50d11a7728382c05fdfcfd539e4ef608357c0049 Mon Sep 17 00:00:00 2001 From: librelad Date: Sun, 24 May 2026 22:07:56 +0100 Subject: [PATCH] feat(uninstall): --skip-docker-images keeps the docker layer for fast reinstall MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: librelad --- init.sh | 66 +++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/init.sh b/init.sh index a406509..bb32576 100755 --- a/init.sh +++ b/init.sh @@ -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,23 +1365,35 @@ 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 - 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 - loginctl terminate-user "$iuser" >/dev/null 2>&1 || true - pkill -9 -u "$iuser" >/dev/null 2>&1 || true - isSuccessful "Stopped containers + rootless docker for '$iuser'" + 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 + loginctl terminate-user "$iuser" >/dev/null 2>&1 || true + 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" - rm -f /etc/sysctl.d/99-libreportal-hardening.conf /etc/sysctl.d/99-libreportal-rootless.conf - sysctl --system >/dev/null 2>&1 + # 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 - sed -i "/^${mgr}:/d;/^${iuser}:/d" /etc/subuid /etc/subgid 2>/dev/null || true - isSuccessful "Removed users '$mgr' + '$iuser' (+ home dirs)" + 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."