#!/bin/bash set -euo pipefail # --- CONFIGURATION --- # The user that runs paru inside the Arch LXCs AUR_USER="liph" EXCLUDE_LIST=(120 121 150 151 152) declare -A CONTAINER_MAP CONTAINER_MAP[100]="/mnt/flash1/podman/lxc_servarr/" CONTAINER_MAP[101]="/mnt/flash1/podman/lxc_second/" CONTAINER_MAP[108]="/mnt/flash1/podman/lxc_dns/" # Multiple paths allowed CONTAINER_MAP[109]="/mnt/flash1/podman/lxc_immich/" CONTAINER_MAP[111]="/mnt/flash1/podman/lxc_ollama/" # --- UTILS --- log() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" } # Wrapper for commands that modify state run_pct() { if [[ "${DRY_RUN:-0}" == "1" ]]; then log "[DRY-RUN] pct $*" else pct "$@" fi } ALL_CTIDS=$(pct list | awk 'NR>1 {print $1}') for CTID in $ALL_CTIDS; do # Check if CTID is in EXCLUDE_LIST IS_EXCLUDED=false for EXCLUDE in "${EXCLUDE_LIST[@]}"; do if [[ "$CTID" == "$EXCLUDE" ]]; then IS_EXCLUDED=true break fi done if [ "$IS_EXCLUDED" = true ]; then continue; fi if pct config "$CTID" | grep -q "template: 1"; then continue; fi NAME=$(pct config "$CTID" | grep "hostname:" | awk '{print $2}') STATUS=$(pct status "$CTID" | awk '{print $2}') echo "=======================================================" log "PROCESSING LXC: $NAME (ID: $CTID)" echo "=======================================================" WAS_STOPPED=false if [ "$STATUS" == "stopped" ]; then run_pct start "$CTID" [[ "${DRY_RUN:-0}" == "0" ]] && sleep 10 WAS_STOPPED=true fi # --- 1. SETUP PASSWORDLESS FOR PARU --- # Paru MUST run as a user, so we keep this sudoers rule for pacman. log "[+] Configuring temporary sudoers for $AUR_USER..." run_pct exec "$CTID" -- bash -c "echo '$AUR_USER ALL=(ALL) NOPASSWD: /usr/bin/pacman' > /etc/sudoers.d/99-paru-nopass" run_pct exec "$CTID" -- chmod 440 /etc/sudoers.d/99-paru-nopass # --- 2. OS UPDATER (User Context) --- log "[+] Updating Arch packages (as $AUR_USER)..." run_pct exec "$CTID" -- sudo -u "$AUR_USER" paru -Syu --noconfirm --cleanafter # --- 3. PODMAN SCAN (Root Context) --- if [[ -n "${CONTAINER_MAP[$CTID]:-}" ]]; then # Check Storage Driver (Diagnostic) DRIVER=$(pct exec "$CTID" -- podman info --format '{{.Store.GraphDriverName}}' 2>/dev/null || echo "unknown") if [[ "$DRIVER" == "vfs" ]]; then log "[WARN] Storage driver is 'vfs'. Image operations will be slow." fi # Support multiple paths ROOT_DIRS=${CONTAINER_MAP[$CTID]} for ROOT_DIR in $ROOT_DIRS; do log "[+] Scanning for compose files in: $ROOT_DIR" # Determine Command if pct exec "$CTID" -- podman compose version >/dev/null 2>&1; then FINAL_CMD="podman compose" elif pct exec "$CTID" -- podman-compose version >/dev/null 2>&1; then FINAL_CMD="podman-compose" elif pct exec "$CTID" -- docker-compose version >/dev/null 2>&1; then FINAL_CMD="docker-compose" else FINAL_CMD=""; fi if [ -n "$FINAL_CMD" ]; then # Scan for files if pct exec "$CTID" -- [ -d "$ROOT_DIR" ]; then pct exec "$CTID" -- find "$ROOT_DIR" -maxdepth 2 -name "docker-compose.y*ml" | while read -r FILE; do [ -z "$FILE" ] && continue DIR=$(dirname "$FILE") log " -> Updating Project: $DIR" log " -> Pulling new images (this may take a while)..." run_pct exec "$CTID" -- bash -c "cd \"$DIR\" && $FINAL_CMD pull" if [ "$WAS_STOPPED" = true ]; then log " -> Recreating stopped container (up --no-start)..." run_pct exec "$CTID" -- bash -c "cd \"$DIR\" && $FINAL_CMD up --no-start" || log " [WARN] Failed to recreate stopped container. Old images may not be pruned." else log " -> Applying updates (up -d --force-recreate)..." run_pct exec "$CTID" -- bash -c "cd \"$DIR\" && $FINAL_CMD up -d --force-recreate" fi done else log " [WARN] Root directory not found: $ROOT_DIR" fi fi done log "[+] Pruning all unused images to save space..." run_pct exec "$CTID" -- podman image prune -a -f fi # --- 4. MAINTENANCE --- log "[+] Running maintenance tasks (journal, orphans)..." run_pct exec "$CTID" -- journalctl --rotate run_pct exec "$CTID" -- journalctl --vacuum-time=7d run_pct exec "$CTID" -- bash -c "orphans=\$(pacman -Qtdq); if [ -n \"\$orphans\" ]; then echo \" -> Removing orphans: \$orphans\"; pacman -Rns \$orphans --noconfirm; fi" if [ "$WAS_STOPPED" = true ]; then log "[+] Shutting down container (was previously stopped)..." run_pct shutdown "$CTID" fi done