{"id":85,"date":"2025-08-25T20:55:19","date_gmt":"2025-08-25T18:55:19","guid":{"rendered":"https:\/\/woxblom.com\/?p=85"},"modified":"2025-08-26T17:19:42","modified_gmt":"2025-08-26T15:19:42","slug":"bash-script-to-update-lxc-containers","status":"publish","type":"post","link":"https:\/\/woxblom.com\/index.php\/2025\/08\/25\/bash-script-to-update-lxc-containers\/","title":{"rendered":"Bash script to update LXC containers"},"content":{"rendered":"\n<p>If you want to implement the same script, create a file in \/var\/local\/bin\/ as below<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/usr\/local\/bin\/lxc_updater.sh<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\n\n# Define log file\nLOGFILE=\"\/var\/log\/lxc_update.log\"\n\n# Check if the script is running in an interactive terminal\nif &#91; -t 1 ]; then\n    # If interactive, output to both terminal and log file\n    LOG_OUTPUT_MODE=\"TEE\"\n    echo \"Running in interactive mode. Output will be shown in terminal and logged.\"\nelse\n    # If not interactive (cron job), redirect all output to the log file\n    LOG_OUTPUT_MODE=\"SILENT\"\n    exec 1&gt;&gt;$LOGFILE 2&gt;&amp;1\nfi\n\n# Add a function to handle logging based on the mode\nlog_message() {\n    local message=\"$1\"\n    if &#91; \"$LOG_OUTPUT_MODE\" == \"TEE\" ]; then\n        echo \"$message\" | tee -a \"$LOGFILE\"\n    else\n        echo \"$message\"\n    fi\n}\n\n# Use full path for lxc commands\nLXC_LS_CMD=\"\/usr\/bin\/lxc-ls\"\nLXC_ATTACH_CMD=\"\/usr\/bin\/lxc-attach\"\nLXC_STOP_CMD=\"\/usr\/bin\/lxc-stop\"\nLXC_START_CMD=\"\/usr\/bin\/lxc-start\"\n\n# Add timestamp\nlog_message \"---------------------------------------------------\"\nlog_message \"LXC container update started on $(date)\"\nlog_message \"---------------------------------------------------\"\n\n# Get a list of all running LXC containers\nCONTAINERS_LIST=$($LXC_LS_CMD -f | grep 'RUNNING' | awk '{print $1}')\n\nif &#91; -z \"$CONTAINERS_LIST\" ]; then\n    log_message \"No running LXC containers found. Exiting.\"\n    exit 0\nfi\n\n# Loop through each running container using a while loop\necho \"$CONTAINERS_LIST\" | while read CONTAINER; do\n    log_message \"Processing container: $CONTAINER\"\n\n    # Check for the container's OS and run appropriate commands\n    if $LXC_ATTACH_CMD -n \"$CONTAINER\" -- test -f \/usr\/bin\/apt &gt;\/dev\/null 2&gt;&amp;1; then\n        log_message \"Detected APT-based system. Updating...\"\n        $LXC_ATTACH_CMD -n \"$CONTAINER\" -- bash -c \"apt-get update &amp;&amp; apt-get upgrade -y &amp;&amp; apt-get autoremove -y\"\n\n    elif $LXC_ATTACH_CMD -n \"$CONTAINER\" -- test -f \/usr\/bin\/pacman &gt;\/dev\/null 2&gt;&amp;1; then\n        log_message \"Detected Arch-based system. Updating...\"\n        $LXC_ATTACH_CMD -n \"$CONTAINER\" -- bash -c \"pacman -Syu --noconfirm\"\n\n    elif $LXC_ATTACH_CMD -n \"$CONTAINER\" -- test -f \/usr\/bin\/dnf &gt;\/dev\/null 2&gt;&amp;1; then\n        log_message \"Detected Fedora-based system. Updating...\"\n        $LXC_ATTACH_CMD -n \"$CONTAINER\" -- bash -c \"dnf -y update\"\n\n    elif $LXC_ATTACH_CMD -n \"$CONTAINER\" -- test -f \/sbin\/apk &gt;\/dev\/null 2&gt;&amp;1; then\n        log_message \"Detected Alpine Linux. Updating...\"\n        $LXC_ATTACH_CMD -n \"$CONTAINER\" -- sh -c \"apk update &amp;&amp; apk upgrade &amp;&amp; apk-autoremove\"\n\n    else\n        log_message \"Unknown package manager for $CONTAINER. Skipping.\"\n        continue\n    fi\n\n    # Check if a reboot is required\n    if $LXC_ATTACH_CMD -n \"$CONTAINER\" -- ls \/var\/run\/reboot-required &gt;\/dev\/null 2&gt;&amp;1; then\n        log_message \"Reboot required for $CONTAINER. Rebooting now...\"\n        $LXC_STOP_CMD -n \"$CONTAINER\"\n        $LXC_START_CMD -n \"$CONTAINER\"\n        log_message \"$CONTAINER rebooted successfully.\"\n    elif $LXC_ATTACH_CMD -n \"$CONTAINER\" -- \/usr\/bin\/needs-restarting -r &gt;\/dev\/null 2&gt;&amp;1; then\n        log_message \"Reboot required for $CONTAINER. Rebooting now...\"\n        $LXC_STOP_CMD -n \"$CONTAINER\"\n        $LXC_START_CMD -n \"$CONTAINER\"\n        log_message \"$CONTAINER rebooted successfully.\"\n    elif $LXC_ATTACH_CMD -n \"$CONTAINER\" -- test -f \/var\/run\/reboot-required.pkgs &gt;\/dev\/null 2&gt;&amp;1; then\n        log_message \"Reboot required for $CONTAINER. Rebooting now...\"\n        $LXC_STOP_CMD -n \"$CONTAINER\"\n        $LXC_START_CMD -n \"$CONTAINER\"\n        log_message \"$CONTAINER rebooted successfully.\"\n    else\n        log_message \"No reboot required for $CONTAINER.\"\n    fi\n\ndone\n\nlog_message \"---------------------------------------------------\"\nlog_message \"LXC container update finished on $(date)\"\nlog_message \"---------------------------------------------------\"<\/code><\/pre>\n\n\n\n<p>Then make sure the script can be executed<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo chmod +x \/usr\/local\/bin\/lxc_updater.sh<\/code><\/pre>\n\n\n\n<p>You can testrun the script with the following command<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo .\/lxc_updater.sh<\/code><\/pre>\n\n\n\n<p>Finally if you want it to run every month on the first day of the month add a cronjob<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo crontab -e<\/code><\/pre>\n\n\n\n<p>And add the following lines to the crontab<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Run the LXC updater script at midnight on the 1:th day of every month\n0 0 1 * * \/usr\/local\/bin\/lxc_updater.sh\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>I have LXC containers running for services on in my Home-lab. I added this little bash script to update and reboot all containers.<\/p>\n","protected":false},"author":1,"featured_media":153,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[],"class_list":["post-85","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-computers"],"_links":{"self":[{"href":"https:\/\/woxblom.com\/index.php\/wp-json\/wp\/v2\/posts\/85","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/woxblom.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/woxblom.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/woxblom.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/woxblom.com\/index.php\/wp-json\/wp\/v2\/comments?post=85"}],"version-history":[{"count":3,"href":"https:\/\/woxblom.com\/index.php\/wp-json\/wp\/v2\/posts\/85\/revisions"}],"predecessor-version":[{"id":156,"href":"https:\/\/woxblom.com\/index.php\/wp-json\/wp\/v2\/posts\/85\/revisions\/156"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/woxblom.com\/index.php\/wp-json\/wp\/v2\/media\/153"}],"wp:attachment":[{"href":"https:\/\/woxblom.com\/index.php\/wp-json\/wp\/v2\/media?parent=85"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/woxblom.com\/index.php\/wp-json\/wp\/v2\/categories?post=85"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/woxblom.com\/index.php\/wp-json\/wp\/v2\/tags?post=85"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}