Michifumi's Blog

This blog is entirely produced by silicon-based slaves. I only issue commands and wield the whip.

Jan 7, 2026 · Updated on May 23, 2026

Mastering the Art of Daily Updates on Linux (and macOS)

Satisfying the compulsion for daily system updates with a custom bash script for Linux and Docker.

#Bash #GNU/Linux #Docker #macOS #Debian #Ubuntu #Fedora #Fedora CoreOS

There are two types of people: those who never update their software and those who do it every day.

Clearly, I’m the latter. I even perform various update operations daily across different devices: from system-level to application-level. And whenever I’m feeling bored, “update” is absolutely my default unconscious choice. I’m so addicted to updating that I wonder if it’s some kind of disease such as compulsive disorder. Perhaps medical or psychological experts have already conducted similar research on this?

Anyway, while updating functions on popular operating systems like iOS, macOS, Android, and Windows has become remarkably straightforward and user-friendly, the process remains less intuitive for Linux systems, particularly when using the command-line interface (CLI). Although users can leverage unattended-upgrades for automated updates, this solution doesn’t address all challenges for heavy Docker users.

That’s why I need to write an update script to satisfy my perverted desire to update.


Installation and Usage

  1. Create the script file:
sudo nano /usr/local/bin/up.sh
  1. Paste the content from your OS-specific script (Debian-based Linux, Fedora/RHEL/CentOS 8+, or Fedora CoreOS) into the file and save it.

  2. Make the script executable:

sudo chmod +x /usr/local/bin/up.sh
  1. Run the script from anywhere in the terminal:
up.sh

Debian-based Linux

Here is the up.sh script that handles system and Docker updates:

#!/bin/bash

# Ensure the script exits if any command fails
set -e

echo "--- Starting System Update ---"
sudo apt-get update
sudo apt-get full-upgrade -y
sudo apt-get autoremove -y

echo "--- Updating Docker Containers via Watchtower ---"
# Note: Docker API version is essential for Watchtower to function correctly
sudo docker run --rm \
    -e DOCKER_API_VERSION=1.44 \
    -v /var/run/docker.sock:/var/run/docker.sock \
    containrrr/watchtower \
    --run-once \
    --cleanup \
    your container names (separate by space)

echo "--- Cleaning up unused Docker resources ---"
sudo docker container prune -f
# Remove -a to keep cached images or keep it for deep clean
sudo docker image prune -a -f
sudo docker volume prune -f
sudo docker network prune -f
sudo docker builder prune -f

echo "Update complete!"

Go back Step 3 to continue


Fedora/RHEL/CentOS 8+

#!/bin/bash
set -e

echo "--- Starting System Update ---"
sudo dnf upgrade -y
sudo dnf autoremove -y

echo "--- Updating Docker Containers via Watchtower ---"
# Note: Docker API version is essential for Watchtower to function correctly
sudo docker run --rm \
    -e DOCKER_API_VERSION=1.44 \
    -v /var/run/docker.sock:/var/run/docker.sock \
    containrrr/watchtower \
    --run-once \
    --cleanup \
    your container names (separate by space)

echo "--- Cleaning up unused Docker resources ---"
sudo docker container prune -f
# Remove -a to keep cached images or keep it for deep clean
sudo docker image prune -a -f
sudo docker volume prune -f
sudo docker network prune -f
sudo docker builder prune -f

echo "Update complete!"

Go back Step 3 to continue


Fedora CoreOS (Automatic Updates via Zincati)

Fedora CoreOS is different, it uses Zincati for fully automatic updates. Unlike traditional Linux distributions, Fedora CoreOS requires no manual intervention for system updates.

How Zincati Works:

Zincati is the automatic update agent that:

  1. Periodically checks for new Fedora CoreOS releases from the Cincinnati update server
  2. Automatically downloads and stages updates in the background (no bandwidth concerns during work hours)
  3. Handles reboots based on your configured strategy

Update Strategies:

Zincati supports different reboot strategies (configured in /etc/zincati/config.d/):

  • immediate: Reboots immediately after staging an update (default on Google Cloud Platform)
  • fleet_lock: Coordinates with other nodes to prevent simultaneous reboots (cluster-friendly)
  • periodic: Reboots only during specified time windows (e.g., 2-4 AM on weekends)

You can check your current strategy with:

systemctl status zincati | grep "update strategy"

Important: Fedora CoreOS uses rpm-ostree, which means updates are atomic and always require a reboot. The new version is staged as a separate deployment and only becomes active after reboot. This ensures:

  • Zero chance of broken updates (either fully applied or not at all)
  • Instant rollback capability (previous version remains available)
  • No “partially updated” system states

The Script:

Since Zincati handles system updates automatically, the script focuses on checking status and updating containers:

#!/bin/bash
set -euo pipefail

echo "--- Checking for staged system updates ---"
rpm-ostree status

# Update toolbox container
# After a major version upgrade, the default toolbox
# container won't exist yet. Create it automatically if missing.
echo ""
echo "--- Updating Toolbox Container ---"
if ! toolbox run true 2>/dev/null; then
    echo "Default toolbox container not found. Creating..."
    toolbox create -y
fi

# Upgrade, autoremove dependencies, AND purge the massive DNF download cache
toolbox run sudo dnf upgrade -y
toolbox run sudo dnf autoremove -y
toolbox run sudo dnf clean all

# Clean up unused Podman resources
echo ""
echo "--- Cleaning up unused Podman resources ---"
# Deletes stopped containers, unused networks, and dangling images
podman system prune -f 

# Removes unused volumes specifically (since system prune doesn't include volumes by default)
podman volume prune -f

# Optional deep clean: Removes all unused images, not just dangling ones
# podman image prune -a -f

echo ""
echo "Update complete!"

echo "Note: System updates are managed automatically by Zincati."

Note: Fedora CoreOS uses Podman by default. For automatic container updates, use podman auto-update with containers running inside systemd units and the io.containers.autoupdate label. Podman also ships with a podman-auto-update.timer that triggers updates daily.

Cleaning up old toolbox containers after a major version upgrade:

After Zincati upgrades Fedora CoreOS to a new major version, the script above creates a new toolbox container. The old toolbox container will remain on disk. To check and clean it up:

# List all toolbox containers
toolbox list

You’ll see something like:

CONTAINER ID  CONTAINER NAME      CREATED      STATUS   IMAGE NAME
a1b2c3d4e5f6  fedora-toolbox-43   3 months ago  running  registry.fedoraproject.org/fedora-toolbox:43
f6e5d4c3b2a1  fedora-toolbox-44   2 minutes ago running  registry.fedoraproject.org/fedora-toolbox:44

Your files in ~/ (e.g. ~/backups, ~/page-stats) are not stored inside the toolbox — toolbox bind-mounts your home directory into the container. Removing an old toolbox only deletes the packages installed via dnf inside it (Node.js, npm, etc.), not your files.

If you no longer need the old toolbox:

# Stop and remove the old container
podman stop fedora-toolbox-43
toolbox rm fedora-toolbox-43

# Optionally, remove the old base image to free disk space
podman rmi registry.fedoraproject.org/fedora-toolbox:43

Go back Step 3 to continue


Bonus: Daily Updates on macOS (Homebrew)

If you are on macOS and want to satisfy your update craving with Homebrew, this single command is all you need for aggressive maintenance:

brew upgrade && brew autoremove && brew cleanup --prune=all
  • brew upgrade: Upgrades all packages.
  • brew autoremove: Removes orphan dependencies that are no longer needed.
  • brew cleanup —prune=all: Aggressively clears the cache to free up maximum disk space, I recommend using it with the 256 GB SSD MacBook Air (that is something only Apple can do).

To make this even easier, add an alias for the terminal ~/.zshrc:

nano ~/.zshrc

Add the following line:

alias up='brew upgrade && brew autoremove && brew cleanup --prune=all'

Then reload your shell:

source ~/.zshrc

Now you can simply type up to update everything that installed from homebrew!