Sooner or later, apt install lets you down. The version in the repos is ancient. The package doesn’t exist at all. Or you need a build flag Ubuntu’s maintainers never bothered to enable. Run each command only after the previous command succeeds. This guide covers both toolchains you’ll hit when that happens: the classic ./configure, make, then sudo make install combo, and the newer CMake workflow.
You’ll learn what each command actually does. You’ll learn how to fix the dependency errors that trip up almost everyone on their first build. And you’ll learn how to clean up afterward, so this doesn’t turn into an orphaned folder mystery six months from now. Nobody enjoys finding a program in /usr/local/bin with zero memory of installing it.
What Does “Building from Source” Actually Mean?
When you run sudo apt install nginx, Ubuntu hands you a pre-compiled binary. Someone else already ran the compiler and packaged the result. Building from source means something different. You download the human-readable source code, usually C or C++, and compile it yourself, on your own machine, with your own compiler.
Why bother? A few reasons come up constantly in homelab and IT work:
- Version lag: Ubuntu 24.04’s repos freeze package versions at release. They only ship security patches after that. If a project adds a feature or fixes a bug six months after launch, apt won’t have it.
- Missing packages: Niche or brand-new open-source tools often skip Ubuntu packaging entirely, especially in their first year.
- Custom build options: Distro maintainers pick one set of compile-time flags for everyone. Building it yourself lets you enable optional features. You can also add debug symbols or hardware-specific optimizations they skipped.
- Contributing or debugging: Patching a project or chasing a bug means you need a local build anyway.
Here’s the trade-off, and it’s a real one: source builds aren’t tracked by apt. They get no automatic security updates. And they can fail in ways a polished .deb package never would. Use apt first. Reach for source builds only when you’ve got a specific reason.
Prerequisites
Before you start, make sure you have:
- Ubuntu 24.04 LTS (or 26.04 LTS) installed, either on bare metal, a VM, or via WSL2 on Windows
- A user account with
sudoprivileges - A terminal application (GNOME Terminal ships by default; any terminal emulator works)
- An active internet connection to download source code and dependency packages
- At least a few hundred MB of free disk space (some projects need several GB during compilation)
- Basic comfort navigating directories with
cdandls
Note for Windows users: If you’re on Windows, run these commands inside WSL2’s Ubuntu environment, not PowerShell or CMD. The Autotools and CMake workflows here are Linux-native. macOS works on similar concepts, but you’ll use Xcode Command Line Tools and Homebrew instead of
aptandbuild-essential. Package names will differ.
| Requirement | Details |
|---|---|
| OS | Ubuntu 24.04 LTS or later |
| Disk space | 500 MB–5 GB depending on project size |
| Privileges | sudo access required for system-wide installs |
| Compiler | Installed via build-essential (Step 1) |
Step-by-Step Guide
Step 1: Update Your Package Lists
Before installing anything, refresh Ubuntu’s package index. You want current metadata, not a stale cache.
sudo apt update
Expected output ends with something like:
Reading package lists… Done
Building dependency tree… Done
Reading state information… Done
All packages are up to date.
Step 2: Install build-essential
build-essential is a meta-package. It doesn’t do anything itself. But it pulls in the tools every source build needs: gcc for C, g++ for C++, make, and the standard C library headers.
sudo apt install build-essential
Expected output (abbreviated):
The following NEW packages will be installed:
build-essential cpp gcc g++ libc6-dev make …
0 upgraded, 12 newly installed, 0 to remove and 0 not upgraded.
Need to get 45.2 MB of archives.
…
Setting up build-essential (12.10ubuntu1) …
Verify it worked by checking the compiler version:
gcc --version
make --version
gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0
GNU Make 4.3
Ubuntu 24.04’s default compiler is GCC 13, but the exact patch-level string (like 13.3.0-6ubuntu2~24.04) can differ slightly depending on your point release and when you installed, since it’s tied to whatever build was current in the archive at that time. Don’t worry if your output doesn’t match this example character-for-character. What matters is that it shows a GCC 13.x version.
Want to check what’s in this package first, like version number, dependencies, and description? Ubuntu’s public package pages work as a quick reference:
Step 3: Install Additional Prerequisites (autoconf, autotools-dev, pkg-config)
Many projects with a ./configure script also expect autoconf, automake, libtool, and pkg-config. None of these, including pkg-config, are pulled in by build-essential, so you’ll need to install them separately if your project needs them. You’ll especially need these if you ever have to regenerate the configure script. Install them now and save yourself a troubleshooting round trip later:
sudo apt install autoconf automake libtool pkg-config
These aren’t always strictly required. Some projects ship a pre-generated configure script that works fine without them. But having them installed avoids a common class of “missing tool” errors. That mostly happens on projects checked out from Git instead of downloaded as a release tarball.
Step 4: Install CMake (If the Project Uses It)
Not every project needs CMake. Check the project’s README or INSTALL file first. If you see a CMakeLists.txt file in the source tree instead of a configure script, you’ll need CMake:
sudo apt install cmake
Check the installed version:
cmake --version
cmake version 3.28.3
Why is Ubuntu’s cmake version older than cmake.org’s? Ubuntu 24.04 ships CMake 3.28.x, packaged and frozen at release time. Meanwhile, cmake.org may already be on 3.31.x or 4.x. Most projects specify a minimum CMake version in their
CMakeLists.txt(viacmake_minimum_required()). 3.28 satisfies the vast majority of them. If a project genuinely requires a newer release, don’t fight apt pinning. Install it via Snap (sudo snap install cmake --classic) or grab a binary release directly from cmake.org.
Step 5: Download the Project’s Source Code
There are two common ways to get source code: a release tarball or a Git clone.
Option A: Download a release tarball (recommended for stable, tested builds):
cd ~/Downloads
wget https://example.org/releases/example-tool-2.4.1.tar.gz
tar -xzf example-tool-2.4.1.tar.gz
cd example-tool-2.4.1
The -xzf flags: -x extracts, -z decompresses gzip, -f specifies the filename that follows.
Option B: Clone from Git (for the latest development code or a specific patch):
git clone https://github.com/example-org/example-tool.git
cd example-tool
Tip: Git checkouts often don’t include a pre-generated
configurescript. You’ll needautoreconfin Step 6a to build one. Release tarballs almost always include it already.
Step 6a: Run ./configure (Autotools Workflow)
From inside the extracted source directory, check whether a configure script exists:
ls
If you see configure, run it:
./configure
If instead you only see configure.ac and no configure file (common with Git clones), regenerate it first:
autoreconf -if
autoreconf -if walks the Autotools chain: aclocal, autoconf, automake, libtoolize. It rebuilds the configure script from source templates. The -i flag adds any missing auxiliary files (like install-sh). The -f flag forces regeneration even if the existing files look up to date. Run this whenever ./configure is missing entirely, or throws cryptic errors about macros it can’t find.
Once configure exists, run it:
./configure
./configure checks your system for the compilers, libraries, and headers the project needs. Then it writes a Makefile tailored to your exact machine. Expected output looks like a scrolling list of checks:
checking for gcc… gcc
checking whether the C compiler works… yes
checking for a BSD-compatible install… /usr/bin/install -c
checking for library containing strerror… none required
checking for zlib.h… yes
checking for openssl/ssl.h… yes
configure: creating ./config.status
config.status: creating Makefile
Want the software installed somewhere other than the default /usr/local? Add the --prefix flag:
./configure --prefix=$HOME/.local
This installs everything under your home directory instead of system-wide directories. You won’t need sudo for the install step later. Handy if you’re testing a build or don’t have root on a shared server.
Step 6b: Configure with CMake (CMake Workflow, Instead of Step 6a)
If the project uses CMake instead of Autotools, the process looks different. CMake commonly generates build files into a separate build directory, keeping your source tree clean, and this out-of-source approach is the widely recommended practice. That said, CMake can also support in-source builds if a project sets things up that way. Autotools traditionally builds directly inside the source folder by default, but modern Autotools projects can also support out-of-source build workflows, so the two tools aren’t as strictly divided on this point as it might seem.
From the project’s root directory (where CMakeLists.txt lives):
cmake -S . -B build
-S .tells CMake the source directory is the current folder-B buildtells CMake to create (or reuse) a folder namedbuildfor all generated files
Expected output:
— The C compiler identification is GNU 13.3.0
— The CXX compiler identification is GNU 13.3.0
— Detecting C compiler ABI info – done
— Found ZLIB: /usr/lib/x86_64-linux-gnu/libz.so
— Configuring done (0.8s)
— Generating done (0.2s)
— Build files have been written to: /home/youruser/example-tool/build
Want a custom install location (CMake’s equivalent of --prefix) or a release-optimized build? Pass variables with -D:
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=$HOME/.local -DCMAKE_BUILD_TYPE=Release
CMAKE_BUILD_TYPE=Release enables compiler optimizations. Whether debug symbols get stripped isn’t a fixed rule of the Release setting itself. It depends on your toolchain and any additional build flags in play. Use Debug instead if you’re troubleshooting a crash and need symbol information.
Step 7a: Compile with make (Autotools Workflow)
Back in the Autotools track, compile the source code:
make
This reads the Makefile that ./configure generated and invokes the compiler on every source file. Expect scrolling output like this:
gcc -DHAVE_CONFIG_H -I. -I./src -g -O2 -c main.c -o main.o
gcc -DHAVE_CONFIG_H -I. -I./src -g -O2 -c parser.c -o parser.o
gcc -o example-tool main.o parser.o -lz -lssl -lcrypto
Build times vary wildly. A small utility might take 10 seconds. A large project like a database engine or web server can eat 20+ minutes on modest hardware. Speed it up using multiple CPU cores:
make -j$(nproc)
-j$(nproc) tells make to run parallel compile jobs equal to your CPU’s core count. (nproc prints that number.) On a 4-core machine, this can cut build time by more than half.
Step 7b: Build with CMake (CMake Workflow)
Instead of running make directly, use CMake’s build wrapper. It calls whatever underlying build tool it generated for: make by default on Ubuntu, or ninja if installed.
cmake --build build
Add the same parallel-jobs flag:
cmake --build build -j$(nproc)
Expected output shows percentage progress markers:
[ 5%] Building CXX object CMakeFiles/example.dir/src/main.cpp.o
[ 45%] Building CXX object CMakeFiles/example.dir/src/parser.cpp.o
[ 90%] Linking CXX executable example-tool
[100%] Built target example-tool
Step 8a: Install with sudo make install (Autotools Workflow)
Once compilation finishes without errors, install the built program into system directories:
sudo make install
Expected output:
/usr/bin/install -c example-tool /usr/local/bin/example-tool
/usr/bin/install -c -m 644 example-tool.1 /usr/local/share/man/man1/
Is it safe to run
sudo make install? Generally yes, for software from a reputable, official source. But know what’s happening: you’re running the project’s install script as root. There’s no sandboxing. It copies files wherever the Makefile tells it to, usually under/usr/local. Always read a project’sINSTALLorREADMEfile first. Never runsudo make installon code you haven’t reviewed, or downloaded from an untrusted mirror. If you used--prefix=$HOME/.localin Step 6a, skipsudoentirely since you own that directory.
Step 8b: Install with cmake –install (CMake Workflow)
The CMake equivalent:
sudo cmake --install build
If you set a custom CMAKE_INSTALL_PREFIX pointing to your home directory, drop sudo:
cmake --install build
Expected output:
— Install configuration: “Release”
— Installing: /usr/local/bin/example-tool
— Installing: /usr/local/lib/libexample.so
Step 9: Verify the Installed Program Runs
Confirm the new binary is on your PATH and runs correctly:
which example-tool
example-tool --version
/usr/local/bin/example-tool
example-tool 2.4.1
If which returns nothing, the install directory likely isn’t in your shell’s PATH. Check the troubleshooting section below.
Configuration
Both workflows expose settings that control where and how the software gets built. Here are the ones you’ll use most often:
| Setting | Workflow | Purpose | Default |
|---|---|---|---|
--prefix=DIR | Autotools (./configure) | Sets install location | /usr/local |
CMAKE_INSTALL_PREFIX | CMake | Sets install location | /usr/local |
CMAKE_BUILD_TYPE | CMake | Release (optimized) or Debug (symbols) | unset |
CC / CXX (env vars) | Both | Override which compiler to use | system default gcc/g++ |
PKG_CONFIG_PATH (env var) | Both | Tells build system where to find .pc library metadata files | system default paths |
-j$(nproc) | Both (make/cmake --build) | Parallel compile jobs | 1 (sequential) |
Example of overriding the compiler for an unusual build:
CC=clang CXX=clang++ ./configure
Tips and Troubleshooting
“./configure: command not found” or “No such file or directory”
Why it happens: You’re not in the extracted source folder. Or the archive didn’t include a configure script. Or the file lost its executable permission during extraction.
Fix: Confirm you’re in the right directory and the file exists:
pwd
ls -la configure
If it’s there but not executable, fix permissions:
chmod +x configure
./configure
If the file is missing entirely (common on Git clones), regenerate it:
autoreconf -if
configure Fails with “Missing Required Library” or Header Errors
Why it happens: Ubuntu splits most libraries into two packages. A runtime package, like libssl3, and a separate development package, like libssl-dev, that holds the header files (.h) compilers need. You might already have the runtime library installed, but not the dev headers. This one trips up nearly everyone the first time.
Fix: Read the exact error message for the library name. Then search and install the matching -dev package:
apt search libssl-dev
sudo apt install libssl-dev
Common dev packages you’ll run into repeatedly:
sudo apt install zlib1g-dev libssl-dev libcurl4-openssl-dev libpcre3-dev libxml2-dev
Not sure of the exact package name? Search Ubuntu’s package index directly. Or use
apt-file search libname.so(installapt-filefirst withsudo apt install apt-file, thensudo apt-file update).
“make: command not found” or Immediate Compiler Errors
Why it happens: build-essential was never installed. So make, gcc, and g++ don’t exist on the system yet.
Fix: Go back and install it:
sudo apt update
sudo apt install build-essential
make install Fails with “Permission Denied”
Why it happens: make install (and cmake --install) write to system-owned directories like /usr/local/bin and /usr/local/lib. Regular user accounts can’t touch those.
Fix: Re-run the install step with sudo:
sudo make install
or for CMake:
sudo cmake --install build
Or sidestep sudo entirely by installing to a directory you own. Set that during the configure step (--prefix=$HOME/.local or -DCMAKE_INSTALL_PREFIX=$HOME/.local).
CMake Version Is Too Old for a Project’s Requirements
Why it happens: Ubuntu 24.04 ships CMake 3.28.x, frozen at release. A project might require CMake 3.30+ for a newer feature.
Fix: Check the required version in the project’s CMakeLists.txt (look for cmake_minimum_required(VERSION ...)). Then install a newer CMake via Snap:
sudo snap install cmake --classic
Or download a binary release directly from cmake.org/download and add it to your PATH ahead of the system version.
Compiled Program “Not Found” After Installing
Why it happens: You installed to a custom --prefix (like $HOME/.local/bin) that isn’t in your shell’s PATH.
Fix: Add the directory to your PATH in ~/.bashrc:
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
Verify:
which example-tool
How to Uninstall or Track a Source-Built Program
Why it’s tricky: Unlike apt-installed packages, source builds aren’t recorded in Ubuntu’s package database. There’s no sudo apt remove example-tool waiting for you later. This is the part that bites people six months down the line. By then, they’ve forgotten they even built the thing.
Fix options, in order of preference:
- Check for
make uninstallsupport: some projects’ Makefiles include this target:
sudo make uninstall
- Use
checkinstallinstead ofmake install: this wraps the install step into a real.debpackage that apt can track and later remove cleanly:
sudo apt install checkinstall
sudo checkinstall
checkinstall runs make install for you but records every file it creates. It then builds a .deb you can remove later with sudo dpkg -r example-tool.
- Manual removal: if neither option above is available, note the
--prefixorCMAKE_INSTALL_PREFIXyou used during install. Then manually delete the resulting files frombin/,lib/, andshare/under that prefix.
Warning: Never manually delete files under
/usr/localin bulk without checking they belong to the program you’re removing. Other source-installed software may share that directory.
What Is the Difference Between ./configure/make and CMake?
Both solve the same core problem: checking your system for dependencies and generating build instructions. But they take different approaches:
- Autotools (
./configure) is older (dating to the early 1990s), Unix-centric, and generates aMakefiledirectly. It’s simple and battle-tested but Linux/Unix-focused. - CMake is newer and cross-platform by design (Linux, Windows, macOS). It generates build files for an underlying tool. Usually that’s
makeon Ubuntu, but it could targetninjaor even Visual Studio project files on Windows. Projects with cross-platform ambitions increasingly pick CMake for that flexibility.
Neither wins outright. Autotools projects tend to be older, mature Unix tools (think gzip or curl). CMake has become the default for newer C++ projects, especially anything targeting multiple operating systems.
Wrapping Up
You now know both paths for compiling software on Ubuntu. Autotools: ./configure, then make, then sudo make install. CMake: cmake -S . -B build, then cmake --build build, then sudo cmake --install build. Do it twice and the “checking for…” scroll of ./configure output stops looking mysterious. It starts looking like exactly what it is: a dependency checklist.
Reach for apt first, always. It’s faster, safer, and gets security updates automatically. Save source builds for when you specifically need a newer version, a missing package, or a custom build flag. And learn checkinstall early if you’re going to make a habit of this. Future-you will thank present-you when it’s time to clean up a build gone sideways.
| Step | Action | Applies To |
|---|---|---|
| 1–2 | sudo apt update, then sudo apt install build-essential | Both workflows |
| 3–4 | Install autoconf/pkg-config or cmake as needed | Depends on project |
| 5 | Download source tarball or git clone | Both workflows |
| 6 | ./configure (or autoreconf -if first) | Autotools |
| 6 | cmake -S . -B build | CMake |
| 7 | make -j$(nproc) | Autotools |
| 7 | cmake --build build -j$(nproc) | CMake |
| 8 | sudo make install | Autotools |
| 8 | sudo cmake --install build | CMake |
| 9 | Verify with which and --version | Both workflows |