Joel Elizaga


Conveniently & consistently install packages en masse

instally is a portable interactive CLI script for conveniently & consistently installing packages en masse on Linux, featuring:

  • 🚚 JSON-driven flexibility: Specify packages for instally to install using JSON, enjoying support for:
    • organizing packages in groups,
    • preferring methods of installation on a package-by-package basis,
    • fallback installation methods,
    • OS-specific installation methods,
    • 9 different package managers,
    • and even running your own installation commands!
  • Minimal dependencies: Owing to instally’s simplicity, you can bring instally to any Linux distribution capable of running Bash and jq.
  • 💼 Superb portability: With instally and your own custom package.json file, you can bring your favorite packages to (almost) any distro and install them right away.

Visit instally’s GitHub page for the full manual.

User Experience

The UI guidelines we use today for heuristic analysis were coded during the 1960s and 70s for command-line interface programs: instally is a CLI script, but I still used the same UX principles a designer would apply to, say, a mobile app or a website.

User & Use-Case

instally’s primary use-case is for the developer or Linux user (aficionado?) who is going to open their terminal upon installing their OS. This is because they’re about to invoke apt, dnf, or zypper with a laundry list of packages to install.

With that in mind, installing and using instally is pretty trivial:

# Download instally off GitHub, unarchive, and run instally:
wget -P ~/Downloads \
tar -xzvf ~/Downloads/v0.23.tar.gz
cd ~/Downloads/instally-0.23

Alternatively (and most effectively), instally makes a great git submodule or script in a dotfiles repo.

This is because a user on a fresh install will:

  1. install git (if it’s missing)
  2. clone their .dotfiles repo (to import & symlink their configs)

If instally is already included in their .dotfiles along with a package.json, all the user has to do is run instally to select their packages for installation.

Given that instally supports 9 different package managers and OS-specific installation methods, this process of installing their package environments is made a whole lot easier: Users don’t have to remember what packages to install, or even what package IDs to use for which package managers on particular operating systems.

Basically, we’re leveraging recognition over recall.

Recognition > Recall

For example, on Debian-based distros Taskwarrior can be installed like so:

sudo apt install taskwarrior

But on RHEL-based distros, this same application is installed with a different ID (task instead of taskwarrior):

sudo dnf install task

If the user recently switched from a Debian-based distro to Fedora, they’ll be running sudo dnf install taskwarrior, get a message from dnf saying, “No, taskwarrior doesn’t exist,” and either try sudo dnf install task out of instinct or try searching dnf for the package—which is a bit of a waste of cognition, especially if they’re installing dozens (or hundreds) of packages just like this.

This is the inefficiency of recall; users drawing package IDs from fallible memory.

Using instally, we can avoid this inefficiency entirely:

  "name": "Taskwarrior",
  "description": "command-line todo list for staying on-task",
  "apt": {
    "id": "taskwarrior"
  "dnf": {
    "id": "task"
  "zypper": {
    "id": "taskwarrior"

↑ Taskwarrior’s the same app, but now we have three different installation methods depending on whichever package manager’s available.

Now all the user has to do is recognize Taskwarrior in instally and select it for installation:

No memorization; no recall—users know they’ll be getting Taskwarrior without fussing with package ID discrepencies and package managers.

IxD Grammar

instally has a dense IxD grammar:






Package Manager

Installation Method




The actions (install, select, and edit) for instally’s objects are consistent: this consistent vocabulary reduces possible cognitive load and ambiguity, training a perceptual pattern with which the user learns to navigate their instally experience: instally is all about selecting and installing, and any object of instally is conceivably editable.

Information Architecture


instally has three colors, each with their own function:

  1. Active color - Highlights ‘active’ menu items and suggested keystrokes the user can perform.
  2. Accent color - Partially decorative, but also used to embellish important information, such as the package report at the end of install runs.
  3. Selected color - Used to denote menu items and selections made.

This simplicity and consistency makes instally easy to use.


Typography is used to make information scannable. For example, instally bolds packages being installed, dependencies, and package managers (software) due to their importance:

↑ Instructions and prompts are consistently italicized, whereas output and instally’s operations are plain.

instally’s ‘sections’ are separated by a line break and each have a bolded and underlined heading, making it completely unambiguous where the user ‘is’ and what’s going on:

This creates a typographical ‘language’ that users can easily scan with minimal focus.


While installing packages, consistent, emoji-based iconography is used to further make information effortlessly scannable:

  • 🎁 - Installed packages,
  • 👍 - Already installed packages,
  • - Packages that could not be installed,
  • - Missing (but expected) packages,
  • - Ambiguously installed packages (shell commands with non-0 exit codes),
  • 🌎 - Installation in-progress,
  • 👉 - Helpful tips (usually on resolving errors),
  • - Updated package indices (apt update, for example),
  • ⚠️ - Warnings (for user-defined shell commands being executed, for example),
  • 📒 - package.json,
  • 🐛 - Bugs & errors.

With a cursory glance down the left side of the terminal, a user can assess the progress of their installation run:

If they’d like further detail, they can actually read the messages—not that it’s necessary; the whole point of instally is to reduce mental investment into the installation process.


This is going to get a bit in-the-weeds:

I wrote instally between Pop!_OS 22.04 and System76’s next LTS release, Pop!_OS 24.04, because there was a whole two years between 22.04 and 24.04 during which I anticipated making a clean reinstall of Pop! due to the projected addition of the new COSMIC desktop environment to Pop!_OS 24.04.

I didn’t (and still don’t—24.04 isn’t out yet) know if COSMIC was going to be a polished enough experience to warrant using Pop!_OS 24.04, it being a new desktop environment and all, so instally’s my way of bringing my system to whatever Linux distribution I ultimately end up using, Pop!_OS or not.

I chose to parse package.json with jq is because I wanted to learn how to use it and JSON lends itself handily to nesting data; the shape of package.json was self-evident from the get-go. I wanted to minimize instally’s dependencies, but I also didn’t want to wrangle awk into being the thing reading the package file, because awk looked like it’d take much longer to grok than jq and asking ChatGPT to hold my hand through that one would feel like cheating.


I learned a whole lot of Bash I wouldn’t have otherwise known while writing instally, and ultimately came to the conclusion that something much more polished could’ve been made in Python with a TUI library instead. Of course, I’d have to call this hypothetical sequel to instally installyy.

The fact that it’s in Bash really does make instally portable, though, and its dependencies can all be grabbed as binaries. It’s not a very economical script, but it’s no-fuss.

For all the UX principles used to design instally, I haven’t user-tested it. Like a lot of FOSS TUI stuff, its use-case is niche and I think it serves its purpose effectively enough to not warrant whittling it into something wholly refined. If I’m going to do some serious testing, it’d have to be on something with a larger user demographic and better incentives beyond sating personal curiosity.

That said, I think instally has its place, which is mostly in my .dotfiles repo where I can set it and forget it while it installs a bunch of packages and runs a few scripts whenever I make a clean install or spin a virtual machine.

Headshot of Joel Elizaga.

Joel Elizaga

Joel Elizaga is a UX engineer based in the Pacific Northwest.