Build an Arch AUR Package (PKGBUILD)
Learn to write an Arch Linux PKGBUILD from scratch, build it with makepkg, lint it with namcap, and publish it to the AUR step by step.
Before you start
- ▸Arch Linux installation with internet access
- ▸base-devel package group installed
- ▸AUR account at aur.archlinux.org with SSH public key configured
- ▸Basic familiarity with Bash scripting and a build system such as CMake or Make
The AUR (Arch User Repository) is one of Arch Linux's greatest strengths. Instead of waiting for a maintainer to package software, you write a PKGBUILD — a plain Bash script that tells makepkg how to fetch, build, and package any software into a standard .pkg.tar.zst archive. This guide covers writing a real PKGBUILD from scratch, testing it with namcap, and submitting the result to the AUR.
Prerequisites and Setup
You need a working Arch Linux installation with the base-devel group installed. This group includes makepkg, gcc, make, binutils, and the other tools the build process depends on.
sudo pacman -S --needed base-devel git namcap
For AUR submission, register an account at aur.archlinux.org and add your SSH public key in your account settings. You interact with the AUR via a dedicated Git remote, not a web upload form.
PKGBUILD Anatomy
A PKGBUILD is a Bash script sourced by makepkg. Every variable and function below is either required or strongly recommended.
Header variables
# Maintainer: Your Name <[email protected]>
pkgname='myprog'
pkgver='1.4.2'
pkgrel=1
epoch= # leave blank unless you need to force a downgrade
pkgdesc='A short, accurate one-line description (≤80 chars)'
arch=('x86_64' 'aarch64')
url='https://github.com/example/myprog'
license=('MIT')
depends=('glibc' 'zlib')
makedepends=('cmake')
optdepends=('python: for the helper scripts')
provides=() # if this replaces another package name
conflicts=() # packages that must not be installed alongside this one
source=("${pkgname}-${pkgver}.tar.gz::https://github.com/example/myprog/archive/v${pkgver}.tar.gz")
b2sums=('SKIP') # replace SKIP with a real checksum before publishing
- pkgrel: Reset to
1whenpkgverchanges; increment it for packaging-only fixes at the same upstream version. - arch: Use
('any')for architecture-independent packages (pure Python, shell scripts, fonts). - depends vs makedepends:
dependsare needed at runtime;makedependsare only needed to build and can be absent on the end-user's machine. - source: Each entry is a URL, optionally prefixed with
localname::to rename the downloaded file. Git sources use the form"git+https://...".
Checksum generation
Never leave SKIP in a published PKGBUILD. Generate real checksums after your source URLs are final:
makepkg -g
Copy the printed b2sums array into your PKGBUILD. b2sums (BLAKE2) is the current preference; sha256sums is also acceptable.
Build and package functions
prepare() {
cd "${srcdir}/${pkgname}-${pkgver}"
# Apply patches, run autoreconf, etc.
patch -p1 < "${srcdir}/fix-build.patch"
}
build() {
cd "${srcdir}/${pkgname}-${pkgver}"
cmake -B build \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr
cmake --build build
}
check() {
cd "${srcdir}/${pkgname}-${pkgver}"
cmake --build build --target test
}
package() {
cd "${srcdir}/${pkgname}-${pkgver}"
DESTDIR="${pkgdir}" cmake --install build
install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
}
Key rules:
prepare()andcheck()are optional but recommended when applicable.- Always install into
${pkgdir}, never into the live filesystem. - Use
install -Dm755for executables and-Dm644for data/config files; this sets owner, permissions, and creates parent directories in one command. - License files must land in
/usr/share/licenses/${pkgname}/. - Never hardcode paths. Use the
$srcdirand$pkgdirvariablesmakepkgprovides.
VCS Packages (Git Sources)
For packages tracking a Git repository's HEAD, the convention is to append -git to pkgname and implement a pkgver() function that generates a version string dynamically:
pkgname='myprog-git'
pkgver='r142.abc1234' # placeholder; pkgver() overwrites this at build time
source=("git+https://github.com/example/myprog.git")
b2sums=('SKIP') # SKIP is acceptable for git+https sources
pkgver() {
cd "${srcdir}/myprog"
printf 'r%s.%s' "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
}
makepkg calls pkgver() after cloning/fetching and updates the variable before packaging. The SKIP checksum is allowed for VCS sources because the commit hash provides integrity.
Building and Testing with makepkg
First build
cd ~/builds/myprog
makepkg -si
-s installs missing dependencies automatically via pacman; -i installs the built package when done. Output will vary; watch for errors in the build or check phases.
Iterating quickly
# Skip re-extracting sources after first run
makepkg -si --noextract
# Clean old build artifacts and rebuild from scratch
makepkg -si --clean
Inspect the package contents
pacman -Qlp myprog-1.4.2-1-x86_64.pkg.tar.zst
Verify binaries land in /usr/bin, libraries in /usr/lib, and that no files end up in /usr/local or other non-standard paths for packaged software.
Linting with namcap
namcap analyses both the PKGBUILD and the built package for common mistakes: missing dependencies, wrong permissions, hardcoded paths, and packaging policy violations.
# Lint the PKGBUILD itself
namcap PKGBUILD
# Lint the compiled package archive
namcap myprog-1.4.2-1-x86_64.pkg.tar.zst
Lines prefixed with E: are errors that must be fixed. Lines prefixed with W: are warnings worth reviewing. A clean package shows no output from either command. Fix every error before submitting to the AUR — reviewers and AUR helpers will run namcap themselves.
Submitting to the AUR
Create the remote repository
The AUR creates a new package repository the first time you push to it. The remote URL format is fixed:
git init
git remote add aur ssh://[email protected]/myprog.git
Generate .SRCINFO
The AUR web interface and AUR helpers parse .SRCINFO, a static metadata file derived from your PKGBUILD. Regenerate it every time you change the PKGBUILD:
makepkg --printsrcinfo > .SRCINFO
Commit and push
git add PKGBUILD .SRCINFO
git commit -m 'Initial release 1.4.2-1'
git push aur HEAD:master
The AUR server validates the push. If .SRCINFO is missing or malformed, the push is rejected with a descriptive error. After a successful push, your package appears at https://aur.archlinux.org/packages/myprog within seconds.
Updating an existing package
Clone your AUR repo first if you are on a new machine, then edit and push:
git clone ssh://[email protected]/myprog.git
cd myprog
# Edit PKGBUILD: bump pkgver, reset pkgrel to 1, update checksums
makepkg -g # regenerate checksums
makepkg --printsrcinfo > .SRCINFO
git add PKGBUILD .SRCINFO
git commit -m 'Upstream 1.4.3'
git push
Troubleshooting
- makepkg fails checksum verification: The upstream tarball changed. Re-run
makepkg -gto update checksums, but verify the upstream release is legitimate before publishing the new hash. - namcap reports "dependency not found": The library is provided by a package with a different name. Use
pkgfile /usr/lib/libfoo.so(requirespkgfileand a current database) to find the correct package name. - SSH push rejected "permission denied": Confirm your public key is saved in AUR account settings and that your SSH config uses the correct key. Test with
ssh [email protected] help— a successful connection prints your AUR username. - Files installed to /usr/local inside pkgdir: The upstream build system defaults to
/usr/local. Pass-DCMAKE_INSTALL_PREFIX=/usr,--prefix=/usrto configure, or setPREFIX=/usrfor Makefile-based projects. - check() fails in a clean chroot but not locally: Use
makepkg --checkinside a clean chroot viaextra-x86_64-build(from thedevtoolspackage) to catch missing test dependencies or environment assumptions.
Frequently asked questions
- Can I build AUR packages as root?
- No. makepkg explicitly refuses to run as root for security reasons. Always build as a regular user; makepkg will call sudo or ask for a password only when installing dependencies or the finished package.
- What is the difference between depends and makedepends?
- depends lists packages required at runtime on the end-user's system. makedepends lists packages only needed during the build, such as cmake or meson; they can be absent once the package is installed.
- How do I handle a VCS package like a -git package?
- Append -git to pkgname, use a git+ source URL, and implement a pkgver() function that generates a version string from git rev-list and rev-parse. makepkg calls pkgver() automatically and updates the variable before packaging.
- Do I need to use a clean chroot for AUR submissions?
- It is strongly recommended. Build in a clean chroot using the devtools package (extra-x86_64-build) to confirm all dependencies are correctly declared and the build does not rely on packages installed only on your own machine.
- How do I transfer maintainership or orphan a package?
- Log in to aur.archlinux.org, navigate to your package page, and use the Manage Co-Maintainers or Disown Package options in the package actions sidebar. Orphaned packages are available for adoption by other AUR users.
Related guides
AI and Artificial-Life Tools on Linux
Set up open-source AI/ML and artificial-life toolkits on Linux: PyTorch, JAX, DEAP, Avida, NetLogo, and RL environments with GPU driver guidance.
Assembly Language on Linux: A Starter Guide
Write x86-64 assembly on Linux from scratch: install NASM and GAS, learn syscalls, assemble and link a working program, then inspect and debug it.
How to Benchmark Disk Performance with fio
Learn to benchmark Linux disk performance with fio: writing job files, testing latency and throughput, and interpreting IOPS and percentile output correctly.
The Linux Boot Process Explained
Trace the full Linux boot sequence from UEFI firmware through GRUB2, the kernel, initramfs, and systemd to your login prompt — with diagnostics at each stage.