Skip to content
Go back

Topgrade: One Command Updates Everything

By SumGuy 9 min read
Topgrade: One Command Updates Everything
Contents

You Have Too Many Package Managers

You’re running Arch. Or Debian. Or macOS with Homebrew bolted on. Whatever the case, your system has at least four things that need updating independently, and you’re running them manually like it’s 2009.

apt update && apt upgrade, okay. flatpak update, sure. pip install --upgrade pip, sigh. cargo install-update -a, wait, I need a plugin for that? rustup update, oh right, that’s separate. npm update -g, you’re still doing this?

It’s 2026. You should not be maintaining a personal shell alias that chains fifteen update commands together and breaks every time you install something new. That alias is load-bearing spaghetti and you know it.

Enter Topgrade.


What Topgrade Actually Does

Topgrade is a single binary that detects every package manager, runtime, and update mechanism on your system and runs them all in sequence. One command. Real output. Failures reported cleanly.

It handles:

And if it can’t figure out how to update something, it skips it and tells you. No silent failures. No mystery.

It’s written in Rust, installs as a static binary, and the current release as of mid-2026 is Topgrade 15.x. The project lives at github.com/topgrade-rs/topgrade.


Installing It

On Arch/Manjaro (AUR)

Terminal window
yay -S topgrade

Or with paru:

Terminal window
paru -S topgrade

On Debian/Ubuntu

No official PPA yet. Grab the binary directly:

Terminal window
curl -s https://api.github.com/repos/topgrade-rs/topgrade/releases/latest \
| grep "browser_download_url.*linux-x86_64.tar.gz" \
| cut -d '"' -f 4 \
| xargs curl -L -o /tmp/topgrade.tar.gz
tar -xzf /tmp/topgrade.tar.gz -C ~/.local/bin/
chmod +x ~/.local/bin/topgrade

Make sure ~/.local/bin is in your $PATH. If it isn’t, that’s a separate problem you should fix anyway.

On macOS

Terminal window
brew install topgrade

Yes, Homebrew will also be one of the things Topgrade updates. The snake eats its own tail. It’s fine.

Via Cargo (if you’re already a Rust person)

Terminal window
cargo install topgrade

Running It

Terminal window
topgrade

That’s it. Topgrade detects what’s installed, runs each step, and prints a summary at the end. Steps that succeed show a checkmark. Steps that fail show the error. Steps that aren’t applicable are skipped silently.

Here’s what a typical run looks like on an Arch box with a handful of language runtimes:

[Topgrade]
─────────────────────────────────────────────────────
System
[✔] Arch Linux: Upgrading packages
[✔] AUR (yay): Upgrading AUR packages
[✔] Flatpak: Updating system packages
Rust
[✔] rustup: Updating toolchains
[✔] cargo-update: Updating installed binaries
Python
[✔] pipx: Upgrading all packages
Node
[✔] npm: Upgrading global packages
Miscellaneous
[✔] Neovim: Updating plugins (lazy.nvim)
[✔] VS Code: Updating extensions
─────────────────────────────────────────────────────
Finished. 0 errors.

The whole thing takes two to five minutes depending on how stale your system is and how fast your internet connection is. Compare that to running each of those manually, waiting for output, forgetting one, and remembering at 2 AM that you haven’t updated your pip globals in three months.


Configuration

Topgrade reads from ~/.config/topgrade.toml. You probably don’t need to touch it immediately, but here are the parts worth knowing about.

Skip steps you don’t want

Maybe you manage your Neovim plugins yourself, or you don’t want Topgrade touching your npm globals because you know that’ll break something. Skip them:

~/.config/topgrade.toml
[misc]
disable = ["node", "neovim"]

Available step names match what Topgrade prints during a run. Run topgrade --list-steps to see every step name your system has.

Add custom commands

This is where Topgrade gets genuinely useful. You can define your own update commands that run inside the Topgrade flow:

~/.config/topgrade.toml
[commands]
"Update my dotfiles" = "cd ~/.dotfiles && git pull --ff-only"
"Sync Helm repos" = "helm repo update"
"Update kubectl plugins" = "kubectl krew upgrade"

These run as regular shell commands and their output is captured like any other step. If the command exits non-zero, it shows up in the failure summary.

Cleanup after upgrades

On Arch, you probably want to clear the package cache after upgrading. Topgrade can do that:

~/.config/topgrade.toml
[linux]
arch_package_cache_cleanup = true

On macOS with Homebrew:

~/.config/topgrade.toml
[brew]
cleanup = true

Require sudo upfront

If you hate being interrupted mid-run for a sudo prompt, use:

~/.config/topgrade.toml
[misc]
pre_sudo = true

This forces a sudo credential refresh at the start so you can walk away and let it finish.

A complete realistic config

Here’s what a reasonably opinionated config looks like for a Linux developer with Rust, Python, and Node in the mix:

~/.config/topgrade.toml
[misc]
pre_sudo = true
cleanup = true
disable = ["flutter", "opam", "stack"]
[linux]
arch_package_cache_cleanup = true
[commands]
"Update dotfiles" = "git -C ~/.dotfiles pull --ff-only --quiet"
"Helm repo sync" = "helm repo update 2>/dev/null || true"
"kubectl krew" = "kubectl krew upgrade 2>/dev/null || true"

The || true on optional tools prevents Topgrade from counting a missing binary as a failure. Useful for commands that might not be installed on every machine where you share your config.


Handling Failures Gracefully

Topgrade doesn’t stop on failure by default. It runs everything and reports failures at the end. That’s the right call, a broken pip environment shouldn’t block your Arch system packages from updating.

If you want it to stop on the first failure (useful for scripted/CI scenarios):

Terminal window
topgrade --fail-fast

You can also run a dry-run to see what would happen without doing anything:

Terminal window
topgrade --dry-run

This is handy after you’ve edited your config and want to sanity-check that you haven’t accidentally disabled something important.


Topgrade vs. Your Existing Alias

You probably have something like this in your .bashrc or .zshrc:

Terminal window
alias update='sudo apt update && sudo apt upgrade -y && flatpak update -y && pip install --upgrade pip 2>/dev/null'

Honestly, that’s fine for basic cases. But here’s where it falls apart:

It doesn’t adapt. Install Rust next month? Add it to the alias manually, or it won’t be updated. Install pipx? Same thing. Your alias is a static snapshot of your system state at the time you wrote it, which was probably when you first set up the machine and then never touched again.

It has no error handling. One command fails, the whole chain is at best uncertain. You don’t get a clean summary of what succeeded and what didn’t.

It doesn’t handle interactive prompts. Some package managers ask you things. Topgrade knows how to handle these in a non-interactive way for most common tools.

It doesn’t update itself. Topgrade updates Topgrade. Your alias can’t do that.

The alias is a good-enough solution for a system with two package managers. Once you’re running four or five, which is most developer machines these days, Topgrade is the right tool.


Running It Automatically

If you want zero-maintenance updates, you can schedule Topgrade with a systemd user timer or a cron job. For a daily run at 8 AM:

Terminal window
crontab -e
0 8 * * * /usr/bin/topgrade --disable neovim --yes 2>&1 | logger -t topgrade

The --yes flag skips interactive confirmations. The logger pipe sends output to syslog so you can check it later with journalctl -t topgrade.

On macOS you can use a launchd plist or just run it in your terminal emulator’s startup config if you open a terminal every morning anyway.

Personally, I don’t run it automatically. I run it when I’m about to start working on something new, as a “let me make sure I’m not on a six-month-old version of something before I debug this.” Takes two minutes while coffee brews.


The One Gotcha Worth Knowing

Topgrade updates things. Sometimes, things break when they’re updated. This is not Topgrade’s fault, it’s the same risk you take when you run apt upgrade or brew upgrade. Topgrade just aggregates the risk into one convenient event.

For most tools, this is fine. For global npm packages or pip packages used by scripts you rely on daily, you might want to exclude those from automatic updates and manage them manually:

~/.config/topgrade.toml
[misc]
disable = ["node"]

Or use pipx instead of pip for globally installed Python tools, since pipx isolates each tool in its own virtualenv. Topgrade handles pipx natively and updates each tool individually. If one breaks, the others are unaffected. This is the right way to manage global Python tooling in 2026, regardless of Topgrade.


Should You Bother?

If your machine has two package managers and you remember to update them regularly, probably not. Your alias is fine.

If your machine has four or more, apt plus flatpak plus cargo plus pipx, or brew plus npm plus gem plus some custom tooling, then yes, absolutely. Topgrade is a fifteen-second install that saves you the mental overhead of remembering to update things you installed three months ago and haven’t thought about since.

It’s the kind of tool that you set up once, run weekly, and then immediately forget about because it just works. The summary at the end is genuinely useful: you’ll catch outdated cargo binaries you forgot you installed, or a VS Code extension that hasn’t updated in months because you always close that prompt.

It’s not magic. It’s just a well-built wrapper around the tools you already have, with better error reporting and zero maintenance overhead. That’s enough.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it'll show up above once verified.


Previous Post
systemd-homed: Portable Encrypted Home Directories
Next Post
Local Voice Assistant: Whisper + Piper + Home Assistant

Discussion

Powered by Garrul . Sign in with GitHub or Google, or post anonymously.

Related Posts