Why I Built a Package Manager That Teaches While It Runs
I don’t like magic.
When something breaks at 2 AM, I don’t want to stare at a tool that “just works” and wonder what it’s doing. I want to know exactly what’s happening so I can fix it.
That’s why I built apkg - a wrapper around Gentoo’s package management that does the tedious work automatically, but shows you every command it runs. The goal isn’t to hide Gentoo’s complexity. It’s to manage it while teaching you.
The Problem: Gentoo Is Exhausting
Here’s what installing Discord looks like on vanilla Gentoo:
# Step 1: Find the package
$ eix discord
[I] net-im/discord
Available versions: ~0.0.40(~amd64)
# Step 2: Wait, it's masked
$ echo "net-im/discord ~amd64" >> /etc/portage/package.accept_keywords/discord
# Step 3: If using a binhost, check if it exists there first
$ ssh binhost "ls /var/cache/binpkgs/net-im/discord*"
# ...it doesn't exist
# Step 4: Build on binhost
$ ssh binhost "emerge net-im/discord"
# ...wait 10 minutes...
# Step 5: Sync the binary
$ rsync binhost:/var/cache/binpkgs/net-im/ /var/cache/binpkgs/net-im/
# Step 6: Actually install
$ emerge --usepkgonly net-im/discord
# Step 7: Handle config files
$ etc-update
# Step 8: Does this have a service?
$ qlist net-im/discord | grep init.d
# (nothing, this one doesn't)
That’s 8 commands to install a chat app. And if you forget to unmask on the binhost too? Silent compilation failure. If you skip the rsync? Emerge downloads the source instead. If you misconfigure USE flags? Broken binary.
After the hundredth package, I was done. I wanted:
$ apkg install discord
And to have it just work. But I also wanted to see exactly what was happening.
The Solution: Intelligent Automation That Explains Itself
Here’s what apkg install discord actually does:
┌─────────────────────────────────────────────────────────────┐
│ apkg install discord │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. RESOLVE PACKAGE │
│ • Search binhost eix: net-im/discord-0.0.40 │
│ ✓ Found: net-im/discord │
│ │
│ 2. CHECK MASKING │
│ • Status: ~amd64 (testing) │
│ • Prompted: "Accept testing version? [Y/n]" │
│ ✓ Added to /etc/portage/package.accept_keywords/discord │
│ ✓ Synced to binhost │
│ │
│ 3. CHECK BINARY AVAILABILITY │
│ • Binhost check: Not found │
│ • Building in tmux session... │
│ ✓ Build complete (8m 32s) │
│ ✓ Synced to local cache │
│ │
│ 4. INSTALL │
│ • Running: emerge --usepkgonly net-im/discord │
│ ✓ Installed │
│ │
│ 5. POST-INSTALL │
│ • Service check: None detected │
│ • Desktop integration: ✓ │
│ │
└─────────────────────────────────────────────────────────────┘
And at the end:
╔══════════════════════════════════════════════════════════════╗
║ What Just Happened ║
╠══════════════════════════════════════════════════════════════╣
║ 1. eix -Ae discord ║
║ → Search for package in database ║
║ ║
║ 2. echo "net-im/discord ~amd64" >> .../package.accept_keywords║
║ → Accept testing version ║
║ ║
║ 3. rsync binhost:/var/cache/binpkgs/... /var/cache/binpkgs/ ║
║ → Download pre-compiled binary ║
║ ║
║ 4. emerge --usepkgonly net-im/discord ║
║ → Install from binary package only ║
╚══════════════════════════════════════════════════════════════╝
This is the philosophy: Do the work. Explain the work. Make yourself unnecessary.
If I ever need to install Gentoo on a machine without apkg, I know exactly what to do. The tool teaches you its own replacement.
The Architecture
apkg isn’t a package manager. It’s a workflow coordinator for Gentoo’s existing tools.
┌─────────────────────────────────────────────────────────────┐
│ Your Desktop (Driver) │
│ │
│ apkg ─────────────────────────────────────────────────────┐│
│ │ ││
│ ├── eix (local) - Search packages ││
│ ├── emerge - Install packages ││
│ ├── equery - Query installed packages ││
│ ├── rc-service - Manage services ││
│ │ ││
│ └── SSH ───────────────── To Binhost ──────────────────┘│
│ │ │
└──────────────┼──────────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ Binhost (Compiler) │
│ │
│ Portage tree │
│ Binary package cache (/var/cache/binpkgs/) │
│ tmux sessions for long builds │
│ │
└─────────────────────────────────────────────────────────────┘
Key Insight: The Binhost/Driver Split
My desktop (“the driver”) never compiles. It only installs binaries.
A dedicated machine (“the binhost”) does all the compilation. It’s headless, it has good cooling, and it can churn through builds while I sleep.
The problem: keeping these two systems in sync is nightmarishly complex. If I unmask a package on my desktop but forget to unmask it on the binhost, compilation fails. If USE flags differ, I get broken packages.
apkg solves this by treating both machines as one system. When you unmask a package, it unmasks on both. When you install, it checks the binhost first. When you update, both systems stay synchronized.
Command Reference
Here’s everything apkg does:
Package Operations
| Command | What It Does |
|---|---|
apkg install <pkg> | Full workflow: resolve → unmask → build/fetch → install |
apkg uninstall <pkg> | Remove with service cleanup |
apkg search <term> | Search all packages (local + overlays) |
apkg info <pkg> | Detailed package information |
apkg status <pkg> | Where is it installed? Binhost? Local? Both? |
apkg list [local|binhost] | List installed packages |
System Operations
| Command | What It Does |
|---|---|
apkg update | Full system update from binhost |
apkg sync | Download all new binaries from binhost |
apkg sync-repos | Sync Portage tree on both systems |
apkg clean | System maintenance (old kernels, temp files) |
Build Operations
| Command | What It Does |
|---|---|
apkg builds | Show active build sessions on binhost |
apkg monitor <session> | Watch a tmux build session |
apkg log <project> | View build log |
apkg github <url> | Build from GitHub (prefers overlays) |
System Administration (Welcome)
| Command | What It Does |
|---|---|
apkg welcome | TUI system control panel |
apkg welcome --gui | Graphical system control panel |
The Welcome Panel
apkg welcome is a control panel I built after getting tired of typing the same diagnostic commands:
╔═══════════════════════════════════════════════════════════════════════════╗
║ ARGO OS CONTROL PANEL ║
╠═══════════════════════════════════════════════════════════════════════════╣
║ SYSTEM ║
║ Hostname: Canopus-Outpost ║
║ Kernel: 6.12.9-gentoo ║
║ Uptime: 4 days, 12:34:56 ║
║ Profile: default/linux/amd64/23.0/desktop/plasma ║
╠═══════════════════════════════════════════════════════════════════════════╣
║ HEALTH ║
║ Disk: / [████████░░░░░░░░░░░░] 42% (186G free) ║
║ Memory: [██████████░░░░░░░░░░] 51% (16G/32G) ║
║ Services: 47 running, 0 failed ║
╠═══════════════════════════════════════════════════════════════════════════╣
║ HARDWARE ║
║ GPU: NVIDIA RTX 4070 Ti @ 42°C ║
║ Logitech: G Pro Wireless [████████████████████] 95% ║
╠═══════════════════════════════════════════════════════════════════════════╣
║ BUILD SWARM ║
║ Gateway: ✓ 10.42.0.199 ║
║ Orchestrator: ✓ 10.42.0.201 (primary) ║
║ Drones: 4 online (62 cores) ║
║ Queue: 0 packages ║
╠═══════════════════════════════════════════════════════════════════════════╣
║ [u] Update [h] Health [c] Check [d] Doctor [x] Clean [r] Refresh ║
║ [g] GUI [q] Quit ║
╚═══════════════════════════════════════════════════════════════════════════╝
Press g for a full graphical interface with service management, snapshot control, and package browsing.
Smart Behaviors
Automatic Unmasking Synchronization
When you install a masked package:
$ apkg install discord
Package net-im/discord is masked (~amd64).
Accept testing version? [Y/n]: y
✓ Added: /etc/portage/package.accept_keywords/discord (local)
✓ Added: /etc/portage/package.accept_keywords/discord (binhost)
Both systems know about the unmask. No more “but it compiled yesterday” confusion.
Intelligent GitHub Builds
When you run apkg github https://github.com/user/project:
- Check Portage tree - Maybe the package already exists?
- Check overlays - It might be in GURU (like Arch’s AUR)
- Offer to enable GURU - One command to add the overlay
- Rust projects: cargo-ebuild - Generate a proper ebuild automatically
- Last resort: direct build - Compiles but warns about unmanaged install
$ apkg github https://github.com/sharkdp/bat
Searching for existing ebuild...
✓ Found in GURU overlay: sys-apps/bat-0.24.0
GURU is not currently enabled.
Enable GURU overlay? [Y/n]: y
✓ Enabled GURU overlay
✓ Building sys-apps/bat...
Service Detection
After installing a package, apkg checks if it provides services:
$ apkg install syncthing
✓ Installed app-misc/syncthing
Service detected: /etc/init.d/syncthing
Enable and start? [Y/n]: y
✓ Added to default runlevel
✓ Started syncthing
It detects both OpenRC and systemd services. No more forgetting to enable something after install.
Configuration
/etc/apkg/config.sh
# Binhost connection
BINHOST_ADDRESS="10.42.0.194"
BINHOST_USER="root"
BINHOST_PKGDIR="/var/cache/binpkgs"
LOCAL_BINPKGDIR="/var/cache/binpkgs"
# Features
SHOW_LEARNING_SUMMARY="yes" # Show "What Just Happened" box
AUTO_SERVICE_SETUP="prompt" # "yes", "no", or "prompt"
Driver machine /etc/portage/make.conf
# Use binhost for packages
PORTAGE_BINHOST="ssh://[email protected]/var/cache/binpkgs"
FEATURES="getbinpkg"
EMERGE_DEFAULT_OPTS="${EMERGE_DEFAULT_OPTS} --usepkg --getbinpkg"
Troubleshooting
”Binary package not found” but it exists on binhost
-
Check SSH connectivity:
ssh [email protected] echo ok -
Verify package path format (GPKG uses subdirectories):
# Correct: /var/cache/binpkgs/net-im/discord-0.0.40.gpkg.tar # Wrong: /var/cache/binpkgs/discord-0.0.40.gpkg.tar -
Clear cache and retry:
sudo rm -rf /var/cache/binpkgs/* apkg sync
Build never completes on binhost
-
Check active sessions:
apkg builds -
Attach to watch:
apkg monitor build-discord -
Common causes: disk full, dependency conflict, network timeout.
Why Not Just Use emerge?
You can! apkg just automates the tedious parts:
| Task | emerge | apkg |
|---|---|---|
| Install package | emerge package | apkg install package |
| Handle masking | Edit file, re-run | Automatic |
| Binary fetch | If configured | Explicit with status |
| Service setup | Manual | Automatic detection |
| Cross-machine sync | Manual | Automatic |
| Learning what happened | Read logs | ”What Just Happened” box |
If you’re comfortable with raw Portage, emerge works great. apkg is for when you want the convenience without losing visibility.
The Philosophy: Teach Yourself Out of a Job
The best tools make themselves unnecessary.
apkg shows you every command it runs. Every file it edits. Every SSH call it makes. After using it for a month, you’ll know how to do everything manually.
That’s the point.
I don’t want to be dependent on my own wrapper. I want to understand Gentoo deeply enough that I could operate without it. apkg is training wheels that show you how the bike works.
When it inevitably breaks (and all software breaks), I won’t be stuck. Neither will you.
apkg is part of the Argo OS project, a custom Gentoo distribution built around binary package distribution and bulletproof rollback.
See also:
- How I Solved Gentoo’s Compile Problem — The Build Swarm
- Hardening the Build Swarm — Fleet security and ghost drones
- Btrfs & Snapper Guide — The snapshot system that makes rollback possible