Alpine Linux
Alpine Linux is a security-oriented, lightweight Linux distribution based on musl libc and busybox.
Motivation
The main reason for moving to Alpine Linux from Arch was to avoid the all or nothing method of updating packages. Since i was taking btrfs snapshots, i could see 3GB+ updates every month, which i felt was excessive compared to my needs. Around this time, i was also trying to replaced the default OS on the Pi Zero 2W due to 512M memory.
Migration path
In our home computers , i installed this OS to homepc2 along with Arch . I have also configured it such that both are identical in almost all functionalities. I intend to use this with pi instead of the default OS in it.
A few months back i.e around mid-april 2025, totally nuked the Arch installation.
The biggest negative is that proprietary software will not work on Alpine, unless the binary is released by the vendor for Alpine Linux.
For the above issue, a debian chroot has been configured with bwrap by following the Alpine Wiki. Tested working with VS Code, Obsidian, google-chrome etc..
Taking an inspiration from Arch wiki, i have been updating Alpine Wiki↗ as part of giving back something to the community.
Found that gcompat can provide a compatibility to glibc and tested to find that it really works. I have updated the wiki of Alpine Linux mainly by re-organizing and existing content.
The configuration in Alpine is nearly identical to earlier setup i.e swaywm , hugo_server , rsync , rclone , btrfs filesystem with btrbk , Refind , Emacs , Beancount etc.
I have managed to get the Canon LBP2900 also configured manually as no package exists for it in Alpine Linux. As of 2024-12-04, Canon installation is not complete. At some point, the manual driver installation was completed drivers works great here.
Installed shotwell as lightweight photo manager to view timeline based photos.
replaced cronie by dcron
Earlier i was using anacron from cronie for running backup for btrbk and Beancount . However, recently understood that dcron does this elegantly with @daily directive.
So moved the entries in etc/anacrontab to /var/spool/cron/crontabs/root as follows:
# min hour day month weekday ID command
*/15 * * * * run-parts /etc/periodic/15min
@hourly ID=periodic.hourly run-parts /etc/periodic/hourly
@daily ID=periodic.daily run-parts /etc/periodic/daily
@weekly ID=periodic.weekly run-parts /etc/periodic/weekly
@monthly ID=periodic.monthly run-parts /etc/periodic/monthly
@daily ID=daily-btrbk sleep 420 && /usr/bin/btrbk -q -c /etc/btrbk/btrbk.conf run
@daily ID=daily-beanbk sleep 240 && /data/myhome/prabu/linux_config/scripts/backup_beancount 2>&1 | logger -t daily-beanbk
@weekly ID=weekly-fstrim sleep 720 && /sbin/fstrim -v /mnt/btrfs 2>&1 | logger -t weekly-fstrim
sending emails meant for root to gmail
To send and receive emails meant for root to a gmail account, installed and configured msmtp as MTA/SMTP client and mailx as mail user agent(MUA). Configured msmtp by copying the /etc/msmtprc from Openwrt .
$ cat /etc/aliases
root: kXXXX@gmail.com
$ ls -l /usr/sbin/sendmail
lrwxrwxrwx 1 root root 14 Oct 10 14:15 /usr/sbin/sendmail -> /usr/bin/msmtp*
$ ls -l /etc/msmtprc
-rw------- 1 root root 1580 Oct 10 14:11 /etc/msmtprc
Using edge as a tagged repository
Initially kept edge main and community as tagged repositories along with stable as apk-tools v3 was in edge only during mid'25.
Found the following solution to install a package without enabling the repository in IRC:
# apk add --repository=https://dl-cdn.alpinelinux.org/alpine/edge/main apk-tools
Well the above option fails, when time for upgrade comes. The package automatically goes to the untagged repository. So i restored back edge in /etc/apk/repositories and due to various other issues, went back to apk-tools v2 and removed the edge repositories.
OS upgrades
On 2024-12-04, the first OS upgrade from 3.20 to 3.21 was done. Followed the progress through RC1 to RC5 as based on wiki instructions using sed to change the version in /etc/apk/repositories file.
| Date | upgraded OS version | Comments |
|---|---|---|
| 2024-12-04 | v3.21 | First upgrade |
| 2025-05-30 | v3.22 | |
| 2025-12-03 | v3.23 | |
| 2025-06-09 | v3.24 | venv changes required due to python upgrade |
Python venv
Due to python upgrade, venv was broken for beancount and graph generation scripts. Deleted the old venv and recreated them and then installed the packages. The venv are located at data/myhome/prabu/org.venv and data/myhome/prabu/bean_venv, with the later symlinked from /data/myhome/prabu/beancount.venv.
graph generation script uses the following packages:
- fa2
- networkx
- python-louvain
beancount venv uses the following packages including some for importers:
- fava
- msoffcrypto-tool
- openpyxl
- smart_importer
So the steps are:
rm -rf /data/myhome/prabu/org/.venv
python3 -m venv /data/myhome/prabu/org/.venv
source /data/myhome/prabu/org/.venv/bin/activate.fish
pip install fa2 networkx python-louvain
TP Link adapter TL-WN823N with Realtek chipset
Earlier used a upgrade driver for the above adapter in Arch . When i moved to Alpine Linux it was working out of the box, so never bothered until today. Unable to connect from any android mobile as in termux arp it shows as “incomplete wlan0”.
On further troubleshooting found that the default driver rtl8xxxu has this known behavior of not working with Android mobiles.
After installing the driver from https://github.com/clnhub/rtl8192eu-linux↗ the issue got resolved.
TL-WN823Nv2/v3 is supported by the above driver.
[Sat Aug 23 13:28:27 2025] 8192eu: loading out-of-tree module taints kernel.
[Sat Aug 23 13:28:27 2025] 8192eu: module verification failed: signature and/or required key missing - tainting kernel
[Sat Aug 23 13:28:27 2025] usbcore: registered new interface driver rtl8192eu
The loading of the driver was automated using the below script:
cat /etc/modprobe.d/rtl8192eu.conf
# Blacklist conflicting drivers
blacklist rtl8xxxu
# Auto-load new driver
install rtl8192eu /sbin/modprobe --ignore-install 8192eu
However still the WiFi module loading failed when kernel got upgraded. So added a script to recompile and load the driver was added as apk commit-hook
ls -l /etc/apk/commit_hooks.d/rebuild-rtl8192eu
-rwxr-xr-x 1 root root 1129 Aug 28 10:15 /etc/apk/commit_hooks.d/rebuild-rtl8192eu*
HTPC
If there some high quality speakers lying around checkout the HTPC page.
Convert sys mode to diskless
- Set media name to save apkvol in the /etc/lbu/lbu.conf file LBU_MEDIA=mmcblk0p1 (Replace mmcblk0p1 with the correct partition name.)
- Configure /etc/fstab for Diskless Operation
- Ensure the media partition (LBU_MEDIA) is listed with noauto.
- Prevent it from being mounted read-write elsewhere.
- Example: If the media was previously used for /boot. Update /etc/fstab:
- UUID=E8C9-4979 /boot vfat noauto,ro,relatime,fmask=0022,dmask=0022,errors=remount-ro 0 2
- UUID=E8C9-4979 /media/mmcblk0p1 vfat noauto,ro 0 0
- Note: If /boot partition is FAT and was previously mounted automatically, no additional changes are needed for booting the diskless OS.
- Mount Root (/) Filesystem as Read-Only. Update /etc/fstab:
- UUID=8e293498-41d6-4d3a-9832-fb4d3334dcc9 / ext4 ro,relatime 0 1
- Include Additional Files for Persistence outside of etc folder using lbu include command
- Commit Changes & Create apkvol using the command $doas lbu commit
- Reboot the pi into Diskless Mode .. $doas reboot
update packages.. or upgrade to newer release or update kernel..
Remount (/) root and /boot(mandatory if kernel update is involved) as Read-Write
- $doas mount -o remount,rw /
- $doas mount -o remount,rw /boot
Perform Updates finish updating of packages/kernel change or configure files
Synchronize Disk Writes $doas sync
Remount as Read-Only
- $doas mount -o remount,ro /
- $doas mount -o remount,ro /boot
Commit Changes to apkvol $lbu commit
Reboot $doas reboot
For unbound following additional changes made Added roothints as per alpine linux wiki Update /etc/resolv.conf to use Unbound locally doas sh -c ’echo -e “nameserver 127.0.0.1\nsearch lan” > /etc/resolv.conf'
prabu@pizero2w ~> cat /etc/resolv.conf nameserver 127.0.0.1 search lan Prevent /etc/resolv.conf from being overwritten doas chattr +i /etc/resolv.conf doas chattr -i /etc/resolv.conf (to update later)
on the homepc2, the following changes made
Solution 1: Disable DHCP-provided DNS in iwd NameResolvingService=none # <— Change this from resolvconf to none Adjust resolvconf.conf (Preferred)
Edit /etc/resolvconf.conf to control what openresolv writes:
resolv_conf=/etc/resolv.conf name_servers=192.168.1.3 name_servers_only=yes # <— Add this line
The name_servers_only=yes directive ensures only your specified DNS servers (192.168.1.3) are listed.
Then, regenerate /etc/resolv.conf: $ doas resolvconf -u
In case of any issues in 192.168.1.3, just change the main.conf file and restart iwd: NameResolvingService=resolvconf # <— Change this from none to resolvconf prabu@homepc2 ~> doas service iwd restart prabu@homepc2 ~> cat /etc/resolv.conf
search lan nameserver 192.168.1.3 nameserver 192.168.1.1
Contributing to aports
Workflow summary
A Git fork is a personal copy of a repository. My fork lives on my GitLab account (prabuanand/aports.git) and serves as a central workspace for all my changes.
The initial forking of aports can be done on gitlab.alpinelinux.org to ones’ namespace.
For each new aport i want to contribute, i should create a new branch from my fork’s master branch. This keeps each contribution isolated and prevents conflicts.
Initial setup
The following one time setup must be done:
aports setup
git clone https://gitlab.alpinelinux.org/alpine/aports.git
cd aports
Change from https to git to complete setup for SSH access by changing your fork to use SSH. With SSH configured, one can push and pull without entering credentials.
git remote set-url origin git@gitlab.alpinelinux.org:prabuanand/aports.git 2>/dev/null || git remote add origin git@gitlab.alpinelinux.org:prabuanand/aports.git
The below command is an idempotent one-liner: “Update the upstream remote URL if it exists; otherwise, add it.”
git remote set-url upstream https://gitlab.alpinelinux.org/alpine/aports.git 2>/dev/null || git remote add upstream https://gitlab.alpinelinux.org/alpine/aports.git
Since i am not Alpine Linux developer, i don’t have write access to main Alpine repo. To prevent unwanted pushes i issue the below command:
git remote set-url --push upstream no_push
The outcome can be verified as follows:
prabu@homepc2 /d/m/p/l/a/aports (master)> git remote -v
origin git@gitlab.alpinelinux.org:prabuanand/aports.git (fetch)
origin git@gitlab.alpinelinux.org:prabuanand/aports.git (push)
upstream https://gitlab.alpinelinux.org/alpine/aports.git (fetch)
upstream no_push (push)
ssh setup
Check if you have SSH keys locally. If you don’t have keys, generate them first.
ls -la ~/.ssh/
ssh-keygen -t ed25519 -C "your.email@example.com"
Add key to SSH agent
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
Copy public key and add it to your GitLab account from the website
cat ~/.ssh/id_ed25519.pub
Test the SSH Connection to gitlab.
ssh -T git@gitlab.alpinelinux.org
Welcome to GitLab, @prabuanand!
Steps for each contribution/update
Sync with master
For each new package update/contribution, make sure local and fork master are current by updating local master branch. This must be done before everytime a new branch is created.
git fetch upstream master
git checkout -B master upstream/master
If i want my fork’s master to look current on the GitLab web UI, use GitLab’s “Update fork” button. It’s a server-side sync, unaffected by local shallow depth.
Create a branch
Before creating a new branch, ensure no stale branch of the same name already exists on your fork:
git ls-remote origin <branch-name>
Create a feature branch from up-to-date master. A sample <branch-name> can be rtl8192eu-src, the same as aports package name.
git checkout -b <branch-name> master
Make the necessary changes like edit APKBUILD, bump pkgver, update checksums, etc.
abuild checksum
abuild -r
Now commit as per COMMITSTYLE.md document.
git add <path/to/APKBUILD>
git commit -m "testing/rtl8192eu-src: upgrade to 5.11.2.3_git20260629"
Push the changes to my fork.
git push origin <branch-name>
Open MR on GitLab website (link is printed after push), target alpine/aports:master
Now wait for the pipelines to finish. Wait for someone to approve the MR.
Monitor Package status
- Watch for any build failures or issues
- Check Alpine’s build logs at: https://build.alpinelinux.org/↗
- Monitor package status in different Alpine versions (edge, v3.19, etc.)
Handling reviewer feedback
If a reviewer requests changes on the MR, edit and push updates on the same branch as GitLab updates the existing MR automatically. Do not create new branch or new MR.
git checkout <branch-name>
Make the requested change i.e edit APKBUILD as requested.
If the MR still has just your one original commit, amend it rather than stacking a new commit. This keeps history clean for a single-aport MR.
git add <path/to/APKBUILD>
git commit --amend --no-edit
Refresh the remote branch pointer first then Push. Here – force is required since amending rewrites the commit hash.
git fetch origin <branch-name>
git push --force-with-lease origin <branch-name>
Remember to reply to the reviewer’s comment on the MR (web UI) confirming it’s addressed and mark it as resolved. The pipeline re-runs automatically against the new commit.
Only use git rebase master (see Rebase MR below) if the MR is also flagged as behind/conflicting with upstream master.
Rebase MR
If master moved forward before MR merges then additional steps are required. This is very normal as others might have submitted MR’s or commits might have happened.
git fetch upstream master
git checkout -B master upstream/master
Rebasing one’s own single commit or branch onto a freshly reset master is safe here. It’s just our own branch’s history changing, not shared history.
Resolve any conflicts if git rebase below reports any. The –force option in the push command only writes to our own feature branch, not master.
git checkout <branch-name>
git rebase master
git fetch origin <branch-name>
git push --force-with-lease origin <branch-name>
Resolve conflicts
When git rebase master runs, git replays my commit(s) one at a time on top of the new master. A conflict happens when the same lines of a file were changed both by me and by someone else’s merged commit — git can’t automatically decide which version wins, so it pauses and asks.
After git rebase master stops on a conflict:
git status
This lists the conflicted file(s), e.g. testing/rtl8192eu-src/APKBUILD.
Open the file — git has inserted conflict markers:
<<<<<<< HEAD
pkgver=5.11.2.4
=======
pkgver=5.11.2.3_git20260701
>>>>>>> your-commit-message
Everything between <<<<<<< HEAD and ======= is what's now on master (someone else's change).
Everything between ======= and >>>>>>> is your change.
Edit the file by hand to keep the correct final version. Often we keep
ours, but check. If the conflict is in shared boilerplate near my
edit, i may need to keep master’s version and reapply my change around
it. Remove the <<<<<<<, =====, >>>>>>> markers entirely — they’re
not valid syntax, just leave clean file content.
Then continue rebase:
git add <path/to/APKBUILD>
git rebase --continue
If there are more commits queued in the rebase, git may pause again for the next conflict. Repeat the above steps. When done, we’re back at a clean state. Now fetch the remote branch and force-push, same as in Rebase MR above.
The force-push is required because rebase rewrote our commit(s) with new hashes and the remote branch has the old ones.
If you get stuck or want to bail out entirely:
git rebase --abort
This puts you back exactly where you were before starting the rebase. It’s always safe to use if you’re unsure.
For a single-package aport, conflicts are usually rare and small. If at all, it’s mostly just pkgver/pkgrel/checksum lines if someone else touched the same package, which is uncommon unless two people are working on the same aport simultaneously.
Cleanup after MR is merged
The final cleanup must be performed once the MR is merged by someone, so the fork is clean both locally and on the gitlab instance.
git push origin --delete <branch-name>
git fetch upstream master
git checkout -B master upstream/master
git branch -d <branch-name>
Things to avoid
- Don’t push to origin/master at all. MRs diff against upstream/master directly, and your fork’s master is likely a protected branch that will reject force-pushes anyway. Use GitLab’s “Update fork” button if you want it to look current in the web UI.
- Don’t shallow-clone this repo if you intend to push branches. Alpine’s GitLab rejects shallow-history pushes for any branch, not just master.
Remember to clean up any build artifacts
rm -rf ~/packages/testing/x86_64/rtl8192eu-src-*
Set Up Anitya Monitoring for Your Project
- Visit Anitya https://release-monitoring.org/↗
- Sign up for an account on Anitya if you don’t have one You can usually sign in with GitHub, GitLab, or other OAuth providers
- Add Your Project like below: Project Name: rtl8192eu-linux-driver (or similar) Homepage: https://github.com/Mange/rtl8192eu-linux-driver↗ Backend: GitHub (since it’s hosted on GitHub) Version URL: https://api.github.com/repos/Mange/rtl8192eu-linux-driver/releases↗
- After adding the project, create Alpine Linux Mapping to link the upstream project to your Alpine package Distribution: Alpine Linux Package name: rtl8192eu-dkms
- Set up how Anitya should detect new versions. GitHub Backend: Monitors GitHub releases/tags Version pattern: Configure if needed (e.g., if versions follow specific patterns) Filter: Set up filters to ignore pre-releases if desired
- After Setup Once the mapping is made, it should automatically send an email to the maintainer whenever a new version is available.
© Prabu Anand K 2020-2026