pbuilder is the core of a software stack for building Debian packages. This page lists some ways to use the stack that might be useful in niche or advanced cases. For normal usage, see pbuilder.
Contents
Include specific Debian sources
Sometimes you need to include packages from a Debian source that isn't available by default. For more information about common sources, see SourcesList and chapter 6 of the Debian FAQ.
Here are some lines you can paste into your /etc/pbuilderrc. A few of them assume you have passed a $DIST environment variable to your builder. See Support git-pbuilder syntax in pbuilder/cowbuilder for other uses of that variable.
# Add all Debian components to the normal repository:
DEBOOTSTRAPOPTS=("--components" "main,contrib,non-free-firmware" "${DEBOOTSTRAPOPTS[@]}")
# Add experimental (packages not ready for unstable):
[ -z "$DIST" -o "$DIST" = sid ] && \
OTHERMIRROR="$OTHERMIRROR|deb [signed-by=/usr/share/keyrings/debian-archive-keyring.gpg] http://deb.debian.org/debian experimental main"
# Add incoming.debian.org (packages in the process of being added to unstable):
[ -z "$DIST" -o "$DIST" = sid ] && \
OTHERMIRROR="$OTHERMIRROR|deb [signed-by=/usr/share/keyrings/debian-archive-keyring.gpg] http://incoming.debian.org/debian-buildd buildd-unstable main"
# Add security.debian.org:
[ -n "$DIST" -a "$DIST" != sid ] && \
OTHERMIRROR="$OTHERMIRROR|deb http://security.debian.org/debian-security $DIST-security main contrib non-free-firmware"
# Add backports:
[ -n "$DIST" -a "$DIST" != sid ] && \
OTHERMIRROR="$OTHERMIRROR|deb http://deb.debian.org/debian $DIST-backports main contrib non-free-firmware"
# tell pbuilder that pbuilderrc succeeded:
true
You may need to modify the priority of repositories before packages will be installed from them. For example, older packages in stable are usually preferred over newer ones in stable-backports. See AptConfiguration for details, and update your build environment's /etc/apt/ directory to fix issues.
If you add a source that uses https:// instead of http://, remember to add --extrapackages ca-certificates.
OTHERMIRROR only has an effect when creating environments, and is ignored when building packages. This is different to sbuild's EXTRA_REPOSITORIES option, which only has an effect when building packages.
Include local packages when building
Sometimes you need to build one package, then have the next package depend on it. For example, you might create a library and a service that uses the library.
First, create a local package repository (e.g. with reprepro).
Then, put your (public) signing key in the base of that repository:
# Fingerprint is the long string on the line below the line ending `[SC]`:
gpg --list-secret-key
gpg --export-options export-minimal --export <fingerprint> \
> /path/to/your/repo/signing-key.pgp
Then put something like the following in your /etc/pbuilderrc:
# Make local repo available to all environments:
BINDMOUNTS="$BINDMOUNTS /path/to/your/repo"
OTHERMIRROR="$OTHERMIRROR | deb [signed-by=/path/to/your/repo/signing-key.pgp] file:///path/to/your/repo ${DIST:-sid} main"
You will need to add the mirror to any existing environments manually. For example, by updating the /etc/apt/sources.list file in the environment.
By default, pbuilder does not run apt-get update while building packages. You may want to rebuild your environments when you update your package repository, or run apt-get update before resolving dependencies.
if you would prefer to use a signing key in /usr/share/keyrings, pass --debootstrapopts --keyring=/usr/share/keyrings/<name>.pgp to your pbuilder create command.
Support git-pbuilder syntax in pbuilder/cowbuilder
pbuilder and cowbuilder are usually called like this:
sudo cowbuilder create --distribution <dist> --architecture <arch>
sudo cowbuilder update --basepath=/var/cache/pbuilder/base-<dist>-<arch>.cow
git-pbuilder uses environment variables in both cases:
DIST=<dist> ARCH=<arch> git-pbuilder create
DIST=<dist> ARCH=<arch> git-pbuilder update
You can make pbuilder and cowbuilder accept environment variables by adding this to your /etc/pbuilderrc:
# Support $DIST and $ARCH environment variables:
if [ -n "$DIST$ARCH" ]
then
BASE="/var/cache/pbuilder/base"
[ -n "$DIST" ] && BASE="$BASE-$DIST" DISTRIBUTION="$DIST"
[ -n "$ARCH" ] && BASE="$BASE-$ARCH" DEBOOTSTRAPOPTS=("--arch" "$ARCH" "${DEBOOTSTRAPOPTS[@]}")
BASETGZ="$BASE.tgz"
BASEPATH="$BASE.cow"
fi
Note: environment variables must come after sudo:
✅ sudo DIST=<dist> ARCH=<arch> cowbuilder create
❌ DIST=<dist> ARCH=<arch> sudo cowbuilder create
Get distribution from changelog
Packages should include a changelog file, which should in turn specify one or more distributions (e.g. trixie forky).
If your changelog only mentions one distribution, you can extract it from the changelog and pass it to your preferred command:
sudo cowbuilder build <package>.dsc --dist "$(dpkg-parsechangelog --file /path/to/changelog --show-field=Distribution)"
Developers often set the distribution to "UNRELEASED" in their changelog until it's time to release, because doing so avoids accidental uploads. Some commands (e.g. piuparts) don't recognise that convention, so you may need to handle "UNRELEASED" as a special case.
Use a temporary base environment
Sometimes, you need to spend a lot of time with a single package. For example, you might be trying to fix a packaging error or debug a problem that only occurs in oldoldstable on i386. It would be useful to have an environment that already has your project's dependencies installed, so you can iterate over the problem more quickly.
Here's one way to create such an environment:
# Get build-dependencies of your project:
sed -n -e 's/[ \t()]//g' -e 's/,/ /g' -e 's/^Build-Depends://p' \
/path/to/your/project/debian/control
# Clone a base environment into a "-tmp" environment:
sudo cp -al /var/cache/pbuilder/base-oldoldstable-i386.cow \
/var/cache/pbuilder/base-oldoldstable-tmp-i386.cow
# Install extra packages in the "-tmp" environment:
sudo cowbuilder login --save-after-login \
--basepath /var/cache/pbuilder/base-oldoldstable-tmp-i386.cow
apt-get update && apt-get upgrade && apt-get -yy install <dependencies>
You can then use your -tmp environment like any other environment:
sudo cowbuilder build \
--basepath /var/cache/pbuilder/base-oldoldstable-tmp-i386.cow \
...
You can also start a shell in your environment:
sudo cowbuilder login \
--basepath /var/cache/pbuilder/base-oldoldstable-tmp-i386.cow \
--bindmounts "/path/to/your/project"
cd /path/to/your/project
make test
Delete the environment when you're done:
# Check for leftover mount points:
mount | grep /var/cache/pbuilder
sudo rm -rf --one-file-system /var/cache/pbuilder/base-oldoldstable-tmp-i386.cow
Create environments in a tmpfs
Creating a build environment is usually I/O-bound even if you use eatmydata. Modern systems usually have enough memory to create environments in a memory-only tmpfs, which doesn't suffer disk lag at all.
Adapt this to suit your requirements:
# Create a tmpfs:
mkdir -p /tmp/cowbuilder
sudo mount -t tmpfs tmpfs /tmp/cowbuilder
# Build your environment with cowbuilder and copy it across:
sudo cowbuilder create --basepath /tmp/cowbuilder/base.cow
sudo mv /tmp/cowbuilder/base.cow /var/cache/pbuilder/
# Or do both steps together with pbuilder:
sudo pbuilder create --buildplace /tmp/cowbuilder
# Remove the tmpfs:
sudo umount /tmp/cowbuilder
sudo rmdir /tmp/cowbuilder
/tmp itself has been a tmpfs since trixie, but it's mounted with the nodev option. The example above mounts an inner tmpfs without that option so cowbuilder can populate /tmp/pbuilder/base.cow/dev.
The instructions above will fail to hardlink packages back to /var/cache/pbuilder/aptcache/, because linking from the tmpfs to another filesystem would be an invalid cross-device link. Consider mirroring repositories locally with apt-cacher-ng or setting APTCACHEHARDLINK=no in your /etc/pbuilderrc.
Consider creating packages in a tmpfs
Depending on your use case, it may also be worth looking into building packages in a tmpfs. Here are some problems you will need to overcome:
putting just /var/cache/pbuilder/build/ in a tmpfs breaks cowbuilder, which needs to hard-link between environments
putting the whole of /var/cache/pbuilder/ in a tmpfs can use up a lot of memory
putting the base images in the lower layer of an overlayfs with a tmpfs for the upper layer still breaks cowbuilder, because you can't hard-link between layers of an overlayfs
Creating an environment in a tmpfs is a good idea for most people. Building a package in a tmpfs might be worth the effort if your workflow involves building small packages in a handful of environments on a system with a lot of memory and a slow hard disk. But it might be more trouble than it's worth if you're cross-compiling a massive framework on a system with low memory and a good SSD.
Run commands after the build
Debian has several checker tools to ensure your package is correct. Running these automatically can avoid many common issues.
This section recommends creating your own wrapper script(s) around pbuilder, but git-buildpackage users might prefer to use the --git-postbuild argument or the postbuild option in ~/.gbp.conf.
pbuilder can run commands inside the chroot using its hookdir. But for reasons discussed below, this is not recommended for any of these tools.
Run piuparts to test build results
piuparts checks whether your package can be installed and uninstalled correctly. It's run automatically for every package uploaded to Debian, and bugs are logged at piuparts.debian.org, so running it yourself might save you some embarrassment some day.
Adapt the following script to suit your requirements:
DIST=testing
ARCH=armhf
BASETGZ=/var/cache/pbuilder/base-"$DIST"-"$ARCH".tgz
# Build the package:
sudo pbuilder build <package>.dsc --basetgz "$BASETGZ"
# Get the .deb filename:
DEB_FILENAME=
for DEB in /var/cache/pbuilder/result/*.deb
do
case "$DEB" in
*-dbgsym_*.deb)
;;
*)
if [ -z "$DEB_FILENAME" -o "$DEB" -nt "$DEB_FILENAME" ]
then DEB_FILENAME="$DEB"
fi
;;
esac
done
# Run the check:
sudo piuparts --basetgz "$BASETGZ" "$DEB_FILENAME"
piuparts only works with .tgz files, not .cow directories. If you use it with cowbuilder, you'll need to maintain .cow directories and .tgz archives in parallel.
this can't go in pbuilder's hookdir because piuparts needs to create its own chroot.
Run lintian to detect common issues
lintian checks for common mistakes and policy violations.
Adapt the following script to suit your requirements:
DIST=testing
ARCH=armhf
BASETGZ=/var/cache/pbuilder/base-"$DIST"-"$ARCH".tgz
# Build the package:
sudo pbuilder build <package>.dsc --basetgz "$BASETGZ"
# Get the .changes filename:
CHANGES_FILENAME=
for CHANGES in /var/cache/pbuilder/result/*.changes
do
if [ -z "$CHANGES_FILENAME" -o "$CHANGES" -nt "$CHANGES_FILENAME" ]
then CHANGES_FILENAME="$CHANGES"
fi
done
# Run the check:
lintian --info --display-info --display-experimental --pedantic "$CHANGES_FILENAME"
For more information about lintian options, see the lintian man page.
running this in pbuilder's hookdir would involve installing lintian inside the build environment, which pulls in many other packages and could hide missing build-dependencies.
Run autopkgtest to run tests from the source package
autopkgtest runs tests defined in your debian/tests directory.
Adapt the following script to suit your requirements:
DIST=testing
ARCH=armhf
BASETGZ=/var/cache/pbuilder/base-"$DIST"-"$ARCH".tgz
# Build the package:
sudo pbuilder build <package>.dsc --basetgz "$BASETGZ"
# Get the .deb filename:
DEB_FILENAME=
for DEB in /var/cache/pbuilder/result/*.deb
do
case "$DEB" in
*-dbgsym_*.deb)
;;
*)
if [ -z "$DEB_FILENAME" -o "$DEB" -nt "$DEB_FILENAME" ]
then DEB_FILENAME="$DEB"
fi
;;
esac
done
# Get the .changes filename:
CHANGES_FILENAME=
for CHANGES in /var/cache/pbuilder/result/*.changes
do
if [ -z "$CHANGES_FILENAME" -o "$CHANGES" -nt "$CHANGES_FILENAME" ]
then CHANGES_FILENAME="$CHANGES"
fi
done
# Run the check:
autopkgtest "$DEB_FILENAME" "$CHANGES_FILENAME" -- \
autopkgtest-virt-unshare --release "$DIST" --arch "$ARCH"
this can't go in pbuilder's hookdir because autopkgtest needs to create a temporary test environment.
See also
The Ubuntu Pbuilder Howto has more tricks
devscripts has programs for many niche use cases
