Building Packages from Source in Alpine Linux: A Complete Developer Guide
I’ll show you how to build packages from source in Alpine Linux. After working with custom software for years, I’ve found Alpine’s package building system is actually pretty straightforward once you get the hang of it.
Introduction
Sometimes you need software that isn’t in the official repositories. Maybe it’s a newer version, custom patches, or something totally unique to your project. That’s where building from source comes in handy.
Alpine uses APKBUILD files to define how packages get built. It’s like a recipe that tells the system where to get source code, how to compile it, and what files to include in the final package.
Why You Need This
- Install software not available in repositories
- Apply custom patches or configurations
- Create packages for internal distribution
- Contribute packages to Alpine Linux community
Prerequisites
You’ll need these things first:
- Alpine Linux system with development tools
- Basic knowledge of shell scripting
- Understanding of compilation process
- Internet connection for downloading sources
Step 1: Set Up Build Environment
Install Development Tools
Let’s start by installing the tools needed for building packages.
What we’re doing: Setting up a complete Alpine package development environment.
# Install build dependencies
apk add alpine-sdk
# Add your user to abuild group
addgroup $USER abuild
# Create abuild directories
mkdir -p ~/packages/mypackage
cd ~/packages/mypackage
Code explanation:
alpine-sdk
: Meta-package containing all build tools (gcc, make, abuild, etc.)addgroup $USER abuild
: Allows your user to build packagesmkdir -p ~/packages/mypackage
: Creates directory structure for package development
Expected Output:
(1/25) Installing binutils (2.40-r7)
(2/25) Installing gcc (12.2.1_git20220924-r10)
...
OK: 234 MiB in 58 packages
Configure Package Signing
What we’re doing: Setting up package signing for security.
# Generate signing key
abuild-keygen -a -i
# Create abuild configuration
echo "PACKAGER_PRIVKEY=\"$HOME/.abuild/your_email-private_key.rsa\"" > ~/.abuild/abuild.conf
Code explanation:
abuild-keygen -a -i
: Creates RSA key pair for package signing-a
: Appends public key to /etc/apk/keys automatically-i
: Installs the key in the system keyring
Tip: Keep your private key safe! If you lose it, you’ll need to regenerate it and re-sign all your packages.
Step 2: Create Your First APKBUILD
Basic APKBUILD Structure
Let’s create a simple APKBUILD file for a common tool.
What we’re doing: Creating an APKBUILD file to build a package from source.
# Create APKBUILD file
vi APKBUILD
Basic APKBUILD template:
# Contributor: Your Name <[email protected]>
# Maintainer: Your Name <[email protected]>
pkgname=hello-world
pkgver=1.0.0
pkgrel=0
pkgdesc="Simple hello world program"
url="https://github.com/example/hello-world"
arch="all"
license="MIT"
makedepends="gcc libc-dev"
source="$pkgname-$pkgver.tar.gz::$url/archive/v$pkgver.tar.gz"
builddir="$srcdir/$pkgname-$pkgver"
build() {
cd "$builddir"
make
}
package() {
cd "$builddir"
make DESTDIR="$pkgdir" install
}
sha512sums="
abcd1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef hello-world-1.0.0.tar.gz
"
APKBUILD explanation:
pkgname
: Package name (must match directory name)pkgver
: Version number of the softwarepkgrel
: Package release number (increment for Alpine-specific changes)makedepends
: Build-time dependenciessource
: Where to download source codebuild()
: Function that compiles the softwarepackage()
: Function that installs files into package
Generate Checksums
What we’re doing: Creating security checksums for source files.
# Download source and generate checksums
abuild checksum
# Verify the checksums
abuild verify
Code explanation:
abuild checksum
: Downloads source files and generates SHA-512 checksumsabuild verify
: Verifies downloaded files match expected checksums
Step 3: Build the Package
Test Build Process
Now let’s actually build our package.
What we’re doing: Compiling source code and creating an Alpine package.
# Clean any previous builds
abuild clean
# Fetch source code
abuild fetch
# Verify source integrity
abuild verify
# Unpack and prepare source
abuild unpack
# Build the package
abuild -r
Code explanation:
abuild clean
: Removes build artifacts from previous runsabuild fetch
: Downloads source files specified in APKBUILDabuild verify
: Checks file integrity using checksumsabuild unpack
: Extracts source archivesabuild -r
: Builds package with dependency resolution
Expected Output:
>>> hello-world: Building community/hello-world 1.0.0-r0 (using abuild 3.11.0-r0)
>>> hello-world: Checking sanity of /home/user/packages/mypackage/APKBUILD...
>>> hello-world: Analyzing dependencies...
>>> hello-world: Installing for build: build-base gcc libc-dev
>>> hello-world: Cleaning up srcdir
>>> hello-world: Cleaning up pkgdir
>>> hello-world: Fetching hello-world-1.0.0.tar.gz::https://github.com/example/hello-world/archive/v1.0.0.tar.gz
>>> hello-world: Checking sha512sums...
hello-world-1.0.0.tar.gz: OK
>>> hello-world: Unpacking /var/cache/distfiles/hello-world-1.0.0.tar.gz...
>>> hello-world: hello-world-1.0.0*
>>> hello-world: Entering fakeroot...
>>> hello-world: Running package()...
>>> hello-world: Tracing dependencies...
>>> hello-world: Package size: 4.0 KB
>>> hello-world: Compressing data...
>>> hello-world: Create checksum...
>>> hello-world: Create hello-world-1.0.0-r0.apk
Install Your Package
What we’re doing: Installing the freshly built package.
# Install the built package
sudo apk add ~/packages/mypackage/hello-world-1.0.0-r0.apk
# Test the installed package
hello-world
Warning: Always test your packages thoroughly before distributing them. A broken package can cause system issues.
Step 4: Advanced APKBUILD Features
Multiple Subpackages
What we’re doing: Creating packages with separate components (binaries, documentation, development files).
# Advanced APKBUILD with subpackages
vi APKBUILD
Advanced APKBUILD example:
# Contributor: Your Name <[email protected]>
# Maintainer: Your Name <[email protected]>
pkgname=advanced-tool
pkgver=2.1.0
pkgrel=0
pkgdesc="Advanced development tool with multiple components"
url="https://github.com/example/advanced-tool"
arch="all"
license="GPL-3.0-or-later"
makedepends="
cmake
samurai
libc-dev
libssl-dev
"
subpackages="
$pkgname-dev
$pkgname-doc
$pkgname-bash-completion:bashcomp:noarch
"
source="$pkgname-$pkgver.tar.gz::$url/archive/v$pkgver.tar.gz
fix-compilation.patch
"
builddir="$srcdir/$pkgname-$pkgver"
prepare() {
default_prepare
# Apply custom patches
cd "$builddir"
patch -p1 < "$srcdir/fix-compilation.patch"
}
build() {
cd "$builddir"
cmake -B build \
-DCMAKE_INSTALL_PREFIX=/usr \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=True
cmake --build build
}
check() {
cd "$builddir"
cmake --build build --target test
}
package() {
cd "$builddir"
DESTDIR="$pkgdir" cmake --install build
}
dev() {
default_dev
amove usr/include
amove usr/lib/pkgconfig
}
doc() {
default_doc
amove usr/share/man
}
bashcomp() {
pkgdesc="Bash completion for $pkgname"
install_if="$pkgname=$pkgver-r$pkgrel bash-completion"
amove usr/share/bash-completion
}
sha512sums="
1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef advanced-tool-2.1.0.tar.gz
abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890 fix-compilation.patch
"
Advanced features explanation:
subpackages
: Creates separate packages for development files, documentation, etc.prepare()
: Function for patching source code before buildcheck()
: Function for running test suiteamove
: Alpine function to move files between subpackagesinstall_if
: Automatically installs subpackage when conditions are met
Custom Build Functions
What we’re doing: Creating specialized build functions for complex software.
# Example for Python package
build() {
cd "$builddir"
python3 setup.py build
}
package() {
cd "$builddir"
python3 setup.py install --prefix=/usr --root="$pkgdir"
}
# Example for Go package
build() {
cd "$builddir"
export GOPATH="$srcdir/go"
go build -v -o "$pkgname"
}
package() {
cd "$builddir"
install -Dm755 "$pkgname" "$pkgdir/usr/bin/$pkgname"
}
Practical Examples
Example 1: Building a Simple C Program
What we’re doing: Creating a package for a basic C application.
# Create project structure
mkdir -p ~/packages/simple-calc
cd ~/packages/simple-calc
# Create APKBUILD
cat > APKBUILD << 'EOF'
# Contributor: Your Name <[email protected]>
# Maintainer: Your Name <[email protected]>
pkgname=simple-calc
pkgver=1.0.0
pkgrel=0
pkgdesc="Simple command-line calculator"
url="https://github.com/example/simple-calc"
arch="all"
license="MIT"
makedepends="gcc libc-dev"
source="$pkgname-$pkgver.tar.gz::$url/archive/v$pkgver.tar.gz"
builddir="$srcdir/$pkgname-$pkgver"
build() {
cd "$builddir"
gcc -o simple-calc main.c -lm
}
package() {
cd "$builddir"
install -Dm755 simple-calc "$pkgdir/usr/bin/simple-calc"
}
sha512sums="SKIP"
EOF
# Generate checksums and build
abuild checksum
abuild -r
Code explanation:
gcc -o simple-calc main.c -lm
: Compiles C source with math libraryinstall -Dm755
: Installs binary with proper permissionssha512sums="SKIP"
: Temporary setting for development (don’t use in production!)
Example 2: Building with Configuration Options
What we’re doing: Building software with custom compile-time options.
# APKBUILD with configure script
build() {
cd "$builddir"
./configure \
--prefix=/usr \
--sysconfdir=/etc \
--mandir=/usr/share/man \
--localstatedir=/var \
--enable-ssl \
--disable-debug \
--with-system-libraries
make
}
package() {
cd "$builddir"
make DESTDIR="$pkgdir" install
# Remove unnecessary files
rm -rf "$pkgdir/usr/share/doc"
}
Troubleshooting
Build Failures
Problem: Package fails to build with compilation errors Solution: Check dependencies and build configuration
# Check build log for errors
abuild -r 2>&1 | tee build.log
# Verify all dependencies are installed
abuild deps
# Test build in clean environment
abuild clean && abuild -r
Dependency Issues
Problem: Missing dependencies during build Solution: Add required packages to makedepends
# Find what package provides a missing file
apk search cmd:gcc
apk search so:libssl.so.1.1
# Add to APKBUILD makedepends
makedepends="gcc libc-dev openssl-dev"
Package Size Issues
Problem: Package is larger than expected Solution: Optimize package contents
# Check what's in your package
tar -tzf simple-calc-1.0.0-r0.apk
# Strip debug symbols
strip "$pkgdir/usr/bin/simple-calc"
# Remove unnecessary files
rm -rf "$pkgdir/usr/share/doc"
Best Practices
-
Version Management: Use semantic versioning
# Good versioning pkgver=1.2.3 pkgrel=0 # Reset to 0 for new upstream version # Increment pkgrel for Alpine-specific changes pkgrel=1 # First Alpine-specific change
-
Security Practices:
- Always verify checksums
- Keep dependencies minimal
- Use official sources when possible
- Test packages thoroughly
-
Code Quality:
- Follow Alpine packaging guidelines
- Use proper file permissions
- Include appropriate metadata
- Add helpful package descriptions
Verification
To verify your package is working correctly:
# Check package contents
apk info -L simple-calc
# Verify package metadata
apk info simple-calc
# Test package functionality
simple-calc --help
Wrapping Up
You just learned how to build packages from source in Alpine Linux:
- Set up a complete development environment
- Created APKBUILD files with proper structure
- Built and installed custom packages
- Handled advanced features like subpackages
- Troubleshot common build issues
Building your own packages gives you complete control over your software stack. I use this process for all my custom tools and it’s saved me countless hours of manual compilation. The Alpine package system is really well designed once you understand the basics.