Void Linux Install: The Minimalist Distro That Feels Surprisingly Modern

Was this helpful?

You know the feeling: you’re installing “a minimal Linux” and somehow you end up with a shrine of interdependent daemons,
boot-time magic, and a package manager that wants a committee meeting before it will let you remove one library.
Void Linux is the rare minimalist distro that doesn’t feel like punishment.

It’s lean, fast to boot, and unapologetically simple—without being stuck in 2009.
If you run production systems (or you just think like someone who does), Void’s design choices land in the right places:
predictable init, straightforward packaging, and a base install that is actually a base.

Why Void feels modern despite being minimalist

Void Linux is minimal in a way that makes operational sense. You don’t get a tangle of “framework services” you didn’t ask for.
You get a clean filesystem, a sane package manager, and an init system that prefers clarity over ambition.
That last part matters: the reliability profile of a system is often dictated by boot and service supervision.

Void uses runit for init and supervision. It’s not fashionable. It is, however, remarkably direct:
services are directories with run scripts; enablement is a symlink; logs can be managed with a companion service.
When something fails, you can usually see exactly what it tried to execute and why it didn’t.

The package manager is xbps. It’s fast, consistent, and refreshingly untheatrical.
There’s no “dependency solver opera” before every transaction. It installs packages, verifies integrity, and moves on.
For a minimalist distro, that’s the right modernity: focus on the mechanics that reduce operational friction.

One quote that should be printed on the inside of every laptop lid:
Paraphrased idea (Werner Vogels): everything fails eventually; design systems so failure is expected and recoverable.

Void’s strength is that it doesn’t pretend failure won’t happen. It gives you fewer moving parts and better visibility.
That doesn’t eliminate mistakes. It just makes them easier to diagnose, and that’s the actual win.

Interesting facts and context (the parts people forget)

  • Void is independent: it’s not a Debian/Ubuntu/Fedora derivative. That independence shows up in tooling and defaults.
  • runit lineage: runit dates back to the early 2000s and has been used in production-grade contexts like embedded and appliances.
  • xbps is not a wrapper: it’s a native package system (binary packages + repositories), not a frontend for something else.
  • glibc and musl builds: Void maintains both, letting you choose compatibility (glibc) or a smaller libc (musl).
  • Rolling release, but curated: it’s rolling, yet packages tend to be reasonably coherent—less “daily rebuild roulette.”
  • Installer philosophy: the live ISO is intentionally spartan; you do not get a wizard that hides the disk layout behind cheerful animations.
  • Runit’s stage model: boot is split into stages, and the “service supervision tree” is explicit rather than emergent.
  • xbps-alternatives: Void uses an alternatives system to manage default implementations (e.g., which editor is “vi”).
  • XBPS keeps metadata locally: you can inspect what’s installed and why with simple queries, without calling home.

Joke 1/2: Void is called “Void” because it starts empty; the upside is it also starts without drama.

Decisions you must make before booting the installer

glibc vs musl

Pick glibc unless you have a specific reason not to. Most third-party binaries assume it.
musl is excellent for certain workloads (static-ish, containers, smaller systems), but it can be a compatibility tax
you don’t want on a daily-driver or workstation.

UEFI vs legacy BIOS

If your machine is from the last decade, you’re almost certainly on UEFI. Use it properly:
GPT partitioning, an EFI System Partition (ESP), and a bootloader that matches your threat model and tolerance for tinkering.

Filesystem choice (the boring fork that matters later)

If you want one filesystem that behaves, choose ext4.
If you have huge files and care about parallel I/O, XFS is fine (but treat resizing with respect).
If you want snapshots and are willing to learn operational discipline, btrfs can be great.
If you’re a storage person and you want checksumming + send/receive at scale, ZFS is a legitimate choice—just don’t pretend it’s “set and forget.”

Encryption

For laptops and anything that leaves a rack, use full-disk encryption. LUKS2 is the normal move.
The two real questions: do you want a separate unencrypted /boot, and do you need remote unlock?
If you don’t know, keep it simple: unencrypted ESP, encrypted root, and accept typing a passphrase at boot.

Swap and hibernation

If you hibernate, you need swap sized appropriately and configured correctly.
If you don’t, swap is still useful as a pressure valve. A small swapfile or partition is fine.
Don’t cargo-cult “no swap ever” unless you enjoy watching compilers get murdered by the OOM killer.

Installation: a practical walkthrough (UEFI + ext4 + encryption option)

Void’s installer is straightforward. The most common failure mode is not “the installer is broken.”
It’s “you made a disk layout assumption you didn’t verify.”
So we’ll verify everything as we go, and we’ll prefer commands that show their work.

Step 0: boot the live ISO and confirm firmware mode

If you think you’re in UEFI mode but you’re not, your ESP will sit there like a well-labeled drawer in a house with no doors.

cr0x@server:~$ ls /sys/firmware/efi
config_table  efivars  fw_platform_size  runtime  runtime-map  systab

What the output means: If /sys/firmware/efi exists and contains entries, you booted in UEFI mode.
Decision: Proceed with GPT + ESP. If it’s missing, reboot and select the UEFI boot entry in firmware.

Step 1: identify the target disk (don’t guess)

cr0x@server:~$ lsblk -o NAME,SIZE,TYPE,FSTYPE,MODEL
NAME   SIZE TYPE FSTYPE MODEL
sda   476.9G disk        Samsung SSD 860
├─sda1  512M part vfat
└─sda2 476.4G part

What the output means: You see disks and partitions, with sizes and (if present) filesystem types.
Decision: Confirm you’re installing to the right device (/dev/sda here). If you’re on a server with multiple disks, stop and label them.

Step 2: partition (example layout)

Minimal UEFI layout:
an ESP (512 MiB) + a root partition (the rest). If encrypting, root becomes a LUKS container.

cr0x@server:~$ sudo parted -s /dev/sda mklabel gpt
cr0x@server:~$ sudo parted -s /dev/sda mkpart ESP fat32 1MiB 513MiB
cr0x@server:~$ sudo parted -s /dev/sda set 1 esp on
cr0x@server:~$ sudo parted -s /dev/sda mkpart primary 513MiB 100%
cr0x@server:~$ sudo parted -s /dev/sda print
Model: Samsung SSD 860 (scsi)
Disk /dev/sda: 512GB
Partition Table: gpt

Number  Start   End     Size    File system  Name  Flags
 1      1049kB  538MB   537MB                ESP   boot, esp
 2      538MB   512GB   511GB                primary

What the output means: GPT exists, partition 1 is flagged as ESP, partition 2 is the main payload.
Decision: If flags don’t show boot, esp on partition 1, fix that now. UEFI boot issues are usually self-inflicted.

Step 3: format filesystems (unencrypted path)

cr0x@server:~$ sudo mkfs.vfat -F32 -n EFI /dev/sda1
mkfs.fat 4.2 (2021-01-31)
cr0x@server:~$ sudo mkfs.ext4 -L void-root /dev/sda2
mke2fs 1.46.5 (30-Dec-2021)
Creating filesystem with 124999680 4k blocks and 31250432 inodes
Filesystem UUID: 9f6b7f39-3c4a-4cb2-8f54-5c3c2f9911a2
Superblock backups stored on blocks:
	32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968, 102400000

What the output means: ESP is FAT32, root is ext4 with a label and UUID.
Decision: If you plan to use encryption, do not format /dev/sda2 as ext4 yet—use the encrypted path next.

Step 3b: encryption path (LUKS2 + ext4 inside)

cr0x@server:~$ sudo cryptsetup luksFormat /dev/sda2
WARNING: Device /dev/sda2 already contains a 'ext4' superblock signature.
Are you sure? (Type 'yes' in capital letters): YES
Enter passphrase for /dev/sda2:
Verify passphrase:
cr0x@server:~$ sudo cryptsetup open /dev/sda2 cryptroot
Enter passphrase for /dev/sda2:
cr0x@server:~$ sudo mkfs.ext4 -L void-root /dev/mapper/cryptroot
mke2fs 1.46.5 (30-Dec-2021)
Creating filesystem with 124900352 4k blocks and 31227904 inodes
Filesystem UUID: 5c1a1d6b-6a4f-4b2e-88fb-f0b4af56c0a3

What the output means: The partition is now an encrypted container, opened as /dev/mapper/cryptroot, and ext4 lives inside it.
Decision: Record the LUKS UUID later for crypttab/dracut. Also decide if you need a keyfile (usually no on laptops).

Step 4: mount target layout

cr0x@server:~$ sudo mount /dev/mapper/cryptroot /mnt
cr0x@server:~$ sudo mkdir -p /mnt/boot/efi
cr0x@server:~$ sudo mount /dev/sda1 /mnt/boot/efi
cr0x@server:~$ mount | grep -E '(/mnt|efi)'
/dev/mapper/cryptroot on /mnt type ext4 (rw,relatime)
/dev/sda1 on /mnt/boot/efi type vfat (rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,errors=remount-ro)

What the output means: Root and ESP are mounted where the installer will place the system and boot artifacts.
Decision: If ESP is mounted anywhere else, fix it. You want a single, obvious ESP mountpoint.

Step 5: run the installer and keep it honest

Void provides a TUI installer. Use it. But cross-check what it did: you’re the on-call engineer now, not the installer.

After installation, chroot or boot into the new system and verify the fundamentals: fstab, bootloader, initramfs, networking, time, users.
The rest is preferences.

Step 6: first boot validation mindset

Your first boot is not “does it reach a login prompt.” Your first boot is “do I understand how it will behave on the worst day of my week.”
That means: how services start, where logs go, how updates are applied, and how the box is recovered if networking is broken.

Practical tasks: commands, outputs, and what you decide next

These are the checks I do on any fresh install. They’re not ceremonial.
Each one is there because I’ve watched the absence of it turn into a ticket.

Task 1: confirm you’re booted with the kernel you think you installed

cr0x@server:~$ uname -a
Linux voidbox 6.6.15_1 #1 SMP PREEMPT_DYNAMIC Thu Jan 18 10:22:01 UTC 2024 x86_64 GNU/Linux

What the output means: Kernel version and architecture.
Decision: If the kernel is older than expected, you likely booted the wrong entry or didn’t install the kernel meta package you intended.

Task 2: check disk UUIDs and labels before writing fstab

cr0x@server:~$ blkid
/dev/sda1: LABEL_FATBOOT="EFI" LABEL="EFI" UUID="7C2A-1B3D" BLOCK_SIZE="512" TYPE="vfat" PARTLABEL="ESP" PARTUUID="e2b7c5a1-6d8d-4e5c-a2b3-7f3a0d4b9b0c"
/dev/sda2: UUID="f1b2c3d4-1111-2222-3333-abcdefabcdef" TYPE="crypto_LUKS" PARTUUID="7a9c8d6e-aaaa-bbbb-cccc-1234567890ab"
/dev/mapper/cryptroot: LABEL="void-root" UUID="5c1a1d6b-6a4f-4b2e-88fb-f0b4af56c0a3" BLOCK_SIZE="4096" TYPE="ext4"

What the output means: Stable identifiers for mounts and encryption mapping.
Decision: Use UUIDs in /etc/fstab and (if encrypted) in your dracut cmdline/crypt config; avoid raw /dev/sdaX.

Task 3: validate mount options and available space

cr0x@server:~$ df -hT
Filesystem              Type   Size  Used Avail Use% Mounted on
/dev/mapper/cryptroot   ext4   468G  2.6G  441G   1% /
/dev/sda1               vfat   511M   12M  500M   3% /boot/efi
tmpfs                   tmpfs  3.1G     0  3.1G   0% /run

What the output means: Filesystem types, sizes, and mountpoints.
Decision: Ensure /boot/efi is not on the root filesystem. If it is, your UEFI boot artifacts might not be persistent.

Task 4: check init is actually runit

cr0x@server:~$ ps -p 1 -o pid,comm,args
  PID COMMAND         COMMAND
    1 runit           runit

What the output means: PID 1 is runit.
Decision: If PID 1 isn’t runit, you’re not on Void as expected, or you’re in a container/chroot context.

Task 5: list enabled services (what will start on boot)

cr0x@server:~$ ls -l /var/service
total 0
lrwxrwxrwx 1 root root  16 Jan 20 09:10 dhcpcd -> /etc/sv/dhcpcd
lrwxrwxrwx 1 root root  14 Jan 20 09:10 sshd -> /etc/sv/sshd
lrwxrwxrwx 1 root root  15 Jan 20 09:10 socklog-unix -> /etc/sv/socklog-unix
lrwxrwxrwx 1 root root  13 Jan 20 09:10 nanoklogd -> /etc/sv/nanoklogd

What the output means: Each symlink is an enabled service supervised by runit.
Decision: If something critical is missing (networking, logging, ssh for remote boxes), enable it now rather than discovering it during an outage.

Task 6: check service status like an adult (not via vibes)

cr0x@server:~$ sudo sv status sshd
run: sshd: (pid 842) 37s

What the output means: Service is running, with PID and uptime.
Decision: If it says down or fail, open the run script and logs before changing random configs.

Task 7: verify logs exist and are being collected

cr0x@server:~$ sudo sv status socklog-unix
run: socklog-unix: (pid 520) 5m12s
cr0x@server:~$ sudo ls -lah /var/log/socklog
total 16K
drwx------  4 root root 4.0K Jan 20 09:11 .
drwxr-xr-x 11 root root 4.0K Jan 20 09:11 ..
drwx------  2 root root 4.0K Jan 20 09:11 kernel
drwx------  2 root root 4.0K Jan 20 09:11 sshd

What the output means: socklog is running and writing per-service logs.
Decision: If logs are missing, fix logging before you “tune performance.” You can’t optimize what you can’t observe.

Task 8: verify repository configuration and perform a safe sync

cr0x@server:~$ xbps-query -L
 997 https://repo-default.voidlinux.org/current (RSA signed)
cr0x@server:~$ sudo xbps-install -S
[*] Updating repository `https://repo-default.voidlinux.org/current/x86_64-repodata' ...
x86_64-repodata: 1824KB [avg rate: 410KB/s]

What the output means: Repos are configured and metadata updates cleanly.
Decision: If signature verification fails, stop. Fix time sync, repository URLs, or key state before installing anything else.

Task 9: audit what’s holding your base system together

cr0x@server:~$ xbps-query -m | head
base-system-0.114_1
linux6.6-6.6.15_1
linux-firmware-20240115_1
runit-2.1.2_12
xbps-0.59.2_2

What the output means: Metapackages provide the “this looks like a system” baseline.
Decision: Keep base-system unless you have a well-documented reason to go off-road.

Task 10: check DNS resolution (because “network up” is not the same as “usable”)

cr0x@server:~$ getent hosts repo-default.voidlinux.org
2a04:4e42:600::644  repo-default.voidlinux.org
2a04:4e42::644       repo-default.voidlinux.org

What the output means: Name resolution works via NSS, returning IPs.
Decision: If this fails but you can ping an IP, the issue is DNS configuration—usually /etc/resolv.conf ownership or your DHCP client.

Task 11: confirm time sync (TLS and package signatures care)

cr0x@server:~$ date -u
Sat Jan 20 09:18:44 UTC 2024

What the output means: Current UTC time.
Decision: If it’s wrong by minutes/hours, fix NTP (e.g., chrony) before blaming “broken SSL” for package downloads.

Task 12: validate boot entries exist (UEFI reality check)

cr0x@server:~$ sudo efibootmgr -v
BootCurrent: 0003
Timeout: 1 seconds
BootOrder: 0003,0001
Boot0001* UEFI: Built-in EFI Shell	RC
Boot0003* VoidLinux	HD(1,GPT,e2b7c5a1-6d8d-4e5c-a2b3-7f3a0d4b9b0c,0x800,0x100000)/File(\EFI\void\grubx64.efi)

What the output means: Your firmware sees an entry for VoidLinux pointing to an EFI binary on the ESP.
Decision: If there’s no entry, you’ll be booting by luck. Fix bootloader installation before you reboot into confusion.

Task 13: inspect initramfs generation (especially for encryption)

cr0x@server:~$ ls -lh /boot | head
total 88M
-rw-r--r-- 1 root root  32M Jan 20 09:05 initramfs-6.6.15_1.img
-rw-r--r-- 1 root root  12M Jan 20 09:05 vmlinuz-6.6.15_1
drwxr-xr-x 3 root root 4.0K Jan 20 09:05 grub

What the output means: Kernel and initramfs exist and are sized plausibly.
Decision: If initramfs is missing, boot may fail after kernel updates. Ensure dracut hooks are installed and configured.

Task 14: confirm TRIM status on SSD (storage hygiene)

cr0x@server:~$ lsblk -D
NAME          DISC-ALN DISC-GRAN DISC-MAX DISC-ZERO
sda                  0      512B       2G         0
├─sda1               0      512B       2G         0
└─sda2               0      512B       2G         0

What the output means: Discard granularity and max discard size are exposed.
Decision: Enable periodic fstrim (or mount discard only if you accept its tradeoffs). If values are zero, the device may not support TRIM.

Task 15: verify memory pressure basics (don’t ship a box that dies on compile)

cr0x@server:~$ free -h
               total        used        free      shared  buff/cache   available
Mem:           7.7Gi       1.1Gi       5.6Gi       120Mi       1.0Gi       6.3Gi
Swap:          2.0Gi          0B       2.0Gi

What the output means: You have swap and reasonable available memory.
Decision: If swap is missing on a laptop, add it. If swap is huge on a small SSD, reconsider—wear is real, but so is stability.

runit: service management that won’t gaslight you

r u n i t is not a lifestyle brand. It’s a supervisor. It runs services, restarts them when they crash,
and provides a consistent interface to check status and manage them.
The mental model is: “a service is a directory with an executable run script.”

Enable and disable services

Enabling is a symlink into /var/service. Disabling removes that symlink.
This is so obvious that it feels suspiciously like it should be more complicated. It isn’t.

cr0x@server:~$ sudo ln -s /etc/sv/sshd /var/service
cr0x@server:~$ sudo sv status sshd
run: sshd: (pid 1123) 4s

What the output means: Once the link exists, runit supervises the service immediately.
Decision: If a service starts unexpectedly at enable-time, that’s not a surprise; it’s the contract. Plan changes accordingly.

Where configuration lives

Service run scripts live under /etc/sv/<service>/run. Many services also have /etc/sv/<service>/conf.
If you want to change how a service starts, you do it there, not by hoping a unit file generator will detect your mood.

Logging is a first-class concern (if you choose it)

A common pattern is a separate log service directory under the service, handled by svlogd.
That gives you predictable local logs without systemd-journal assumptions.
If you centralize logs (and you should, for anything important), keep local logs anyway.
They’re the black box recorder when the network is on fire.

Storage choices: ext4, XFS, btrfs, ZFS (and what I’d pick)

Storage is where minimalist distros either shine or implode. Void gives you options without insisting you use any of them.
That’s good. But it also means you can build a fragile snowflake.

ext4: the default you should not apologize for

ext4 is boring in the best sense. Recovery tools are mature, performance is consistent, and most failure modes are familiar.
If your goal is “workstation that always boots” or “server that never surprises,” ext4 is a strong baseline.

XFS: fast, stable, but respect the edges

XFS loves parallel I/O and large filesystems. It also expects you to be deliberate about growth and repair procedures.
If you’re used to casually resizing partitions and filesystems in random orders, learn the correct sequence first.

btrfs: features with operational obligations

Snapshots, subvolumes, send/receive—btrfs can make rollback and backups cleaner.
But btrfs isn’t “free reliability.” It’s “power tools.” You need monitoring, scrub schedules, and a plan for what snapshots mean for disk usage.

ZFS: great if you treat it like a storage system, not a filesystem

ZFS is excellent at data integrity (checksums) and replication workflows (send/receive).
It’s also a different ecosystem: kernel module considerations, memory expectations, and an operational model that rewards discipline.
If you’re installing Void on a laptop, ZFS root is possible, but I’d only recommend it if you already know why you want it.

Joke 2/2: Every time you say “I’ll just use ZFS for the snapshots,” a future you schedules an extra weekend shift.

Networking: Wi‑Fi, DHCP, DNS, and the boring parts

Networking isn’t hard. Networking is just allergic to assumptions.
Void doesn’t hide networking behind a monolithic manager unless you install one. You can keep it simple.

DHCP with dhcpcd

dhcpcd is commonly used on Void. Enable it, verify it’s running, and confirm it actually configured routes and DNS.

cr0x@server:~$ sudo sv status dhcpcd
run: dhcpcd: (pid 610) 8m44s
cr0x@server:~$ ip route
default via 192.168.1.1 dev enp3s0 proto dhcp src 192.168.1.50 metric 100
192.168.1.0/24 dev enp3s0 proto kernel scope link src 192.168.1.50 metric 100

What the output means: The default route exists and points to a gateway on the expected interface.
Decision: If the default route is missing, you don’t have “a DNS issue.” You have “no path to the internet.” Fix DHCP or static config first.

Wi‑Fi with wpa_supplicant (practical and explicit)

If you prefer NetworkManager, install it. If you prefer transparent configs, wpa_supplicant is fine.
The main requirement is: store configs with correct permissions and ensure the service starts at boot.

cr0x@server:~$ sudo install -d -m 0700 /etc/wpa_supplicant
cr0x@server:~$ sudo wpa_passphrase "CorpWiFi" "correct horse battery staple" | sudo tee /etc/wpa_supplicant/wpa_supplicant-wlp2s0.conf >/dev/null
cr0x@server:~$ sudo chmod 600 /etc/wpa_supplicant/wpa_supplicant-wlp2s0.conf
cr0x@server:~$ sudo ln -s /etc/sv/wpa_supplicant /var/service
cr0x@server:~$ sudo sv status wpa_supplicant
run: wpa_supplicant: (pid 1337) 2s

What the output means: Config is created, secured, and the service is running.
Decision: If it runs but doesn’t associate, check interface names and regulatory domain; don’t immediately blame “drivers.”

DNS: resolv.conf ownership wars

DNS failures often come from resolv.conf being overwritten (or not) by different components.
Decide who owns it: dhcpcd, resolvconf, NetworkManager, or your own static file. Then make it true.

cr0x@server:~$ ls -l /etc/resolv.conf
-rw-r--r-- 1 root root 46 Jan 20 09:12 /etc/resolv.conf
cr0x@server:~$ cat /etc/resolv.conf
nameserver 192.168.1.1
search lan

What the output means: A plain file with a local resolver and search domain.
Decision: If this keeps changing unexpectedly, identify the writer and either configure it properly or remove it.

Updates and rollback thinking with xbps

Void is rolling release. That’s fine in production if you treat updates as a controlled activity instead of a hobby.
You want: predictable update windows, logs of what changed, and a plan to recover from a bad boot.

Update workflow: sync, simulate if needed, then apply

cr0x@server:~$ sudo xbps-install -S
[*] Updating repository `https://repo-default.voidlinux.org/current/x86_64-repodata' ...
x86_64-repodata: 1824KB [avg rate: 390KB/s]
cr0x@server:~$ sudo xbps-install -u
[*] Updating package `openssl-3.2.0_1' ...
[*] Updating package `curl-8.5.0_1' ...
[*] Updating package `linux6.6-6.6.16_1' ...

What the output means: Repos synced; packages updated, including the kernel.
Decision: Kernel updates mean you should verify boot artifacts and reboot on your schedule, not when an application forces it.

Query what changed and why

cr0x@server:~$ xbps-query -R linux6.6 | head -n 10
pkgver: linux6.6-6.6.16_1
repository: https://repo-default.voidlinux.org/current
short_desc: Linux kernel and modules (6.6 series)
maintainer: Void Linux team
license: GPL-2.0-only
architecture: x86_64

What the output means: Package metadata and repository source.
Decision: If a package came from an unexpected repo, you may have mixed repositories. That’s how you build a system that updates like a haunted house.

Keep a bootable fallback kernel

On any system you care about, keep at least one known-good kernel installed.
It’s not paranoia; it’s a cheap insurance policy.
Verify your bootloader actually lists it, and that initramfs exists for it.

Fast diagnosis playbook

When Void “feels slow” or “won’t boot” or “network is weird,” you don’t need a philosophy seminar.
You need an order of operations that finds the bottleneck quickly and avoids thrashing.

First: determine the failure domain

  • Boot/firmware domain: can’t reach login, drops to emergency shell, or loops at bootloader.
  • Storage domain: I/O waits, filesystem read-only, services timing out, weird freezes.
  • Network domain: can ping gateway but not resolve DNS, or can resolve but not reach remote.
  • Service domain: system boots but app/service is down, flapping, or wedged.

Second: check the simplest observables with high information value

These checks are fast and don’t require you to believe anything.

Boot chain sanity

cr0x@server:~$ mount | grep -E ' /boot|/boot/efi '
/dev/sda1 on /boot/efi type vfat (rw,relatime)

Decision: If the ESP isn’t mounted, kernel/initramfs updates may not update the real boot media.

Service supervision view

cr0x@server:~$ sudo sv status /var/service/*
run: dhcpcd: (pid 610) 12m33s
run: nanoklogd: (pid 505) 12m34s
run: socklog-unix: (pid 520) 12m34s
run: sshd: (pid 842) 12m32s

Decision: If a critical service is down, that’s your immediate scope. Don’t debug the app until the supervisor state is stable.

CPU vs I/O bottleneck

cr0x@server:~$ uptime
 09:34:10 up 25 min,  1 user,  load average: 3.12, 2.40, 1.80
cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 3  0      0 5852400 112340 812400    0    0    12    35  412  780 12  4 82  2  0
 2  1      0 5849800 112340 812900    0    0  2400   120  520  900  8  3 61 28  0

What it means: If wa (I/O wait) is high, you’re storage-bound. If us/sy dominates, you’re CPU-bound.
Decision: Don’t “tune sysctl” before you know if you’re waiting on disk.

Third: isolate the culprit with targeted checks

Once you’ve placed the issue in a domain, you do a domain-appropriate deepening step:
disk health, filesystem errors, driver logs, route tables, service run scripts.
Avoid broad changes. Make one change, observe, then proceed.

Common mistakes: symptoms → root cause → fix

1) Symptom: system boots into firmware or “no bootable device”

Root cause: ESP not created correctly, not flagged, or boot entry missing; sometimes installed in legacy mode.

Fix: Boot the live ISO in UEFI mode, mount ESP at /boot/efi, reinstall bootloader, and confirm with efibootmgr -v.

2) Symptom: after updates, system drops to initramfs shell (encrypted root not found)

Root cause: initramfs lacks crypto modules or kernel cmdline doesn’t specify the LUKS device.

Fix: Ensure dracut includes crypt support, verify correct UUID, rebuild initramfs, and validate bootloader cmdline.

3) Symptom: networking “works” but xbps can’t download or TLS fails

Root cause: incorrect system time or broken DNS resolution.

Fix: fix NTP/chrony; verify getent hosts works; check /etc/resolv.conf ownership and DHCP client behavior.

4) Symptom: service keeps restarting every few seconds

Root cause: run script exits immediately (bad config, missing binary, wrong permissions), supervisor does its job and restarts.

Fix: inspect /etc/sv/<service>/run, check executable paths, and read logs under /var/log/socklog.

5) Symptom: disk fills up unexpectedly on a “minimal” system

Root cause: logs grow without rotation, or snapshots/subvolumes (btrfs/ZFS) aren’t pruned.

Fix: implement log rotation (or svlogd retention), schedule pruning, and monitor df -h plus per-directory growth.

6) Symptom: laptop suspend/resume is flaky

Root cause: firmware quirks, missing microcode/firmware packages, or power management settings.

Fix: install appropriate firmware/microcode, verify kernel parameters, and avoid stacking multiple “power” tools that fight each other.

7) Symptom: you can’t resolve hostnames after installing NetworkManager

Root cause: two components managing resolv.conf (e.g., dhcpcd + NetworkManager) and stomping each other.

Fix: pick one network stack; disable the other; confirm resolv.conf is stable across reboots.

8) Symptom: package updates complain about signatures or repo metadata

Root cause: wrong system time, corrupted cache, or mixed repos.

Fix: correct time, clear relevant cache if needed, verify repo list with xbps-query -L, and remove unexpected repo entries.

Three corporate mini-stories from the trenches

Mini-story 1: the incident caused by a wrong assumption

A mid-size company had a small fleet of build agents. They weren’t “production,” which is a phrase that tends to lower standards in a hurry.
The agents were reimaged often, and someone proposed Void because it boots fast and stays out of the way.
Good call. The rollout was quick, and the first week was quiet.

Then, one Monday, builds started failing intermittently. Not all. Not consistently.
The failures were network-flavored: timeouts, package downloads stalling, random TLS errors.
The initial diagnosis was the usual corporate reflex: “the firewall team changed something.”
Tickets were filed. Meetings were scheduled. The on-call engineer aged a year.

The real issue was a wrong assumption: that the system time would be “close enough” without a time sync service.
Some agents were booting with clock drift large enough to cause TLS validation failures against certain endpoints.
It wasn’t deterministic because firmware clocks were drifting at different rates and images were built at different times.

The fix was embarrassingly simple: install and enable a time sync daemon and add a pre-flight check in the agent startup script
that refused to start the build worker if the time was wildly off. Everything stabilized immediately.
Nobody had to change the firewall. The meetings still happened, because meetings are a separate subsystem.

Mini-story 2: the optimization that backfired

Another org ran a set of internal developer workstations that doubled as test benches. Someone noticed that boot was “slow”
on a particular hardware model and decided to optimize it. The plan was to aggressively prune services and remove “unnecessary” packages
from the base install to reduce startup work.

The optimization worked—on paper. Boot got faster by a few seconds. Then the subtle failures began.
Developers reported that after a kernel update, systems would sometimes fail to boot, and recovery required a USB stick.
The failures clustered around machines that had been “optimized” more aggressively.

The backfire was rooted in removing what looked like optional tooling around initramfs generation and firmware loading.
The machines still booted under the current kernel, so the change looked safe.
But when the next kernel landed, the system didn’t rebuild initramfs correctly, and the updated kernel couldn’t find the encrypted root on some units.
That’s not a performance problem; that’s a lifecycle problem.

The remediation wasn’t “stop optimizing.” It was “optimize with constraints.”
They restored the kernel and initramfs tooling as a protected baseline, documented which packages were non-negotiable, and measured boot time again.
Boot was slightly slower than the “optimized” version and dramatically faster than “booting from USB to fix your workstation.”

Mini-story 3: the boring but correct practice that saved the day

A team ran a few small services on Void VMs in a lab environment. Nothing glamorous: internal API, metrics gateway, some scheduled jobs.
They had one habit that looked almost comically conservative: before any upgrade window, they took a snapshot at the hypervisor layer
and exported a text copy of a small “system state report” (kernel, package list, enabled services, key configs).

One upgrade introduced a networking regression specific to their virtual NIC driver version. The VMs booted, but the interface never came up.
That could have turned into an all-hands incident because the services were dependencies for other teams’ testing pipelines.
Instead, the on-call engineer followed the runbook: confirm dhcpcd state, confirm link state, compare the system state report, and roll back.

Rolling back wasn’t an admission of defeat; it was a controlled response.
They restored service in minutes, then reproduced the issue in one sacrificial VM.
With the baseline report, they could tell exactly what changed. That reduced the problem space from “anything on the internet” to “a handful of packages.”

The final fix was to pin one package for a short period and schedule the upgrade once the regression was resolved upstream.
The boring practice—snapshots plus state reports—was the difference between a blip and a day of chaos.
It wasn’t clever. It was correct.

Checklists / step-by-step plan

Install plan (UEFI laptop/workstation, sensible defaults)

  1. Boot the live ISO in UEFI mode; confirm with ls /sys/firmware/efi.
  2. Identify the target disk with lsblk; unplug extra drives if you can.
  3. Create GPT, ESP (512 MiB), and root partition with parted.
  4. Format ESP as FAT32; create LUKS2 on root if encrypting; format ext4 inside.
  5. Mount root at /mnt, ESP at /mnt/boot/efi.
  6. Run the installer; set hostname, locale, timezone; create a user.
  7. On first boot: verify fstab uses UUIDs; verify boot entries exist.
  8. Enable logging (socklog) and time sync; verify networking and DNS.
  9. Run xbps-install -S and then xbps-install -u in a controlled window.
  10. Reboot after kernel updates; confirm fallback kernel exists.

Operational baseline checklist (the “future me” package)

  • Remote access: sshd enabled and confirmed running; keys installed; password auth policy decided.
  • Logs: local logs present and bounded; decision made on centralization.
  • Time: time sync enabled; clock correct at boot.
  • Updates: update cadence defined; kernel reboot plan exists.
  • Backups: at least one system image/snapshot method; restore test performed once.
  • Storage hygiene: periodic fstrim if SSD; monitoring for disk usage growth.
  • Network ownership: one tool owns DHCP/DNS; no conflicting managers.

Hardening steps that are worth the effort (and ones to skip)

Worth it:
strong SSH configuration, minimal installed packages, regular updates, disk encryption on mobile devices,
and basic firewall rules appropriate to your role (workstation vs server).

Skip it unless you have a reason:
elaborate “security suites,” stacking three power managers, and exotic kernel parameter collections copied from strangers.
Security is about reducing attack surface and maintaining patch hygiene, not collecting knobs.

FAQ

Is Void Linux good for production servers?

Yes, if your team is comfortable with a rolling release and you have an update process.
The operational model (runit + xbps) is clean. The risk is not the distro; it’s unmanaged updates and undocumented changes.

Should I choose musl for a workstation?

Usually no. glibc will save you compatibility headaches with third-party binaries and development tooling.
Choose musl when you know exactly why you want it (and you’re prepared to debug the fallout).

How do I enable a service on Void?

Create a symlink from /etc/sv/<service> to /var/service/<service>.
Then check status with sv status <service>.

Where do logs live?

It depends on what you install and enable. A common setup uses socklog and stores logs under /var/log/socklog.
Decide early how you want logs handled; don’t leave it ambiguous.

What’s the simplest desktop setup?

If you want minimal friction, use a lightweight window manager or a mainstream desktop you already know.
Install only what you need, and don’t treat “minimal” as a competitive sport.

How do I handle kernel updates safely?

Update in a window, ensure initramfs exists for the new kernel, keep at least one fallback kernel,
and reboot on your schedule. Verify bootloader entries with efibootmgr -v.

Can I do full disk encryption with an unencrypted ESP?

Yes. That’s a standard UEFI approach: ESP unencrypted, root encrypted (LUKS2).
Your threat model should decide whether you also need Secure Boot, TPM integration, or remote unlock.

Why does my DNS keep changing?

Because multiple components are trying to manage /etc/resolv.conf.
Pick one: dhcpcd (+ resolvconf if you use it), NetworkManager, or a static config. Then disable the rest.

Is Void harder than Arch?

Different hard. Arch has extensive community patterns; Void has fewer layers and less ceremony.
Void can feel easier once you internalize runit and xbps, because there’s less “magic” to unlearn.

What’s the best filesystem for Void?

ext4 if you want predictable. btrfs if you want snapshots and will manage them.
ZFS if you are intentionally running a storage system and accept the operational model.
If you can’t articulate the tradeoff, pick ext4 and move on with your life.

Conclusion: next steps that keep you out of trouble

Void Linux is minimal in the way that matters: fewer abstractions between you and the system’s behavior.
That makes it a great platform for people who like understanding their machines, not negotiating with them.
But minimalism doesn’t excuse operational laziness. It just makes laziness more visible.

Next steps I’d actually do, in order:

  1. Enable logging and time sync; verify they survive reboot.
  2. Lock down SSH (or remove it if you don’t need it) and confirm you can recover access.
  3. Define your update cadence and test a kernel update + reboot while you’re not under pressure.
  4. Pick a storage posture: ext4 baseline, or snapshots with btrfs/ZFS—with monitoring and pruning.
  5. Write a one-page local runbook: disk layout, encryption UUIDs, enabled services, and how to boot a fallback kernel.

The goal isn’t to build the most minimal system on the internet. The goal is to build a system you can operate calmly when it misbehaves.
Void gives you the tools. Use them like you’re going to be the one paged at 3 a.m.—because you might be.

← Previous
Batch Rename Files Safely: The Script That Won’t Trash Names
Next →
Kali Linux 2025.4 Install: The Safe Way (So You Don’t Brick Your Laptop)

Leave a comment