Building Software from Source (User Build / Root Install Model)

Overview

A reliable and widely used pattern when building software from source on Unix-like systems is:

Build as a normal user → Install as root (only if required).

The core idea is simple:

This avoids permission problems, toolchain path issues, and accidental pollution of your system with root-owned build artifacts.


Why This Pattern Exists

Build tools (compilers, package managers, language toolchains) assume a normal user environment. Running builds as root can introduce several issues:

Common problems when building as root:

Because of this, the long-standing Unix convention is:

Root installs software. Users build software.


The Traditional Source Build Workflow

Most classic Unix build systems follow this pattern:

./configure
make
sudo make install

Expected responsibilities:

StepPurpose
configureDetect system features and generate build configuration
makeCompile everything
make installCopy finished artifacts into system locations

If the build system follows this convention properly, make install should not compile anything.


When Install Targets Rebuild Things

Modern projects sometimes break this convention and trigger builds during make install. For example:

When this happens, running sudo make install can fail because root does not have the same environment or toolchain paths.

In these cases, a better pattern is needed.


The Staged Install Pattern (DESTDIR)

The staged install approach avoids running install steps as root.

Instead of installing directly to /usr, /etc, etc., files are installed into a temporary directory first.

Example:

make install DESTDIR="$PWD/pkgroot"

This produces a directory tree like:

pkgroot/
  usr/local/bin/nginx
  usr/local/nginx/modules/...
  etc/nginx/...

At this stage:

Once staging is complete, the files are copied into the real filesystem:

sudo rsync -a pkgroot/ /

Optional safety check:

sudo rsync -a --dry-run pkgroot/ /

This shows what will be installed before actually copying anything.


Why rsync is Preferred Over cp

While cp -a pkgroot/ / technically works, it is generally avoided because it is less transparent and harder to audit.

rsync provides:

Example:

sudo rsync -a --info=NAME pkgroot/ /

Practical Build Workflow

A typical safe build process looks like this:

./configure
make -j$(nproc)
make install DESTDIR="$PWD/pkgroot"

sudo rsync -a --dry-run pkgroot/ /
sudo rsync -a pkgroot/ /

This ensures:


When You Can Still Use sudo make install

If make install only copies files and does not trigger any builds, the traditional approach remains perfectly fine:

./configure
make
sudo make install

Many mature projects (especially older Unix software) still follow this clean separation.


Mental Model

Think of the process like this:

developer user → builds software
root           → places software into system directories

Root acts only as the authority that can write into protected locations such as:

All compilation and toolchains belong in user space.


Summary

Best practice when building software from source:

  1. Compile as a normal user
  2. Avoid running toolchains as root
  3. Use staged installs (DESTDIR) if install steps rebuild things
  4. Use root only for final file placement

This pattern prevents permission issues, toolchain path problems, and unstable build environments.

Following this model leads to cleaner builds and fewer surprises when rebuilding or upgrading software later.