Linux

How to Build Software from Source on Ubuntu 24.04 (configure, make, cmake)

16 min read

The Back Room Tech is reader-supported. We may earn a commission when you buy through links on our site. Learn more.

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 sudo privileges
  • 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 cd and ls

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 apt and build-essential. Package names will differ.

RequirementDetails
OSUbuntu 24.04 LTS or later
Disk space500 MB–5 GB depending on project size
Privilegessudo access required for system-wide installs
CompilerInstalled 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) …

GNOME Terminal window showing the output of 'sudo apt install build-essential', highlighting the final 'Setting up build-essential' line. Use each marker target exactly. Terminal layout: Ubuntu 24.04 GNOME Terminal. Supplied commands/output are illustrative; GCC is 13.x. Preserve source-page text and all bracketed progress output. If reference search cannot supply a suitable image, use the deployed detailed text-only fallback; the image still requires visual approval.

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:

Very tight crop of Ubuntu Packages page containing ONLY the heading build-essential and the exact description Informational list of build-essential packages. Do not show ANY version number, terminal, command output, package list, dates or extra panels. This image identifies the package; no additional content is needed.

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 (via cmake_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 configure script. You’ll need autoreconf in 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

Terminal showing ./configure running dependency checks, highlighting the final 'configure: creating ./config.status' summary line. Use each marker target exactly. Terminal layout: Ubuntu 24.04 GNOME Terminal. Supplied commands/output are illustrative; GCC is 13.x. Preserve source-page text and all bracketed progress output. If reference search cannot supply a suitable image, use the deployed detailed text-only fallback; the image still requires visual approval.

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 build tells CMake to create (or reuse) a folder named build for 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

Terminal showing 'cmake -S . -B build' generating build files, highlighting the 'Build files have been written to' line. Use each marker target exactly. Terminal layout: Ubuntu 24.04 GNOME Terminal. Supplied commands/output are illustrative; GCC is 13.x. Preserve source-page text and all bracketed progress output. If reference search cannot supply a suitable image, use the deployed detailed text-only fallback; the image still requires visual approval.

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

Terminal showing 'make' compiling source files, highlighting the scrolling gcc compiler invocation lines. Use each marker target exactly. Terminal layout: Ubuntu 24.04 GNOME Terminal. Supplied commands/output are illustrative; GCC is 13.x. Preserve source-page text and all bracketed progress output. If reference search cannot supply a suitable image, use the deployed detailed text-only fallback; the image still requires visual approval.

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

Terminal showing 'cmake --build build' compiling, highlighting the percentage progress indicators like [ 45%]. Use each marker target exactly. Terminal layout: Ubuntu 24.04 GNOME Terminal. Supplied commands/output are illustrative; GCC is 13.x. Preserve source-page text and all bracketed progress output. If reference search cannot supply a suitable image, use the deployed detailed text-only fallback; the image still requires visual approval.

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/

Terminal showing 'sudo make install' copying compiled files into /usr/local directories, highlighting the completion output. Use each marker target exactly. Terminal layout: Ubuntu 24.04 GNOME Terminal. Supplied commands/output are illustrative; GCC is 13.x. Preserve source-page text and all bracketed progress output. If reference search cannot supply a suitable image, use the deployed detailed text-only fallback; the image still requires visual approval.

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’s INSTALL or README file first. Never run sudo make install on code you haven’t reviewed, or downloaded from an untrusted mirror. If you used --prefix=$HOME/.local in Step 6a, skip sudo entirely 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:

SettingWorkflowPurposeDefault
--prefix=DIRAutotools (./configure)Sets install location/usr/local
CMAKE_INSTALL_PREFIXCMakeSets install location/usr/local
CMAKE_BUILD_TYPECMakeRelease (optimized) or Debug (symbols)unset
CC / CXX (env vars)BothOverride which compiler to usesystem default gcc/g++
PKG_CONFIG_PATH (env var)BothTells build system where to find .pc library metadata filessystem default paths
-j$(nproc)Both (make/cmake --build)Parallel compile jobs1 (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
Terminal showing a ./configure error message about a missing library or header file, highlighting the need to install a -dev package. Use each marker target exactly. Terminal layout: Ubuntu 24.04 GNOME Terminal. Supplied commands/output are illustrative; GCC is 13.x. Preserve source-page text and all bracketed progress output. If reference search cannot supply a suitable image, use the deployed detailed text-only fallback; the image still requires visual approval.

Not sure of the exact package name? Search Ubuntu’s package index directly. Or use apt-file search libname.so (install apt-file first with sudo apt install apt-file, then sudo 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 uninstall support: some projects’ Makefiles include this target:
sudo make uninstall
  • Use checkinstall instead of make install: this wraps the install step into a real .deb package 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 --prefix or CMAKE_INSTALL_PREFIX you used during install. Then manually delete the resulting files from bin/, lib/, and share/ under that prefix.

Warning: Never manually delete files under /usr/local in 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 a Makefile directly. 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 make on Ubuntu, but it could target ninja or 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.

StepActionApplies To
1–2sudo apt update, then sudo apt install build-essentialBoth workflows
3–4Install autoconf/pkg-config or cmake as neededDepends on project
5Download source tarball or git cloneBoth workflows
6./configure (or autoreconf -if first)Autotools
6cmake -S . -B buildCMake
7make -j$(nproc)Autotools
7cmake --build build -j$(nproc)CMake
8sudo make installAutotools
8sudo cmake --install buildCMake
9Verify with which and --versionBoth workflows

Resources