Offloading System Updates to my NAS
One of the first things I do after booting my Arch Linux system is run a system update. However, this completely hogs my internet connection. All network resources are allocated to downloading packages, meaning web pages can take several minutes to load. A simple HTTP request to a static website that normally takes \~50ms suddenly takes over 1.3 seconds.
I like to run updates in the background in a detached `tmux` session, but I don't want it to interfere with my normal computer usage. So, I came up with a plan: each time I start up my system, it should report to my NAS server exactly which packages need updating. At 6 PM, when I'm not actively using my computer, the NAS should download all the required updates in the background. The next day, I can install them locally in seconds.
Sounds simple, right?
I created a network share on my unRAID NAS to hold a text file with the download URLs and a directory for the downloaded `.pkg.tar.zst` files.
I started with a bash script that parses pending updates using the `checkupdates` utility (from the `pacman-contrib` package). I configured this script to run via a systemd service, ensuring it only executes after the network share is successfully mounted.
To download the packages onto the NAS, I initially wrote a script that spun up a temporary Docker container using the official `archlinux` image. Inside the container, it would run `pacman -Sw --noconfirm package-name` to download the package, and then copy the files to the mounted network share.
But this initial approach had a major flaw. My workstation is a full desktop system with a complex web of dependencies. If `firefox` needed an update, the barebones Docker container didn't have the underlying desktop libraries installed. `pacman -Sw` would happily download `firefox` and pull in all of its missing dependencies (like `gtk3` and potentially half of GNOME). I would end up downloading gigabytes of packages I didn't actually need updates for, completely defeating the purpose of a lightweight background task.
I realized I could print the direct download URL of a package using `pacman -Sp package-name`. I updated my script to grab these URLs and send them to the NAS. But immediately, I hit a wall: if I already had an older version of the package in my local `/var/cache/pacman/pkg/` directory, `pacman` would "helpfully" print a `file:///var/cache/...` URL instead of the remote HTTP mirror URL.
I tried forcing `pacman` to use an empty temporary folder via `pacman --cachedir /tmp/empty_dir -Sp package-name`. Apparently, `--cachedir` appends to the search list rather than replacing the default cache. Pacman still found the old file locally and returned the `file://` URL.
The root cause was that my local system database knew I already had an older version installed, and it was matching that against the local cache. I fixed this by replicating what `checkupdates` does under the hood: I created a fresh, temporary database.
The final, bulletproof command looks like this: `pacman --nodeps --dbpath "$tmp_db" --cachedir "$tmp_db" -Sp "$package"`
Here is why this works perfectly:
1. `--dbpath "$tmp_db"` forces `pacman` to use a freshly synced database, so it looks for the new version's filename. 2. `--cachedir "$tmp_db"` points to a guaranteed-empty directory, preventing any local `file://` fallback. 3. `--nodeps` prevents `pacman` from aborting the query with a "breaks dependency" error (which commonly happens with split packages like `gdb`/`gdb-common` during transitions).
With the Arch side finally outputting clean HTTP URLs, the unRAID side is beautifully simple: just a `wget -c -i` command reading the text file.
To run it at 6 PM, I installed the User Scripts plugin to create a cron job. I pasted the script in, wrote a cron schedule expression, and... it didn't work.
After a long debugging session, I realized the issue wasn't my script, my network, or the stars aligning wrong. It was me. My server was in the wrong time zone. I had recently reinstalled unRAID. I bought my license back in 2024 with one year of updates. Outside my license window, version 7.3.0 released, which added an Onboarding Wizard that asks for your time zone. Because I didn't want to pay $36 to extend my license for another year just to access that update, I missed the time zone setup. A quick manual fix in the unRAID GUI sorted it out, and the cron job started firing perfectly.
As a final experiment, I had the idea to add my NAS package share to the `CacheDir` list in my `pacman.conf`. My hope was that when I ran `pacman -Syu`, it would see the new packages on the NAS and install them directly from there. Instead, it ignored them, redownloaded the packages anyway, and the total progress bar did weird things (like going beyond 100%).
So, I stuck to the original plan: a simple `sudo pacman -U /mnt/arch-pkg-cache/pkgs/*.pkg.tar.zst` the next day. It installs instantly, uses zero internet, and keeps my daytime browsing perfectly smooth.