Skip to content <?xml version="1.0" encoding="UTF-8"?>

How to Keep Archlinux Up to Date

Archlinux has been my linux distribution of choice for many years now. It is my favourite development environment; I like its simplicity, but more specifically, I enjoy having the latest version of the programming languages installed to test new features out.

Although the rolling-release model allows frequent software updates and security fixes, it requires nevertheless regular maintenance that can sometimes feel burdensome. To be able to upgrade the system regularly and quickly, I wanted to make the process as straightforward as possible. With a couple of bash scripts, I automated the most common tasks and facilitated the system maintenance.

Since there are only a few tutorials out there discussing how to maintain the system, I thought it would be interesting to share my experience. This article is not about providing an out-of-the-box solution, but rather, about sharing a simple methodology for maintaining a desktop environment.

Note: If you are not familiar with this linux distribution, pacman is the package manager and AUR is the Arch User Repository which contains packages maintained by the community.

Updating the list of mirrors

Pacman uses a list of mirrors to know which servers packages can be downloaded from. I could use the default list, but I prefer customizing it to fit my needs. I use the Pacman Mirrorlist Generator which is an HTTP endpoint that can be called to get a list of servers. I only run this script a couple of times a year or whenever a server returns an HTTP error.

main() {
    # Ask for user confirmation
    printf "Do you want to update the mirror list? [Y/n]: "
    read -r update_mirrors

    # The default answer is yes
    [[ -z "$update_mirrors" ]] && update_mirrors=y

    if [[ "$update_mirrors" = y ]]; then
        info "Updating mirror list"

        # We keep a copy of the previous list in case something bad happens
        local new_file_path=/etc/pacman.d/mirrorlist
        local old_file_path=/etc/pacman.d/mirrorlist.pacold

        msg "Moving current mirror list to \\x1b[33m${old_file_path}\\x1b[39m..."
        sudo mv "$new_file_path" "$old_file_path"

        # We use curl to call the endpoint with the following parameters:
        # - country: France & Germany
        # - protocol: https
        # - ip_version: IPv4 & IPv6
        # - use_mirror_status: is the server on?

        msg "Downloading custom mirror list..."
        sudo curl -v "https://www.archlinux.org/mirrorlist/?country=FR&country=DE&protocol=https&ip_version=4&ip_version=6&use_mirror_status=on" -o "$new_file_path"

        # The servers list is commented out by default
        msg "Uncommenting servers..."
        sudo sed -i 's/^#Server/Server/' "$new_file_path"

        # We set the correct owner and permissions
        msg "Changing file's owner and permissions..."
        sudo chown root.root "$new_file_path"
        sudo chmod 0644 "$new_file_path"

        # Do we want to delete the previous files?
        printf "Do you want to delete the previous mirror list? [y/N]: "
        read -r delete_prev_list

        # If the user's answer is y and the file exists
        if [[ "$delete_prev_list" = y && -f "${old_file_path}" ]]; then
          sudo rm "${old_file_path}"
        else
          info "The previous mirror list was not deleted."
        fi
    fi
}

main

Upgrading official packages

As recommended in the wiki, I always run a full upgrade of the system. Depending on my needs, I upgrade the system about once a week to keep the update small and quick. Usually, I take a look at the latest news on the arch website beforehand to check whether the upgrade requires any manual interventions; I also run pacdiff after each upgrade to update the configuration files when it is necessary.

update_arch_packages() {
    info "Updating Arch packages..."

    # There might be a manual intervention
    # so let's check the latest news
    printf "Shall we check the Archlinux news? [Y/n]: "
    read -r check_news

    # The default answer is yes
    [[ -z "$check_news" ]] && check_news=y

    if [[ "$check_news" = y ]]; then
        # Open the website in the default browser
        xdg-open https://www.archlinux.org
    fi

    # Start a full upgrade
    sudo pacman -Syu

    printf "Do you need to run pacdiff? [y/N]: "
    read -r run_pacdiff

    if [[ "$run_pacdiff" = y ]]; then
        # Run pacdiff to fix conflicted files if needed
        sudo pacdiff
    fi
}

Upgrading AUR packages

The Archlinux team provides a dedicated repository called the Arch User Repository where the community can upload non-official packages. Being not part of the official release, those packages are not updated during a system upgrade so they must be maintained separately. There are a few helpers available out there but I find it simpler to install and upgrade them manually.

I always clone the source of those packages in the same directory, for instance ~/my-aur-packages. To perform an upgrade, I simply check whether the remote repository has been updated, pull the latest changes, and rebuild the package. Using makepkg, we can build the package as well as resolve its dependencies.

update_aur_packages() {
    # $AUR_PKG is the place where I clone AUR packages,
    # for instance in my home directory ~/my-aur-packages

    info "Updating AUR packages..."
    msg "Working directory \\x1b[33m${AUR_PKG}\\x1b[39m"

    # Get the list of directories
    local repositories=$(find "$AUR_PKG" -maxdepth 1 -type d ! -path "$AUR_PKG")

    # Iterate over the list to update each package
    for repository in ${repositories[*]}; do
        update_aur_package "$repository"
    done
}

update_aur_package() {
    # The first argument is the directory path
    local path="${1}"
    # Get the directory name which is also the package name in this case
    local dirname=$(basename "$path")

    msg "Updating \\x1b[33m${dirname}\\x1b[39m..."

    # Move into the directory
    cd "$path" || error "Could not enter directory '$path'"

    # If it's a git repository
    if [[ -d .git ]]; then
        # Fetch remote repository updates
        git remote update

        # Get the local and remote commit ID
        local local_id=$(git rev-parse master)
        local remote_id=$(git rev-parse origin/master)

        # If the local commit ID and the remote commit ID don't match,
        if [[ "$local_id" != "$remote_id" ]]; then

            # We pull the latest changes...
            msg "Pulling latest changes..."
            git pull

            # ...and rebuild the package with the following options:
            # syncdeps: automatically resolves and installs any dependencies
            # install: install the package
            # rmdeps: removes build-time dependencies after the build
            # clean: cleans up temporary build files after the build

            msg "Building package..."
            makepkg --syncdeps --install --rmdeps --clean
        else
            msg "\\x1b[33m${dirname}\\x1b[39m is up-to-date"
        fi
    else
        warning "\\x1b[33m${dirname}\\x1b[39m is not a git repository. Skipping."
    fi
}

Removing orphaned packages

Constantly rebuilding AUR packages might leave orphaned packages in the system. There is a neat trick to clean them up. I run the following function to look up orphans and remove them right after upgrading AUR packages.

check_orphans() {
    info "Looking up orphans..."

    local orphans=()

    # Get the list of orphans
    IFS=" " read -r -a orphans <<<"$(sudo pacman -Qtdq)"

    if [[ ${#orphans[@]} -eq 0 ]]; then
        msg "No orphans found"
    else
        printf "The following orphaned packages have been found: "
        echo -e "${orphans[@]}"

        printf "Do you want to remove them? [y/N]: "
        read -r remove_pkgs

        if [[ "$remove_pkgs" = y ]]; then
            # Remove orphaned packages
            sudo pacman -Rns "${orphans[@]}"
        fi
    fi
}

Cleaning up the cache

Pacman keeps all versions of a package in its cache, which is really handy if you need to downgrade a package; however, the cache can get pretty big over time so we need to clean it up every once in a while.

The script paccache helps us do exactly that. We can specify how many versions we want to keep in the cache. I only keep the current and the previous version just in case I need to downgrade a package.

clean_cache() {
    local keep=2

    # We first run a dry run of paccache
    # to check where there are any candidates for removal
    info "Looking up unnecessary packages in cache..."
    sudo paccache -dk "$keep"

    printf "Do you want to clean up the cache (keeping %s last version(s))? [y/N]: " $keep
    read -r clean_cache

    if [[ "$clean_cache" = y ]]; then
        # Remove the old packages,
        # we only keep the last 2 versions of each package
        sudo paccache -rk "$keep"
    fi
}

Conclusion

Archlinux certainly requires a bit of maintenance, however, I still have the feeling that it is the most stable operating system I have ever used. I have followed this method for years now and I have never had any weird dependency issues. Moreover, besides the download time, it only takes a few minutes to perform a full upgrade of the system. If you are interested and want to find out a bit more, you can find a complete version of the scripts to update the list of mirrors and to upgrade the system in my dotfiles repository.