Back to Index

PREFACE

Zeppe-Lin Handbook.
Because figuring it out yourself is overrated.

This guide provides everything you need to install, configure, and maintain your Zeppe-Lin system.

Build the system you need. This handbook shows you how.
Because nobody else will.


1 INTRODUCTION

1.1 About Zeppe-Lin

Zeppe‑Lin is a lightweight, source‑based GNU/Linux distribution for x86‑64 systems. A fork of CRUX, it emphasizes simplicity, predictability, and maintainable design. It follows UNIX principles: small tools, plain‑text configuration, and explicit behavior. Automation is minimal and visible.

Zeppe‑Lin is designed for users who want full control over their system. Everything can be built, inspected, and maintained from source, ensuring autonomy, transparency, and reproducibility. The system favors practical, reliable behavior over cleverness or unnecessary features.

1.2 Audience

Zeppe‑Lin suits users who:

1.3 Summary

By keeping components small, behavior explicit, and operations visible, Zeppe‑Lin ensures the system is comprehensible, controllable, and practical, while remaining flexible enough to adapt to individual needs.


2 INSTALLATION

2.1 Supported Hardware

Zeppe-Lin Linux supports x86-64 processors (e.g., AMD Athlon 64, Intel Core, Intel Atom). Older architectures, such as i686, are incompatible.

2.2 Boot a Live System

Zeppe-Lin is distributed as a compiled tarball containing a root filesystem. To install it, first boot your computer using a Linux-based “Live CD/DVD/USB” system.

Launch your preferred Live system, open a terminal, and gain root privileges (e.g., sudo su).

2.3 Disk Partitions and Filesystems

This section describes “UEFI and LVM on LUKS” installation. Additional partitions/filesystems schemes may be added in the future.

2.3.1 UEFI and LVM on LUKS

Set up Zeppe-Lin on a fully encrypted disk (excluding the bootloader partition for UEFI compatibility) using dm-crypt with LUKS and an LVM container inside the encrypted partition.

Important:

Ensure the following tools are available in the Live system:

2.3.1.1 Partition Scheme

Example device: /dev/sda. Replace with your actual device (e.g., /dev/sdb, /dev/nvme0n1).

Partition Filesystem Size Description
/dev/sda1 FAT32 512 MiB boot partition
/dev/sda2 none remaining space LUKS container

Important:

On UEFI systems, the boot partition must be an EFI System Partition (ESP).

A size of 512 MiB is recommended.

2.3.1.2 Create Partitions

Use parted(8) to partition the disk:

# as root
parted -s /dev/sda <<'EOF'
mklabel gpt
mkpart ESP fat32 1MiB 513MiB
set 1 boot on
name 1 efiboot
mkpart primary 513MiB 100%
name 2 luks
quit
EOF

Encrypt /dev/sda2 with LUKS and map it:

# as root
cryptsetup luksFormat /dev/sda2
cryptsetup luksOpen /dev/sda2 crypt

2.3.1.3 Create LVM Inside LUKS

Create a physical volume and volume group (e.g., zpln):

# as root
pvcreate /dev/mapper/crypt
vgcreate zpln /dev/mapper/crypt

Logical volume layout:

Volume name Filesystem Size Description
swap none 2 x RAM swap area
root ext4 remaining space root filesystem

Check RAM size with # free -m. For example, if you have 4 GiB RAM, a swap of 8 GiB (2 x RAM) is recommended.

Create the logical volumes:

# as root
lvcreate -L 8G -n swap zpln
lvcreate -l 100%FREE -n root zpln

2.3.1.4 Create Filesystems and Swap

Format partitions and volumes:

# as root
mkfs.vfat -F32 /dev/sda1
mkfs.ext4 /dev/zpln/root
mkswap /dev/zpln/swap
swapon /dev/zpln/swap

2.3.1.5 Mount Partitions

The /mnt directory is used as the default mount point in this handbook.

Mount the root volume:

# as root
mount /dev/zpln/root /mnt

Mount the boot partition:

# as root
mkdir /mnt/boot
mount /dev/sda1 /mnt/boot

2.4 Install Base System

Download, verify, and extract the rootfs tarball to set up your base system.

2.4.1 Download Rootfs Tarball

Download the tarball directly into the /mnt directory (the default mount point) to avoid using live media RAM.

The current Zeppe-Lin release is published at
https://github.com/zeppe-lin/pkgsrc-core/releases/latest

Identify the release version (e.g., vX.Y), then download the corresponding rootfs archive and its detached signature:

# as root
cd /mnt

URL=https://github.com/zeppe-lin/pkgsrc-core/releases/download
VERSION=vX.Y

wget -c ${URL}/${VERSION}/rootfs-${VERSION}-x86_64.tar.xz \
        ${URL}/${VERSION}/rootfs-${VERSION}-x86_64.tar.xz.sig

2.4.2 Verify Downloaded Tarball

The tarball is signed with GPG. For security, verifying its integrity is highly recommended.

Import the public key:

# as root
gpg --keyserver keyserver.ubuntu.com --recv-keys 59ec1986fbd902cf

Verify the signature:

# as root
gpg --verify rootfs-${VERSION}-x86_64.tar.xz.sig \
        rootfs-${VERSION}-x86_64.tar.xz

2.4.3 Extract Rootfs Tarball

Once the rootfs tarball is downloaded and verified, extract its contents using the following command. Make sure to replace ${VERSION} with the version you downloaded:

# as root
tar --numeric-owner --xattrs --xattrs-include='*' -xpf \
        rootfs-${VERSION}-x86_64.tar.xz

Important:

It is critical to use all the specified options to ensure proper extraction. Here’s what each option does:

2.5 Chroot Into Base System

Important:

Copy the DNS configuration to ensure network access inside the chroot, which is needed for updates and installing software:

# as root
cp /etc/resolv.conf /mnt/etc/resolv.conf

Mount necessary pseudo-filesystems:

# as root
mount -B /dev /mnt/dev
mount -B /tmp /mnt/tmp
mount -B /run /mnt/run
mount -t proc proc /mnt/proc
mount -t sysfs none /mnt/sys
mount -t devpts -o noexec,nosuid,gid=tty,mode=0620 devpts \
        /mnt/dev/pts

For UEFI systems, mount EFI variables:

# as root
mount -B /sys/firmware/efi/efivars /mnt/sys/firmware/efi/efivars

Enter the chroot:

# as root
chroot /mnt /bin/bash

Set the SHELL variable to /bin/bash to ensure tools like vim work properly:

# chrooted, as root
export SHELL=/bin/bash

2.6 Configure Base System

Set the root password:

# chrooted, as root
passwd root

Ensure proper ownership and permissions:

# chrooted, as root
chown root:root /
chmod 755 /

Generate necessary locales (e.g., en_US):

# chrooted, as root
localedef -i en_US -f UTF-8 en_US.UTF-8

Set the system default locale by adding it to /etc/profile:

export LANG=en_US.UTF-8

See Generating Locales for details.

Edit /etc/fstab to configure filesystems (see fstab(5)). Example for UEFI and LVM on LUKS:

/dev/zpln/root  /      ext4  defaults,noatime,nodiratime  1 2
/dev/sda1       /boot  vfat  defaults,noatime,nodiratime  1 2
/dev/zpln/swap  swap   swap  defaults                     0 0

Note:

Use UUID=... or /dev/disk/by-uuid/* instead of /dev/* for better reliability on multi-disk systems. Find UUIDs with:

# as root
blkid -o value -s UUID <DEVICE>

Edit /etc/rc.conf (see rc.conf(5)) to configure system settings like console font, keyboard, timezone, hostname, and services.

Configure network settings in /etc/rc.d/net, /etc/hosts, and /etc/resolv.conf (see Networking).

2.6.1 Mount Additional Filesystems

Before building and installing additional software, mount additional pseudo-filesystems.

Shared memory (needed by some builds, e.g., python3):

# chrooted, as root
mount /dev/shm

Optional package build directory (RAM builds):

# chrooted, as root
mount /var/cache/pkgmk/work

Important:

Mount /var/cache/pkgmk/work only if enabled in /etc/fstab and you have enough RAM.

2.6.2 Configure Users and Privileges

Add a regular user (set a specific UID now if needed):

# chrooted, as root
useradd --shell /bin/bash \
        --create-home  \
        --groups audio,video,scanner,cdrom,input,users \
        --uid 1000 \
        --user-group <USERNAME>

Set the password:

# chrooted, as root
passwd <USERNAME>

To grant administrative privileges (e.g., as Ubuntu does), first add the user to the wheel group:

# chrooted, as root
usermod -aG wheel <USERNAME>

And second, grant the users in the wheel group to be root via sudo(8) by creating the file /etc/sudoers.d/00_wheel with the following content:

%wheel ALL=(ALL:ALL) ALL

2.7 Update the Base System

Before building and installing additional packages, it is highly recommended to update the freshly installed Zeppe-Lin system.

2.7.1 Prepare Pkgsrc Collections

Packages’ sources are organized into collections.

Clone the necessary collections, keeping in mind that each depends on the previous one:

# chrooted, as root
cd /usr/src/
URL=https://github.com/zeppe-lin
git clone ${URL}/pkgsrc-core    --branch 1.x
git clone ${URL}/pkgsrc-system  --branch 1.x
git clone ${URL}/pkgsrc-xorg    --branch 1.x
git clone ${URL}/pkgsrc-desktop --branch 1.x

Important:

The 1.x branch applies to all 1. releases (1.0, 1.1, 1.2, …). When a new major line appears, switch to its branch (e.g., 2.x). Verify your release against the current release or the pkgsrc-core releases page.

Enable the cloned collections in /etc/pkgman.conf. By default, only pkgsrc-core collection is enabled.

2.7.2 Perform the System Update

Perform a full system update with dependency handling and sorting, and stop if the installation of at least one package fails:

# chrooted, as root
pkgman sysup --deps --depsort --group

Merge any rejected files during update:

# chrooted, as root
rejmerge

Check for broken packages and rebuild as needed:

# chrooted, as root
pkgman update -fr --depsort $(revdep)

Here, revdep(1) identifies packages with broken dependencies, and if any are reported, pkgman update -fr rebuilds the affected packages.

2.8 Install Essential Packages

Install packages required for a minimal, functional workstation:

# chrooted, as root
pkgman install --deps --group \
        cryptsetup e2fsprogs dosfstools grub2 grub2-efi iw gnupg \
        lvm2 pinentry wireless-tools wpa-supplicant dhcpcd iputils

This is a generic setup; users can add any additional packages they require based on their specific needs.

Note:

For network setup and configuration details, see Networking.

If you set up a networking bridge, specify the bridge interface in the /etc/rc.d/dhcpcd and/or /etc/rc.d/wpa_supplicant.

2.9 Kernel Setup

Two methods are available: build and install a package or compile manually.

2.9.1 Kernel Package

Build and install the packaged Linux kernel:

# chrooted, as root
pkgman install --deps --group --config-append="runscripts no" linux

The linux package includes a post-install script that runs mkinitramfs(8) and updates the GRUB config. This script is disabled here through --config-append="runscripts no" to run manually later.

Important:

When you update the linux package through the package manager, the old kernel and its modules are removed as part of the update process. This can cause issues if the system is still using the old kernel while the new one is being installed. To avoid these problems, it is recommended to lock the linux package from automatic updates and handle kernel updates separately using pkgman-update(8). See pkgman-lock(8) for details.

If you installed the kernel using this method, proceed to Kernel Firmware if your system requires additional firmware, or Initramfs otherwise.

2.9.2 Manual Kernel Compilation

This method suits those wanting a minimal kernel or needing sources for driver building (e.g., Nvidia, VirtualBox).

Important:

Use the kernel version from Zeppe-Lin’s package sources for best compatibility. Other versions might cause issues.

Download sources:

# chrooted, as root
pkgman install -do linux
KV=$(pkgman printf %v --filter=linux)
tar -xvf /var/cache/pkgmk/sources/linux-${KV}.tar.?z -C /usr/src/

Apply Zeppe-Lin patches:

# chrooted, as root
cd /usr/src/linux-${KV}
for p in $(pkgman path linux)/*.patch; do
        [ -f "$p" ] && patch -Np1 -i "$p"
done

vs

Important:

It is highly recommended to perform a dry run to check for errors before applying. To do this, simply add --dry-run option to the patch(1) command within the loop shown above.

Configure kernel (choose one of the following):

Next steps:

2.10 Kernel Firmware

Some hardware (like certain Wi-Fi or graphics cards) needs extra firmware.

Install non-free firmware if required:

# chrooted, as root
pkgman install --deps --group linux-firmware

Skip this if you prefer a completely free software system.

2.11 Initramfs

First, install mkinitramfs:

# chrooted, as root
pkgman install --deps --group mkinitramfs

The /etc/mkinitramfs/config file controls how mkinitramfs(8) builds the initial ramdisk image used at boot. It controls which modules, hooks, and settings are included in the initramfs image.

Here’s an example for UEFI and LVM on LUKS:

# /etc/mkinitramfs/config
hostonly=1 # optional, creates smaller initramfs
compress="gzip --fast"
hooks="eudev luks lvm resume"
root=/dev/zpln/root
root_type=ext4
resume=/dev/zpln/swap
luks_name=crypt
luks_root=/dev/sda2
# End of file.

See mkinitramfs.config(5) for more information.

Important:

The hostonly=1 setting creates a smaller initramfs with only necessary modules. If you move the drive to different hardware later, it might not boot.

Note:

For multi-disk systems, using UUID=... or /dev/disk/by-uuid/* instead of /dev/* in the config can prevent boot issues. Find UUIDs with:

# as root
blkid -o value -s UUID <DEVICE>

Now, prepare an initramfs. If you installed the Linux kernel manually, you have already set KV variable to kernel version. Otherwise, fetch the kernel version from the package source:

# chrooted, as root
KV=$(pkgman printf %v --filter=linux)

Finally, generate the initramfs image:

# chrooted, as root
mkinitramfs -o /boot/initramfs-${KV}.img -k ${KV}

2.12 Bootloader

This section covers installing GRUB2 as your bootloader. Support for other bootloaders may be added later.

2.12.1 GRUB2

Create /etc/default/grub with:

# Set the delay before booting:
GRUB_TIMEOUT=3

# Show ZPLN in the GRUB menu:
GRUB_DISTRIBUTOR=ZPLN

# Set kernel parameters (quiet boot, swap for hibernation):
GRUB_CMDLINE_LINUX_DEFAULT="quiet resume=/dev/zpln/swap"

Important:

For better reliability on multi-disk systems, use UUID=... instead of /dev/* for the resume partition.

Find the UUID with:

# as root
blkid -o value -s UUID <DEVICE>

Next, install GRUB2 on your target drive. Example device: /dev/sda. Replace with your actual device (e.g., /dev/sdb, /dev/nvme0n1).

For UEFI, ensure the EFI System Partition (ESP) is mounted at /boot/efi inside the chroot. Then install GRUB with EFI support:

# chrooted, as root
grub-install --target=x86_64-efi --efi-directory=/boot /dev/sda

For legacy BIOS booting, install GRUB directly to the disk’s MBR:

# chrooted, as root
grub-install --target=i386-pc /dev/sda

Finally, generate the GRUB2 configuration file based on /etc/default/grub and detected kernels:

# chrooted, as root
grub-mkconfig -o /boot/grub/grub.cfg

2.13 Post-installation Tasks

2.13.1 Install Xorg and Drivers

To find video and input drivers, run:

# chrooted, as root
pkgman search -vv xf86-

Common video drivers include xorg-xf86-video-intel (Intel), xorg-xf86-video-amdgpu/xorg-xf86-video-ati (AMD), and xorg-xf86-video-nouveau (NVIDIA). For NVIDIA, the nouveau-firmware is also recommended.

For input, modern drivers like xorg-xf86-input-libinput and xorg-xf86-input-evdev are recommended. Legacy options like xorg-xf86-input-keyboard and xorg-xf86-input-mouse are also available.

After selecting drivers for your hardware, install xorg meta-package and additional drivers with:

# chrooted, as root
pkgman install --deps --group xorg [DRIVERS]

Replace [DRIVERS] with the ones matching your system.

2.13.2 Install a Window Manager

Zeppe-Lin focuses on lightweight and efficient window managers rather than complex desktop environments like GNOME or KDE. This approach avoids unnecessary dependencies and simplifies configuration.

Zeppe-Lin provides package sources for several window managers, but users are welcome to choose and contribute package sources for any window manager they prefer.

2.13.2.1 Ratpoison

Currently, the pkgsrc-desktop repository provides the ratpoison window manager. You can install it with:

# chrooted, as root
pkgman install --deps --group ratpoison

2.13.2.2 Unofficial: Window Maker

For users interested in a retro, intuitive window manager, you can install Window Maker from the unofficial pkgsrc-wmaker repository.

Warning:

Using unofficial repositories means the packages might not follow the same update schedule or quality standards as the official repositories. Use with caution.

To install Window Maker, first clone the repository:

# chrooted, as root
cd /usr/src
URL=https://github.com/zeppe-lin
git clone ${URL}/pkgsrc-wmaker --branch 1.x

Important:

The 1.x branch applies to all 1. releases (1.0, 1.1, 1.2, …). When a new major line appears, switch to its branch (e.g., 2.x). Verify your release against the current release or the pkgsrc-core releases page.

Next, you need to tell pkgman(1) about this new repository by adding the following line to /etc/pkgman.conf:

pkgsrcdir /usr/src/pkgsrc-wmaker

Then, install it with:

# chrooted, as root
pkgman install --deps --group wmaker

Optional meta-packages for Window Maker include wmaker-dockapps for added functionality and wmaker-themes for customization.

2.14 Reboot

Exit the chroot:

# chrooted, as root
exit

Unmount all filesystems, and reboot:

# as root
cd /
umount -R /mnt
shutdown -r now

The GRUB menu should appear, allowing you to boot into Zeppe-Lin.


3 UPGRADING

WARNING! THIS SECTION IS UNDER DEVELOPMENT AND CANNOT BE USED AS A MANUAL!

Upgrading Zeppe-Lin requires careful planning. This section outlines methods for updating your system. Follow the steps as presented to ensure a reliable upgrade.

Upgrade methods:

  1. Quick binary upgrade (core components).
  2. Full update from sources.

Before starting, read the release notes for the target version and back up important data.

3.1 Release Notes

Read the release notes for the new Zeppe-Lin version. They contain essential details about new features, removed packages, and compatibility issues. The latest release notes you can obtain here.

3.2 Set Release And Sync

Synchronize your local pkgsrc collections to match the new release branch. Replace 1.x with the specific release you are upgrading to:

# as root
git -C /usr/src/pkgsrc-core    switch 1.x
git -C /usr/src/pkgsrc-system  switch 1.x
git -C /usr/src/pkgsrc-xorg    switch 1.x
git -C /usr/src/pkgsrc-desktop switch 1.x

3.2.1 Binary Upgrade

Use precompiled binary packages for faster core system upgrades, especially to avoid build issues during initial updates.

Important:

Upgrades may cause file conflicts, such as a file being reassigned between packages. To prevent issues, read the release notes for the target Zeppe-Lin version. They detail package changes like removals, merges, or renames that may require manual intervention.

3.2.1.1 Download and Extract binpkgs

Note:

Verify the integrity of the downloaded binpkgs tarball before extracting it. It is signed using the same method as the Zeppe-Lin root filesystem image.

Download the tarball and its signature for your upgrade, replacing v1.0 with the target version:

URL=https://github.com/zeppe-lin/pkgsrc-core/releases/download
VERSION=v1.0
wget -c ${URL}/${VERSION}/binpkgs-${VERSION}-x86_64.tar.xz{,.sig}

Verify the tarball with gpg(1):

gpg --keyserver keyserver.ubuntu.com --recv-keys 59ec1986fbd902cf
gpg --verify binpkgs-${VERSION}-x86_64.tar.xz{.sig,}

Extract the tarball after successful verification:

mkdir -p ~/binpkgs
tar -xf binpkgs-${VERSION}-x86_64.tar.xz -C ~/binpkgs

3.2.1.2 Upgrade Core Packages

Navigate to the directory and upgrade core packages:

cd ~/binpkgs

# as root, upgrade all upgradeable packages
for p in *.pkg.tar.gz; do
    pkgadd -u "$p"
done

pkgadd -u upgrades each package. New, non-upgradable packages will return an error, which the loop skips, ensuring all upgradeable packages are updated.

3.2.1.3 Install New Core Packages

Install new core packages introduced in the release:

cd ~/binpkgs

# as root, install all new packages
for p in *.pkg.tar.gz; do
    pkgadd "$p"
done

pkgadd installs each package. Already installed packages return an error, which the loop skips to ensure all new packages are installed.


4 PACKAGE MANAGEMENT

4.1 Introduction

Zeppe-Lin’s package management system revolves around packages and package sources. Low-level tools handle atomic operations like building and installation, working with individual package sources or packages as instructed. The high-level tool pkgman manages the package source collections and orchestrates these low-level tasks for system-wide software management.

To work with packages and package sources, Zeppe-Lin provides:

4.2 What is a Package?

A Zeppe-Lin package is a compressed archive (e.g., tar.gz) that contains only the software files. It avoids any kind of embedded metadata, ensuring simplicity and transparency in its structure.

4.2.1 Package Naming

Zeppe-Lin packages follow a clear naming convention:

name#version-release.pkg.tar.gz

Components:

Example:

ed#1.21-1.pkg.tar.gz

The extension .pkg.tar.gz denotes a Zeppe-Lin package. Other common compression formats like tar.bz2, tar.xz, and tar.zst are also supported.

4.2.2 Package Database

The package database, located at /var/lib/pkg/db, tracks installed packages and their contents. Each package entry follows this format:

<name>                 # Package name
<version>-<release>    # Version and release
<dir>/                 # Top-level directory
<dir>/<subdir>         # Subdirectory
<dir>/<subdir>/<file>  # File within subdirectory
(Blank line)           # Separates package entries

Example entry for ed:

ed
1.21-1
bin/
bin/ed
usr/
usr/share/
usr/share/man/
usr/share/man/man1/
usr/share/man/man1/ed.1.gz
(Blank line)

Note:

This database enables essential operations like installation (pkgadd(8)), removal (pkgrm(8)), and inspection (pkginfo(8)).

4.3 Low-level Tools: pkgutils

pkgutils provides the core utilities to handle packages:

4.3.1 Using pkgutils

4.3.1.1 Installing a Package

Install a package file with pkgadd(8):

# as root
pkgadd bash#5.0.18-1.pkg.tar.gz

By default, pkgadd(8) prevents overwriting existing files. Use -f or --force to override this behavior (use with caution!):

# as root
pkgadd -f bash#5.0.18-1.pkg.tar.gz

4.3.1.2 Upgrading a Package

Upgrade a package with the -u or --upgrade option:

# as root
pkgadd -u bash#5.0.18-1.pkg.tar.gz

This replaces the installed package. If the package is not installed, it will return an error. You can even “upgrade” to an older version.

Files that should not be upgraded (based on /etc/pkgadd.conf) are placed in /var/lib/pkg/rejected/.

4.3.1.3 Removing a Package

To remove a package, use pkgrm(8) followed by the package name:

# as root
pkgrm bash

Caution:

This command removes all files owned by the package, without prompting. Misspelling the package name may remove something entirely different (e.g., glib vs. glibc).

4.3.1.4 Inspecting Packages

Use pkginfo(1) to get information about packages.

List installed packages and versions:

pkginfo -i

List files in a package:

pkginfo -l bash

List files in a package archive (before installing):

pkginfo -l grep#2.5-1.pkg.tar.gz

Find which package owns a file:

pkginfo -o /usr/bin/ls

See pkgadd(8), pkgrm(8), and pkginfo(1) for details.

4.3.2 Configuring pkgadd

You can customize pkgadd(8) behavior during package upgrades using /etc/pkgadd.conf. Rules define whether files should be upgraded or preserved.

4.3.2.1 Rule Format

Each rule in /etc/pkgadd.conf follows this format:

EVENT   PATTERN   ACTION

Rules are processed sequentially, with the last matching rule taking priority.

4.3.2.2 Example

Example /etc/pkgadd.conf entries:

UPGRADE   ^etc/.*$              NO
UPGRADE   ^var/log/.*$          NO
UPGRADE   ^etc/X11/xorg.conf$   YES

These rules will:

Caution:

Patterns match filenames inside the package, not full system paths. Do not use / at the start!

See pkgadd.conf(5) for details.

4.4 Low-level Tools: rejmerge

rejmerge(8) resolves files rejected during upgrades using pkgadd -u. Rejected files are moved to /var/lib/pkg/rejected/ for manual handling, while rejmerge(8) provides a way to manage these files semi-automatically.

4.4.1 Using rejmerge

If files are present in /var/lib/pkg/rejected, rejmerge(8) prompts you to decide their fate. For each file, you can choose:

=======> file
(diff output between installed file and rejected file)
[K]eep [U]pgrade [M]erge [D]iff [S]kip

Legend:

See rejmerge(8) for details.

4.4.2 Configuring rejmerge

Configurations for rejmerge(8) can be adjusted in /etc/rejmerge.conf. Key settings:

4.4.2.1 Example

Use unified, colorized output for differences:

rejmerge_diff() { diff -u --color=always $1 $2 > $3 ; }

Use sdiff(1) for merging files:

rejmerge_merge() { sdiff -o $3 $1 $2 ; }

See rejmerge.conf(5) for more details.

4.5 Low-level Tools: pkgmk

pkgmk(8) builds Zeppe-Lin packages (compressed archives) from source files using a Pkgfile. These files, along with the supporting directory, form a Package Source – a self-contained unit that includes all files needed to build and install software.

A Package Source typically contains:

A Package Source is the basic unit of software organization in Zeppe-Lin. While this section introduces Package Sources in the context of building packages, PACKAGE SOURCES AND COLLECTIONS explores their organization into Collections and Repositories for broader software management workflows.

4.5.1 Pkgfile Format

The Pkgfile defines:

4.5.1.1 Example

name=hello
version=2.12.1
release=1
source=https://ftp.gnu.org/gnu/hello/$name-$version.tar.gz

build() {
    cd $name-$version
    ./configure --prefix=/usr --disable-nls
    make
    make DESTDIR=$PKG install
    rm -rf $PKG/usr/info
}

See pkgmk.Pkgfile(5) for details.

4.5.2 Using pkgmk

4.5.2.1 Building a Package

To build a package, create a directory named after the package, cd into this directory, place the Pkgfile inside, and run:

fakeroot pkgmk -d -cf /dev/null

On the first build, pkgmk(8) generates:

The final package (e.g., hello#2.12.1-1.pkg.tar.gz) will be in the current directory.

4.5.3 Configuring pkgmk

Customize pkgmk(8) with /etc/pkgmk.conf. Key settings:

4.5.3.1 Example

Define backup URLs:

PKGMK_SOURCE_MIRRORS="http://fileserver.intranet/sources/
                      http://mirror2/sources/"

Set a unique work directory:

PKGMK_WORK_DIR="/var/cache/pkgmk/work/$name-$$"

Using $name (package name) and $$ (process ID) avoids conflicts during simultaneous builds.

Note:

Although unique directories avoid build conflicts, pkgadd(8) locks the database, so simultaneous installations may fail.

See pkgmk.conf(5) for full options.

4.6 High-level Tool: pkgman

pkgman(1) serves as a versatile interface for managing Zeppe-Lin’s package ecosystem. Building on tools like pkgutils and pkgmk, it unifies and simplifies workflows while supporting a wide range of package management tasks.

pkgman(1) empowers users to:

With its adaptable design, pkgman(1) integrates seamlessly into diverse system management scenarios, making it an indispensable tool for maintaining and customizing Zeppe-Lin.

4.6.1 Using pkgman

4.6.1.1 List Installed Packages

List installed packages:

pkgman list

Include versions:

pkgman list -v

Include versions and descriptions:

pkgman list -vv

4.6.1.2 Query Package Information

Display detailed package information:

pkgman info acl

Example output:

Name:         acl
Path:         /usr/src/pkgsrc-core
Version:      2.3.1
Release:      1
Description:  Access Control List filesystem support
URL:          http://savannah.nongnu.org/projects/acl
Dependencies: attr

4.6.1.3 Search Package Sources

Search by name:

pkgman search -vv glibc

Search using regular expressions:

pkgman search -vv --regex '^(glib)c?$'

Search by description:

pkgman dsearch -vv archive

4.6.1.4 4.6.1.4. Query Dependencies

Show direct dependencies:

pkgman dep vim

Show recursive dependencies:

pkgman dep vim --recursive

Show detailed dependency tree:

pkgman dep vim --recursive --tree

4.6.1.5 4.6.1.5. Build and Install Packages

Build and install a package with dependencies (recommended):

# as root
pkgman install --deps --group xterm

Use --group to stop installation if a dependency fails. Retry failed installations:

# as root
pkgman install --deps --group --force xterm

--force skips already installed packages during the retry.

4.6.1.6 4.6.1.6. Update Installed Packages

Identify outdated packages:

pkgman diff --deps --full

Update a single package:

# as root
pkgman update --deps --group bind

Upgrade all packages in the system:

# as root
pkgman sysup --deps --depsort --group

4.6.2 Configuring pkgman

pkgman(1) is configured via /etc/pkgman.conf. Key settings:

See pkgman.conf(5) for more details.

4.7 The Essentials (outro)

This chapter introduced Zeppe-Lin’s tools for managing individual packages and basic system updates.

In the next chapter, Collections of Package Sources are introduced — a modular approach to organizing and managing software — with pkgman serving as the central orchestration tool.


5 PACKAGE SOURCES AND COLLECTIONS

5.1 Introduction

Zeppe-Lin organizes software sources into three key components:

These components provide a structured approach to building, managing, and distributing software on Zeppe-Lin. Tools like pkgmk(8) and pkgman(1) are used to interact with these components, supporting workflows such as dependency management, package building, and system updates.

5.2 Organizing Package Sources

5.2.1 Package Source: The Core Unit

In Zeppe-Lin, a Package Source is the fundamental unit for building software. Each one represents a single application, library, or utility, organized as a directory with all the necessary files to build and install the software.

Essential files include:

Some Package Sources may include additional files to support custom builds or configurations:

Together, these files create a self-contained environment for building and managing software.

5.2.2 Collections

Collections are logical groupings of Package Sources. A Collection is simply a directory containing multiple Package Sources with a shared purpose, like system utilities or desktop applications.

Example Collection structure:

Collection (e.g., pkgsrc-core)
|
|-- acl/ (Package Source)
|   |-- Pkgfile
|   |-- .md5sum
|   `-- .footprint
|
|-- bash/ (Package Source)
|   |-- Pkgfile
|   |-- .md5sum
|   `-- .footprint
|
`-- ... (more Package Sources)

This logical organization enables modular software management. Instead of dealing with individual packages in isolation, Collections provide a structured approach to grouping and managing related sets of software.

Common Collections in Zeppe-Lin:

These Collections form the foundation of Zeppe-Lin’s software ecosystem, allowing users to selectively enable the software they need.

5.2.3 Repositories: Distributing and Versioning Collections

Collections are organized into Repositories, hosted on systems like Git. It’s important to note that while a repository can contain multiple collections, Zeppe-Lin’s official repositories adopt a strategy where each repository primarily focuses on a single collection. This design choice is driven by the benefits of allowing users to selectively clone only the collections they need. Repositories provide:

The official Zeppe-Lin repositories, following this one-collection-per-repository approach, include:

Repositories ensure that collections are both modular and manageable, allowing users to selectively clone only the collections they need.

5.3 Managing Package Sources

5.3.1 Setting Up Collections

To access software for installation in Zeppe-Lin, download the official package source collections and enable them for pkgman(1).

5.3.1.1 Syncing Official Repositories

Clone the repositories from the Zeppe-Lin GitHub account:

# as root
cd /usr/src/
URL=https://github.com/zeppe-lin
git clone ${URL}/pkgsrc-core    --branch 1.x
git clone ${URL}/pkgsrc-system  --branch 1.x
git clone ${URL}/pkgsrc-xorg    --branch 1.x
git clone ${URL}/pkgsrc-desktop --branch 1.x

Important:

The 1.x branch applies to all 1. releases (1.0, 1.1, 1.2, …). When a new major line appears, switch to its branch (e.g., 2.x). Verify your release against the current release or the pkgsrc-core releases page.

5.3.1.2 Enabling Collections in pkgman.conf

Edit /etc/pkgman.conf to enable package sources collections. For example:

pkgsrcdir /usr/src/pkgsrc-core
pkgsrcdir /usr/src/pkgsrc-system
pkgsrcdir /usr/src/pkgsrc-xorg
pkgsrcdir /usr/src/pkgsrc-desktop

For detailed configuration options, see pkgman.conf(5).

5.3.1.3 Verify Your Setup

Confirm that pkgman(1) recognizes the collections:

pkgman list --all

This lists all package sources from enabled collections.

5.3.2 Exploring and Installing Packages

With collections enabled, you can explore and install packages using pkgman(1).

5.3.2.1 Listing Available Packages

List all package sources:

pkgman list --all

Example output:

-- list ([i] = installed)
[i] acl
[i] bash
[ ] vim
[ ] zathura
...

Legend:

To display the full path of each package source (helpful for identifying which collection a package belongs to), use the --path option:

pkgman list --all --path

Example output:

[i] /usr/src/pkgsrc-core/acl
[ ] /usr/src/pkgsrc-system/vim

5.3.2.2 Searching for Packages

If you’re looking for specific software, use the search command. For example, to search for packages related to alsa:

$ pkgman search alsa
-- search ([i] = installed)
[ ] alsa-lib
[ ] alsa-ucm-conf
[i] alsa-utils

The output indicates which packages are installed and available.

For a detailed search that includes versions, descriptions, and collection paths, add the -vv and --path options:

$ pkgman search -vv --path alsa
-- search ([i] = installed)
[ ] /usr/src/pkgsrc-system/alsa-lib 1.2.15.1-1: ALSA libraries
[ ] /usr/src/pkgsrc-system/alsa-ucm-conf 1.2.15.1-1: ALSA Use Case Manager configuration
[i] /usr/src/pkgsrc-system/alsa-utils 1.2.15.1-1: Utilities for the ALSA

5.3.2.3 Building and Installing Packages

Build and install a package with dependencies:

# as root
pkgman install --deps --group <package_name>

Example:

# as root
pkgman install --deps --group vim

Build and install multiple packages:

# as root
pkgman install --deps --group vim zathura

For verbose output (show building process on stdout):

# as root
pkgman install -vv --deps --group vim zathura

Dry run:

# as root
pkgman install -vv --test --deps --group vim zathura

Tip:

Use pkgman search to locate package names.

Run pkgman install --help or see pkgman-install(8) for information about all installation options.

5.3.3 Keeping Your System Up-to-Date

Maintaining your Zeppe-Lin system involves two primary tasks:

  1. Sync your Repositories regularly to stay up-to-date.
  2. Updating installed packages to their latest versions.

5.3.3.1 Synchronizing Local Collections

Regularly sync local collections with remote repositories using git pull:

# as root
git -C /usr/src/pkgsrc-core     pull
git -C /usr/src/pkgsrc-system   pull
git -C /usr/src/pkgsrc-xorg     pull
git -C /usr/src/pkgsrc-desktop  pull

Include unofficial collections if applicable.

Automate Syncing: Set up a cron job with crond(8):

# Weekly sync at 3:00 AM on Sundays
0 3 * * 0 run-one git -C /usr/src/pkgsrc-core    pull -q
0 3 * * 0 run-one git -C /usr/src/pkgsrc-system  pull -q
0 3 * * 0 run-one git -C /usr/src/pkgsrc-xorg    pull -q
0 3 * * 0 run-one git -C /usr/src/pkgsrc-desktop pull -q

Important:

Ensure run-one package from pkgsrc-core collection is installed.

5.3.3.2 Checking for Package Updates

Once the local collections are synced, identify outdated packages with pkgman diff:

pkgman diff --deps --full

Example output:

-- Differences between installed packages and packages sources tree
Package         Installed      Available
feh             3.7.2-1        3.7.2-2
screen          4.9.0-1        5.0.0-1
...

-- Packages not found in the packages sources tree
Package         Installed      Required by
libncurses      6.2-1          vim

Review this output to determine which packages need updates. Packages in the “not found” section may have been removed from the collection or installed manually from other sources.

5.3.3.3 Updating Packages

To upgrade the system, start by downloading all required source files. This ensures the upgrade won’t be interrupted by missing files.

# as root
pkgman sysup -do

Once the sources are downloaded, upgrade the packages with proper dependency handling and ordering:

# as root
pkgman sysup --deps --depsort --group

Options explained:

5.3.3.4 Updating Specific Packages

Update individual packages with pkgman update. For example:

# as root
pkgman update --deps --depsort --group screen feh

This command updates only the specified packages while resolving their dependencies.

5.3.4 Advanced Package Management

5.3.4.1 Detecting Broken Dependencies with revdep(1)

revdep(1) checks installed packages for missing or incorrect shared libraries. Use it to verify system stability and identify packages needing a rebuild after updates.

Check system-wide for broken libraries:

revdep

Rebuild affected packages:

# as root
pkgman update -fr $(revdep)

Inspect a specific package:

revdep -p bash

Run revdep regularly after updates or repository syncs to ensure all packages remain functional.

See revdep(1) and revdep.d(5) for more information.

5.3.4.2 Ignoring Packages During Updates

Prevent updates to specific packages during system upgrades with --ignore. Example:

# as root
pkgman sysup --deps --depsort --group --ignore=linux,linux-firmware

Regularly revisit ignored packages to avoid security risks and dependency conflicts.

5.3.4.3 Locking Packages

Lock a package to prevent upgrades indefinitely:

# as root
pkgman lock linux

Unlock a package to resume updates:

# as root
pkgman unlock linux

Locked packages are useful for maintaining specific software versions critical to workflows.

See pkgman-lock(8), pkgman-unlock(8), and pkgman-list-locked(1) for more information.

5.3.4.4 Handling Lower Available Versions

Occasionally, a package source may revert to a lower version than the one installed on your system. This often happens when maintainers roll back to a stable release after issues with a newer version. By default, pkgman does not prioritize installed versions over available ones.

To override this behavior, enable the preferhigher option:

# as root
pkgman --config-set="preferhigher yes" sysup --deps --depsort

This ensures pkgman retains the higher installed version during upgrades. Use this feature cautiously, as higher versions might be less stable or unsupported.

5.3.4.5 Practical Considerations

5.4 Extending Software Sources

5.4.1 Adding a Local Collection

This section explains how to create custom collections. A local collection allows you to add new packages that are not available in the official repositories.

5.4.1.1 Creating the Collection Directory

Create your local collection directory in /usr/src, alongside official Zeppe-Lin collections. Using directories with broader read permissions ensures that unprivileged users like pkgmk can access the collection.

Example:

# as root
cd /usr/src
mkdir mynewcollection

5.4.1.2 Enabling the Collection in pkgman.conf

Add your collection to /etc/pkgman.conf. List it above official collections to give it search priority:

pkgsrcdir /usr/src/mynewcollection
pkgsrcdir /usr/src/pkgsrc-core
pkgsrcdir /usr/src/pkgsrc-system
pkgsrcdir /usr/src/pkgsrc-xorg

Save the file and proceed to verify the setup.

5.4.1.3 Verifying the Collection Setup

Verify that pkgman(1) recognizes your collection:

pkgman dumpconfig

Check the “Packages sources directories” section for your collection:

Packages sources directories: <n>
  /usr/src/mynewcollection <-- here
  /usr/src/pkgsrc-core
  /usr/src/pkgsrc-system
  /usr/src/pkgsrc-xorg
  ...

If it appears, the setup is complete, and you can start adding package sources.

5.4.2 Creating Custom Packages

After setting up your local collection, the next step is to add a package source. This involves creating a directory for the package and writing a Pkgfile with build instructions and metadata.

5.4.2.1 Creating the Package Directory

Create a subdirectory for the new package in your local collection. Ensure the directory name matches the package name in the Pkgfile:

# mkdir /usr/src/mynewcollection/hello
# cd /usr/src/mynewcollection/hello

Important:

Naming consistency is critical. A mismatch between the directory name and name in the Pkgfile cause build error.

5.4.2.2 Writing the Pkgfile

The Pkgfile serves two purposes:

  1. Provides build instructions for pkgmk(8).
  2. Supplies metadata for pkgman(1) to enable dependency resolution, search, and queries.

Example Pkgfile:

# Description: Example package based on GNU hello
# URL:         https://www.gnu.org/software/hello/
# Depends on:  glibc

name=hello
version=2.12.1
release=1
source=https://ftp.gnu.org/gnu/hello/$name-$version.tar.gz

build() {
        cd $name-$version

        ./configure --prefix=/usr --disable-nls

        make V=1
        make DESTDIR=$PKG install

        rm -rf $PKG/usr/share/info
}

5.4.2.3 Key Metadata Headers for pkgman

Include these headers in the Pkgfile for optimal integration with pkgman(1):

Important:
Without these headers, pkgman(1) cannot display metadata or resolve dependencies.

5.4.2.4 Verifying the Package Source

Verify your package metadata with:

$ pkgman info hello
Name:         hello
Path:         /usr/src/mynewcollection/hello
Version:      2.12.1
Release:      1
Description:  Example package based on GNU hello
URL:          https://www.gnu.org/software/hello/
Depends on:   glibc

If metadata is missing, review the Pkgfile for errors.

5.4.3 Building and Installing Custom Packages

After creating a custom package source, the next step is to build and install the package using pkgman(1) and pkgmk(8), addressing any potential permission issues.

5.4.3.1 Building the Package

Build your custom package with:

# as root
pkgman install -vv --deps hello

This command:

  1. Downloads source files if not cached.
  2. Builds the package using pkgmk(8).
  3. Installs the package with pkgadd(8).

Note:

Warnings about missing .md5sum and .footprint files like /usr/bin/pkgmk: ... cannot create .md5sum: Permission denied occur because packages are built with the unprivileged pkgmk user, which has not write permissions in /usr/src/mynewcollection.

5.4.3.2 Resolving Permissions for Build Files

There are at least two options to resolve permissions case.

  1. Manually Updating Files.

    Navigate to the package directory and generate the files:

    # as root
    cd /usr/src/mynewcollection/hello
    pkgmk -um   # Update .md5sum
    pkgmk -uf   # Update .footprint
  2. Adjusting Permissions.

    Grant write access to pkgmk using setfacl(1):

    # as root
    setfacl -m u:pkgmk:rwx /usr/src/mynewcollection/hello

    Rebuild the package afterward:

    # as root
    pkgman update -fr hello

5.4.3.3 Verifying Generated Files

After addressing warnings, confirm the presence of required files:

$ ls -l /usr/src/mynewcollection/hello
-rw-r--r--  1 pkgmk pkgmk  251 .footprint
-rw-r--r--  1 pkgmk pkgmk   54 .md5sum
-rw-r--r--  1 root  root   359 Pkgfile

5.4.3.4 Verifying the Installation

Ensure the package is installed on your system:

$ pkgman isinst hello
package 'hello' is installed

Run:

$ hello
Hello, world!

5.4.4 Building Packages from Bleeding-Edge Sources

Some packages need to be built directly from version control systems (VCS) like Git or Subversion, instead of fixed release archives. This section outlines two approaches: a stable approach and a bleeding-edge approach.

5.4.4.1 Stable Commit Approach

For production, pinning to a specific commit ensures consistent builds. Example:

# Description: Ratpoison window manager
# URL:         https://www.nongnu.org/ratpoison/
# Depends on:  xorg-server

name=ratpoison
version=1.4.9-db94d49  # Release + commit hash
release=1

# Custom function to fetch or clone git repository
download_git() {
    if [ -d $PKGMK_SOURCE_DIR/$name ]; then
        git -C $PKGMK_SOURCE_DIR/$name fetch --depth=1
        git -C $PKGMK_SOURCE_DIR/$name clean -f
        git -C $PKGMK_SOURCE_DIR/$name reset --hard origin/$2
    else
        git -C $PKGMK_SOURCE_DIR clone --depth=1 $1 -b $2 $name
    fi
    cp -r $PKGMK_SOURCE_DIR/$name $PKGMK_WORK_DIR/src/$name
}

build() {
    download_git https://git.savannah.nongnu.org/git/ratpoison.git master
    cd ratpoison
    git checkout ${version#*-}  # Pin commit 'db94d49'

    ./configure --prefix=/usr
    make V=1
    make DESTDIR=$PKG install
}

Pinning builds guarantees stability and shields against upstream changes.

5.4.4.2 Bleeding-Edge Approach

For testing, dynamically set the version to the current date (YYYYMMDD) to track upstream changes. Example:

# Description: Ruby interpreter (bleeding-edge)
# URL:         http://www.ruby-lang.org
# Depends on:  gdbm libgmp openssl zlib libffi libyaml

name=ruby-scm
version=$(date +%Y%m%d)  # Version updates daily to track build date
release=1

download_git() {
    if [ -d $PKGMK_SOURCE_DIR/$name ]; then
        git -C $PKGMK_SOURCE_DIR/$name fetch --depth=1
        git -C $PKGMK_SOURCE_DIR/$name clean -f
        git -C $PKGMK_SOURCE_DIR/$name reset --hard origin/$2
    else
        git -C $PKGMK_SOURCE_DIR clone --depth=1 $1 -b $2 $name
    fi
    cp -r $PKGMK_SOURCE_DIR/$name $PKGMK_WORK_DIR/src/$name
}

build() {
    download_git https://github.com/ruby/ruby master
    cd ruby-scm

    ./configure --prefix=/usr
    make V=1
    make DESTDIR=$PKG install
}

“Daily Updates”: If the installed version is outdated, pkgman(1) flags it during pkgman diff, prompting updates:

pkgman diff

Expected output:

-- Differences between installed packages and packages sources tree
ruby-scm                        20250216-1          20250401-1

This approach is ideal for development but unsuitable for production environments.

5.4.4.3 Subversion Repository Download Function

For Subversion-based builds, use a download function like this:

download_svn() {
    if [ -d $PKGMK_SOURCE_DIR/$name ]; then
        svn cleanup $PKGMK_SOURCE_DIR/$name
        svn up $PKGMK_SOURCE_DIR/$name
    else
        svn co $1 $PKGMK_SOURCE_DIR/$name
    fi
    cp -r $PKGMK_SOURCE_DIR/$name $PKGMK_WORK_DIR/src/$name
}

Example usage in a Pkgfile:

download_svn https://svn.project.org/svn/program/trunk

Add subversion to dependencies for compatibility. Unlike git, which resides in pkgsrc-core, subversion requires explicit declaration.

5.4.4.4 Best Practices

5.5 Tips and Best Practices

5.5.1 Renicing Builds

Package builds can reduce system responsiveness due to high CPU and I/O usage. Mitigate this by adjusting process priorities using nice(1) and ionice(1):

Update the makecommand in /etc/pkgman.conf:

makecommand sudo -H -u pkgmk nice -n10 ionice -c2 -n6 fakeroot pkgmk

Explanation:

These changes help the build process interfere less with interactive tasks, improving system responsiveness.

5.5.2 RAM Builds for Faster Package Compilation

Using RAM for temporary build files (tmpfs) can significantly speed up package compilation in Zeppe-Lin.

5.5.2.1 Enable RAM Builds in Zeppe-Lin

  1. Uncomment this line in /etc/fstab:

    pkgmk /var/cache/pkgmk/work tmpfs size=<SIZE>,uid=<UID>,defaults 0 0

    Replace SIZE (e.g., 16G). The UID for the pkgmk user is preconfigured.

  2. Mount tmpfs:

    # as root
    mount pkgmk
  3. Remove -pipe from CFLAGS and CXXFLAGS in /etc/pkgmk.conf.

5.5.2.2 RAM Build Configuration (Educational)

For users interested in customizing the setup:

  1. Creating a dedicated build user:

    # as root
    useradd -b /var/cache/ -m -s /bin/false -U pkgmk
  2. Setting up build directories and assigning ownership:

    # as root
    mkdir -p /var/cache/pkgmk/{sources,packages,work}
    chown -R pkgmk:pkgmk /var/cache/pkgmk/{sources,packages,work}
  3. Configuring tmpfs in /etc/fstab:

    pkgmk /var/cache/pkgmk/work tmpfs size=<SIZE>,uid=<UID>,defaults 0 0
  4. Setting the work directory in /etc/pkgmk.conf:

    PKGMK_WORK_DIR="/var/cache/pkgmk/work/$name"
  5. Removing -pipe from CFLAGS and CXXFLAGS in /etc/pkgmk.conf.

5.5.3 Unprivileged Builds in Zeppe-Lin

Zeppe-Lin builds packages as an unprivileged user (typically pkgmk) by default. This secure and modular setup isolates the build process to mitigate risks. If you are using Zeppe-Lin, these steps are already configured out of the box. There is no need to repeat them.

However, this guide is provided for educational purposes, to show how it was achieved, or for users wanting to adapt this approach for other distributions or customizations.

  1. Create a Dedicated Build User. A restricted user is used to perform package builds securely:

    # as root
    useradd -b /var/cache/ -m -s /bin/false -U pkgmk

    This creates a user and group named pkgmk with limited shell access.

  2. Set Up Build Directories. Directories for sources, packages, and temporary files are created and assigned ownership to the pkgmk user:

    # as root
    mkdir -p /var/cache/pkgmk/{sources,packages,work}
    chown -R pkgmk:pkgmk /var/cache/pkgmk/{sources,packages,work}
  3. Configure pkgmk. In /etc/pkgmk.conf, the paths for these directories are defined:

    PKGMK_SOURCE_DIR="/var/cache/pkgmk/sources"
    PKGMK_PACKAGE_DIR="/var/cache/pkgmk/packages"
    PKGMK_WORK_DIR="/var/cache/pkgmk/work/$name-$$"
  4. Integrate pkgman. In /etc/pkgman.conf, the makecommand variable is updated to run pkgmk(8) as the unprivileged pkgmk user via sudo(8) and fakeroot(1):

    makecommand sudo -H -u pkgmk fakeroot pkgmk

This structure ensures package builds are securely isolated while remaining flexible for adaptations.


6 CONFIGURATION

6.1 Generating Locales

In Zeppe-Lin, glibc package includes a minimal set of locales by default, with C.UTF-8 generated for compatibility with pkgmk(8). To create additional locales, replace en_US with your desired locale and run:

# as root
localedef -i en_US -f UTF-8 en_US.UTF-8

Set the system default locale by adding it to /etc/profile:

export LANG=en_US.UTF-8

This ensures consistent locale settings across your system.

6.2 System Initialization and Configuration

Zeppe-Lin uses a lightweight BSD-style init system to manage startup, services, and shutdown. This system relies on shell scripts located in /etc and /etc/rc.d, offering an efficient and flexible way to control processes.

6.2.1 System States (Runlevels)

Runlevels define the operating state of the system, such as halting, rebooting, or normal operation. Zeppe-Lin’s runlevels are configured in /etc/inittab:

Runlevel State Purpose
0 Halt Shut down the system completely
1 (S) Single-User Mode Maintenance mode with minimal setup
2 Multi-User Mode Full system operation with multiple users
3-5 Unused Reserved for custom use
6 Reboot Restart the system

By default, Zeppe-Lin boots into multi-user mode (runlevel 2), which enables all services and user access. Single-user mode (runlevel 1 or S) is ideal for system maintenance, providing a minimal environment.

6.2.2 Core Init Scripts

Zeppe-Lin’s init system uses the following scripts to manage system processes:

File/Directory Purpose
/etc/rc Main system boot script, invoked by init(8)
/etc/rc.modules Kernel module initialization, invoked by /etc/rc
/etc/rc.single Single-user mode script for maintenance tasks
/etc/rc.multi Multi-user mode script for normal operation
/etc/rc.local Custom commands executed after /etc/rc.multi during normal boot
/etc/rc.d Directory containing scripts to control individual services
/etc/rc.shutdown Script invoked by shutdown(8) to gracefully stop services

Customize system behavior by modifying /etc/rc.local for personal commands or /etc/rc.modules to load specific kernel modules.

See rc(8) for details.

6.2.3 Configuring Runlevels and Init Behavior

The /etc/inittab file determines actions for each runlevel. Each entry follows this format: id:runlevel:action:command

Key Components:

Example /etc/inittab Configuration:

# Set default runlevel to multi-user mode
id:2:initdefault:

# Core startup scripts
rc::sysinit:/etc/rc         # Main boot script
rs:S1:wait:/etc/rc.single   # Single-user mode startup
rm:2:wait:/etc/rc.multi     # Multi-user mode startup
rd:06:wait:/etc/rc.shutdown # Graceful shutdown

# Virtual console login prompts
c1:2:respawn:/sbin/agetty --noclear 38400 tty1 linux
c2:2:respawn:/sbin/agetty 38400 tty2 linux

This configuration ensures a smooth startup, runtime, and shutdown process. See inittab(5) for configuration details.

6.2.4 Configuring System Behavior (/etc/rc.conf)

The /etc/rc.conf file centralizes system configuration settings, such as hostname, timezone, and services. Example:

HOSTNAME="myhostname"
TIMEZONE="Europe/Berlin"
SERVICES="lo crond sshd"

Services listed in SERVICES correspond to scripts in /etc/rc.d/, which are started by /etc/rc.multi and stopped by /etc/rc.shutdown (in reverse order).

See rc.conf(5) for additional configuration settings.

6.2.5 Service Control Scripts (/etc/rc.d/)

The /etc/rc.d/ directory contains individual scripts to manage services. Each script supports commands like:

Example usage:

# as root
/etc/rc.d/sshd start
/etc/rc.d/sshd status

See rc.d(7) for more information.

6.2.6 Kernel Module Management (/etc/rc.modules)

The /etc/rc.modules script is used to initialize kernel modules at boot. It begins by running /sbin/depmod -a to refresh the module dependency map, ensuring correct load order.

After this, rc.modules delegates to the standalone modules-load(8) utility. This utility reads configuration files from the following directories:

Files must end with .conf and contain one module name per line. Empty lines and lines beginning with # are ignored. An empty or comment-only file in /etc/modules-load.d disables vendor or runtime lists of the same name.

Example configuration file /etc/modules-load.d/virtio.conf:

# Load virtio drivers at boot
virtio-net
virtio-blk

Note:
For the design reasoning behind modules-load and its configuration directories, see RATIONALE.md in the source tree.

For modules requiring parameters, configure them in /etc/modprobe.d/ for persistent management.
For example, to set options for the snd_hda_intel module, create /etc/modprobe.d/snd_hda_intel.conf:

# Example: limit snd_hda_intel probing to a specific device
# (probe_mask is hardware-dependent; 8 is only an example)
options snd_hda_intel probe_mask=8

This ensures parameters are automatically applied whenever the module is loaded, avoiding manual specification and following best practice.

Custom one-off module commands may still be added directly to /etc/rc.modules after the modules-load call, e.g.:

# Example: load loopback block device support at boot
/sbin/modprobe loop

# Example: force-load Intel HDA sound driver at boot
# (normally auto-loaded; shown here for illustration)
/sbin/modprobe snd_hda_intel

The /etc/rc.modules script itself is invoked by /etc/rc during the boot process.

6.2.7 Custom Startup Commands (/etc/rc.local)

User-defined actions are added to /etc/rc.local, executed as last step of a normal boot (invoked by /etc/rc.multi). Example:

# Start a monitoring app
/usr/bin/my_monitoring_app &

6.2.8 From Boot to Login: System Startup Sequence

Here’s the sequence from power-up to login. It differs slightly with or without initramfs.

6.2.8.1 Without Initramfs

  1. Power On: System starts.
  2. Bootloader: Loads the kernel into memory.
  3. Kernel starts: Initializes hardware and memory.
  4. /sbin/init starts: The first user-space process (PID 1).
  5. /etc/inittab is read: init gets its instructions.
  6. /etc/rc runs: The main boot script executes.
  7. Runlevel is set: The default runlevel (usually 2) is chosen.
  8. /etc/rc.multi starts services: Services from /etc/rc.conf are launched.
  9. /etc/rc.local runs: Your custom commands are executed.
  10. Login prompts: The system is ready for you.

6.2.8.2 With Initramfs

  1. Power On: System starts.
  2. Bootloader: Loads kernel and initramfs into memory.
  3. Kernel starts: Initializes basic hardware.
  4. initramfs mounts: A temporary root filesystem in RAM.
  5. /init from initramfs runs: Performs early setup (e.g., storage drivers).
  6. Real root mounts: Your main filesystem is mounted.
  7. Root is switched: System moves from initramfs to your real root.
  8. /sbin/init on real root starts: The init process from your main system.
  9. (Steps 5-10 from Without Initramfs follow).

6.2.9 Minimal Mode (Single-User Mode)

Runlevel 1 or S boots the system with a minimal setup for maintenance. It runs /etc/rc.single, providing a root shell for troubleshooting.

6.2.10 Graceful Shutdown Procedure

When shutting down or rebooting, /etc/rc.shutdown is invoked to stop services, unmount filesystems, and safely power off the system.

6.3 Networking

6.3.1 Hostname Configuration

The hostname identifies your system on the network.

To set it temporarily, use:

# as root
hostname <new_hostname>

For a permanent setting, edit /etc/rc.conf and set:

HOSTNAME='<new_hostname>'

For proper network function, edit /etc/hosts and add your hostname to the loopback lines:

127.0.0.1 localhost <new_hostname>
::1       localhost <new_hostname>

This ensures your system recognizes its own name.

6.3.2 Static Address

To configure a static IP, edit /etc/rc.d/net. Set the network interface, IP, subnet mask, and gateway. For example:

DEV=enp11s0
ADDR=192.168.1.100
MASK=24
GW=192.168.1.1

Set DNS in /etc/resolv.conf:

search <your internal domain>
nameserver <your DNS server>

Start, stop or restart the service via:

# as root
/etc/rc.d/net [start|stop|restart]

Important:

To auto-start service at boot, add net to the SERVICES line in /etc/rc.conf.

6.3.3 Dynamic Address

To get an IP address automatically via DHCP, install the dhcpcd package and edit /etc/rc.d/dhcpcd to specify the network interface (e.g., enp5s0):

DEV=enp5s0

Start, stop, or restart the service via:

# as root
/etc/rc.d/dhcpcd [start|stop|restart]

Check dhcpcd’s README for potential issues:

pkgman readme dhcpcd

Important:

To auto-start service at boot, add dhcpcd to the SERVICES line in /etc/rc.conf.

6.3.4 Wireless Network

Ensure the wireless interface isn’t blocked. Use rfkill(8):

rfkill list

# as root
rfkill unblock <ID|TYPE>

Install the wpa-supplicant package and edit /etc/rc.d/wpa_supplicant to set the wireless interface:

DEV=wlp1s0

Update /etc/wpa_supplicant.conf based on your network settings. Common security configurations follow.

6.3.4.1 WPA-PSK (Pre-Shared Key)

Generate a key with wpa_passphrase(8):

wpa_passphrase <MYSSID> <PASSPHRASE>

Example /etc/wpa_supplicant.conf:

network={
    ssid="YOUR_NETWORK_NAME"
    psk="THE_GENERATED_KEY"
}

6.3.4.2 WPA-EAP (Enterprise)

Generate a password hash:

echo -n <PASSPHRASE> | iconv -t utf16le | openssl md4

Example /etc/wpa_supplicant.conf:

network={
    ssid="YOUR_NETWORK_NAME"
    key_mgmt=WPA-EAP
    identity="YOUR_USERNAME"
    password=hash:<YOUR_PASSWORD_HASH>
}

6.3.4.3 WEP

Example /etc/wpa_supplicant.conf for WEP:

network={
    ssid="YOUR_NETWORK_NAME"
    key_mgmt=NONE
    wep_key0="YOUR_WEP_KEY"
    wep_tx_keyidx=0
    auth_alg=SHARED
}

For automatic IP, ensure the wireless interface is set in /etc/rc.d/dhcpcd. Start the services:

# as root
/etc/rc.d/wpa_supplicant start
/etc/rc.d/dhcpcd start

Important:

To auto-start service at boot, add wpa_supplicant (and dhcpcd if using dynamic IP) to the SERVICES line in /etc/rc.conf.

6.4 Time and Date Configuration

6.4.1 Timezone Setup

Select your timezone from /usr/share/zoneinfo (e.g., Europe/Berlin).

Set the timezone immediately:

# as root
ln -sfn /usr/share/zoneinfo/Europe/Berlin /etc/localtime

To make it persistent, add the timezone to /etc/rc.conf:

TIMEZONE="Europe/Berlin"

6.4.2 System Clock Setup

Manually set the system clock using the MMDDhhmmYYYY.ss format:

# as root

# Example: March 27, 05:35, 2025
date 032705352025.00

Important:

Manual configuration is temporary. Use NTP for continuous synchronization (described next).

6.4.3 Network Time Protocol (NTP) Configuration

Note:

Accurate time via NTP is crucial for time-sensitive things like OTP.

Use an NTP client, such as chrony, to synchronize the system clock.

After installing chrony, start, stop, or restart the service via:

# as root
/etc/rc.d/chrony [start|stop|restart]

Important:

To enable automatic startup, add chrony to the SERVICES line in /etc/rc.conf.

6.5 User Authentication and Environment

This section explains Zeppe-Lin’s approach to user authentication, environment setup, and PAM (Pluggable Authentication Modules).

6.5.1 Password Management

Zeppe-Lin uses libxcrypt to provide the crypt(3) API for password hashing.

The default hash algorithm is SHA-512, ensuring strong security. Legacy algorithms (MD5, traditional DES) are supported for compatibility, but their use is discouraged.

Programs that authenticate users via crypt(3) must link against libxcrypt (-lcrypt).

System-wide password policies are defined in /etc/login.defs. Key parameters include:

Use passwd(1) to set or change user passwords. SHA-512 is recommended for all accounts.

6.5.2 User Environment

User accounts are created via useradd(8) according to /etc/login.defs.

Home directories created with useradd -m are empty by default. To provide default files, use /etc/skel.

The default shell PATH is defined in /etc/profile:

/sbin:/usr/sbin:/bin:/usr/bin

This allows access to administrative commands without specifying full paths.

6.5.3 Pluggable Authentication Modules (PAM)

Zeppe-Lin uses PAM for authentication, session management, and resource control.

PAM configuration files are located in /etc/pam.d. Each file corresponds to a specific service (e.g., login, su, sshd). Key modules included:

For advanced configurations, consult module-specific man pages (e.g., pam_dumb_runtime_dir(8), pam_env(8), pam_limits(8), pam_exec(8)).


7 APPENDICES

This appendix summarizes the licensing and legal terms applicable to Zeppe-Lin, including software components, build scripts, website content, and artwork.

7.1.1 Software & Packages

Zeppe-Lin is composed entirely of free and open-source software obtained from publicly available upstream projects.

Each package retains its original upstream license (including GPL, BSD, MIT, Apache, and others). Zeppe-Lin does not relicense, restrict, or replace upstream licensing terms.

The distributed root filesystem and any binary packages are built from these sources without the inclusion of proprietary software. Patches applied during the build process are limited to correctness, portability, or security fixes and do not alter licensing conditions.

Redistribution and use of binaries are governed solely by the licenses of the upstream projects from which they originate.

7.1.2 Build Scripts

Build scripts and related tooling are derived from CRUX and extended by the Zeppe-Lin project.

These scripts are licensed under the GNU General Public License version 3 or later (GPLv3+).

7.1.3 Website & Artwork

Website content and visual assets, including documentation text, branding, and release artwork, are maintained by the Zeppe-Lin project.

This material is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License (CC-BY-SA 3.0).

This license applies only to website content and artwork. Source code, packages, and build scripts are governed by their respective licenses.

Artwork repository: https://github.com/zeppe-lin/artwork
Website repository: https://github.com/zeppe-lin/zeppe-lin.github.io/

7.1.4 Disclaimer

Zeppe-Lin is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Use it at YOUR OWN RISK.

7.2 Appendix B: Troubleshooting

This appendix collects solutions to common issues in Zeppe‑Lin. Topics are grouped by area for easier navigation.

7.2.1 dhcpcd Networking Issues

7.2.1.1 Overview

Covers common problems with dhcpcd, including carrier loss, client ID mismatches, and noncompliant routers. Edit /etc/dhcpcd/dhcpcd.conf to apply fixes.

7.2.1.2 Carrier Lost

If dhcpcd logs show messages like:

dhcpcd[852]: enp0s31f6: carrier lost

or

dhcpcd[852]: enp0s31f6: waiting for carrier
dhcpcd[852]: carrier acquired
dhcpcd[852]: soliciting an IPv6 router
dhcpcd[852]: no global addresses for default route
dhcpcd[852]: timed out

You may try adding the following options to /etc/dhcpcd/dhcpcd.conf:

nolink   # Ignore carrier status messages (for buggy drivers)
noipv6   # Disable IPv6 router advertisements

7.2.1.3 Client ID Issues

If your DHCPv4 network filters Client IDs by MAC address, change:

duid

To:

clientid

This forces dhcpcd to use the hardware MAC address for DHCPv4, avoiding issues with RFC4361-style identifiers. See RFC 4361 for details.

7.2.1.4 Noncompliant Routers

Some routers reject requests unless you comment out:

require dhcp_server_identifier

This is safe unless you have multiple DHCP servers on the same network. See Microsoft Technet for background.

7.2.1.5 Quick Summary

7.3 Appendix C: Frequently Asked Questions

This appendix collects common questions about Zeppe‑Lin. Questions are grouped by topic for easier navigation.

7.3.1 General

Why is the distribution named Zeppe-Lin?

The name is a deliberate nod to classic UNIX culture and the early era of Linux experimentation. It reflects the project’s appreciation for engineering craft, simplicity, and a certain irreverent independence.

The name carries no technical meaning and should not be over-interpreted.

Or, if you prefer, you can consider it our “Whole Lotta Love” for Linux and GNU — just renamed!

Is Zeppe-Lin a fork of another distribution?

Yes. Zeppe‑Lin is a fork of CRUX, a lightweight, source‑based Linux distribution.

It inherits CRUX’s minimalist philosophy and tooling, but evolves them independently, with its own design decisions and policies.

What makes Zeppe-Lin different from CRUX or KISS Linux?

Zeppe-Lin focuses on practical minimalism rather than extreme reduction or strict preservation of legacy behavior.

Compared to CRUX, Zeppe-Lin actively refactors core tooling instead of keeping it unchanged for compatibility’s sake. Package management, build utilities, and system tools are reorganized for clarity, consistency, and long-term maintainability.

Compared to KISS Linux, Zeppe-Lin favors clear structure and explicit behavior over minimal code size. It is willing to use more capable implementations when they improve correctness, auditability, or usability.

Across the system, Zeppe-Lin generally favors:

Zeppe-Lin does not aim to be the smallest possible system. It aims to remain understandable and controllable as it evolves.

Is Zeppe-Lin suitable for beginners?

No. Zeppe-Lin assumes prior Linux experience. Users are expected to be comfortable with the command line, manual configuration, and building software from source.

It is not designed to be beginner-friendly, automated, or “hands-off”.

7.3.2 Installation

I can’t find the Zeppe‑Lin installation CD/DVD. Did I miss something?

No installation media exists. Zeppe-Lin is installed using a minimal root filesystem tarball. Installation is performed from a live environment of your choice.

Why is there no installer?

An installer would obscure system layout and behavior behind automation.

Zeppe-Lin intentionally exposes system setup so users understand what is installed, how it is configured, and why it works. This approach aligns with the project’s emphasis on clarity and control.

7.3.3 Configuration

Does Zeppe-Lin use systemd?

No. Zeppe-Lin uses a traditional, script-based init system.

Service management is explicit and readable, and does not rely on a monolithic service manager.

7.3.4 Packages and Features

Where is the package or feature I need?

Zeppe-Lin is community-driven. If a package or feature is missing, users are encouraged to add it.

Follow the packaging(7) guidelines and submit a pull request to the appropriate repository.

The system evolves through user contributions rather than centralized curation.

Why are some familiar CRUX tools missing or replaced?

Some tools were removed or replaced because their original design goals no longer aligned with Zeppe-Lin’s direction.

When this happens, replacements are chosen to be simpler, more explicit, or easier to maintain. Each major tool includes documentation explaining its design and differences.

7.3.5 Maintenance

How often does Zeppe-Lin release new versions?

Zeppe-Lin follows a semi-rolling release model.

The toolchain (compiler, libc, and related components) is kept stable for extended periods. Userland packages evolve on top of this stable base.

When accumulated changes make the existing toolchain a limiting factor, the toolchain is updated, userland is rebuilt and polished, and a new major version is released.

Between major releases, minor versions (e.g., v1.1, v1.2) are published when enough non-toolchain changes accumulate to justify a new root filesystem.

There is no fixed schedule. Releases happen when they are ready.

Is Zeppe-Lin stable?

Zeppe-Lin prioritizes correctness and predictability over rapid change.

While it is source-based and expects user involvement, care is taken to avoid unnecessary breakage, especially within a stable toolchain cycle.

Important packaging changes, required manual interventions, and security updates are announced on the mailing list. Users are strongly encouraged to subscribe and follow these notices.

7.3.6 Support and Community

How can I report bugs or issues?

See Appendix D: Reporting Issues. Report issues in the repository corresponding to the affected component (packages, tools, website, or artwork). Include version, logs, steps to reproduce, and any relevant local modifications.

Can I contribute?

Yes. Contributions can include:

Follow repository guidelines, and submit a pull request to the appropriate repository.

7.3.7 Philosophy

Is Zeppe-Lin trying to compete with other distributions?

No. Zeppe-Lin exists to satisfy its own design goals and the needs of its users.

It does not aim to replace other distributions or appeal to a broad audience.

7.4 Appendix D: Reporting Issues

This appendix explains how to report problems with Zeppe‑Lin. Clear, well-structured reports help the team respond efficiently.

7.4.1 Where to Report

Submit issues in the repository that corresponds to the type of problem:

You can subscribe to or report via mailing list:

7.4.2 What to Include

Provide as much relevant information as possible:

7.4.3 Reporting Guidelines

Tip:

Well-prepared reports get faster responses and make troubleshooting easier for both users and developers.