Most software on Ubuntu should come from the package manager, but sometimes you need a newer release, a compile-time option the distribution disabled, or a tool that is not packaged at all. In that case you build it from source. In this tutorial you will install the toolchain on Ubuntu 24.04, download and verify a real source release (GNU nano), build it with the classic configure, make, make install workflow, install it under /usr/local without touching system packages, and learn the equivalent commands for projects that use CMake or Meson.

Prerequisites

To follow this guide you need:

  • A server running Ubuntu 24.04 LTS, for example a CubePath VPS.
  • A non-root user with sudo privileges.
  • About 1 GB of free disk space for build tools and sources. Large projects (compilers, browsers, databases) need several GB and more RAM.

Step 1 - Installing the build toolchain

The build-essential metapackage pulls in the GCC and G++ compilers, make, and the C library headers. Add pkg-config, which build scripts use to locate libraries, and the tools needed to download and verify sources:

sudo apt update
sudo apt install build-essential pkg-config curl gnupg xz-utils

Confirm the compiler and make are available:

gcc --version
make --version
gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0
...
GNU Make 4.3

Step 2 - Installing the library dependencies

Every project lists its build dependencies in a README, INSTALL or BUILDING file. On Ubuntu, libraries you link against are split into a runtime package (libncursesw6) and a development package ending in -dev (libncurses-dev) that contains the headers. You need the -dev packages to compile.

GNU nano needs the ncurses headers, plus libmagic-dev for syntax detection by file type:

sudo apt install libncurses-dev libmagic-dev

Using apt build-dep for packaged software

When you are building a newer version of something Ubuntu already packages, you can let apt install the exact build dependencies of the packaged version, which are usually close to what the new release needs. This requires source repositories. On Ubuntu 24.04 they are defined in /etc/apt/sources.list.d/ubuntu.sources:

sudo nano /etc/apt/sources.list.d/ubuntu.sources

In each block, change the Types: line so it includes deb-src:

Types: deb deb-src

Then refresh the package index and install the build dependencies:

sudo apt update
sudo apt build-dep nano

Finding which package provides a missing file

When configure complains about a missing header such as zlib.h, apt-file tells you which package ships it:

sudo apt install apt-file
sudo apt-file update
apt-file search include/zlib.h
zlib1g-dev: /usr/include/zlib.h

Step 3 - Downloading and verifying the source

Keep sources in a directory you own rather than building as root. Create one and move into it:

mkdir -p ~/src
cd ~/src

Check https://ftp.gnu.org/gnu/nano/ for the latest release and set the version in a variable so the following commands stay the same. This guide uses 8.3:

VERSION=8.3
curl -fLO "https://ftp.gnu.org/gnu/nano/nano-${VERSION}.tar.xz"
curl -fLO "https://ftp.gnu.org/gnu/nano/nano-${VERSION}.tar.xz.sig"

Before building anything, verify that the tarball was signed by the project's maintainers. GNU publishes a keyring with its maintainers' public keys:

curl -fLO https://ftp.gnu.org/gnu/gnu-keyring.gpg
gpg --verify --keyring ./gnu-keyring.gpg "nano-${VERSION}.tar.xz.sig" "nano-${VERSION}.tar.xz"

Look for Good signature in the output. A warning that the key is not certified with a trusted signature is expected, because you have not personally signed the maintainer's key:

gpg: Signature made ...
gpg: Good signature from "Benno Schulenberg <[email protected]>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!

If you see BAD signature, delete the files and do not build them.

Extract the archive and enter the source directory:

tar -xf "nano-${VERSION}.tar.xz"
cd "nano-${VERSION}"

Step 4 - Configuring the build

Projects that use GNU Autotools ship a configure script. It checks your system for compilers and libraries and writes the Makefiles. The most important option is --prefix, the directory the software will be installed into. The default for source builds is /usr/local, which keeps your files separate from those managed by apt (which live under /usr).

List the project-specific options first, so you know which features you can enable or disable:

./configure --help | less

Run configure with the prefix:

./configure --prefix=/usr/local

If a dependency is missing, configure stops with an error that names it, for example:

configure: error: *** No curses library found

Install the matching -dev package (Step 2) and run ./configure again. A successful run ends by creating the Makefiles and a config.status file, with no error: lines.

Step 5 - Compiling

Run make to compile. The -j flag runs several compile jobs in parallel; nproc returns the number of CPU cores:

make -j"$(nproc)"

On a small server this takes under a minute for nano. When it finishes without Error lines, the binary is in the src/ directory. Test it before installing:

./src/nano --version
 GNU nano, version 8.3

Many projects also provide a test suite. When one exists, run it at this point with make check (Autotools) or the equivalent documented by the project.

Step 6 - Installing under /usr/local

Only the installation step needs root, because it writes to /usr/local:

sudo make install

On Ubuntu, /usr/local/bin comes before /usr/bin in PATH, so your shell will now find the compiled version first. Clear the shell's command cache and check which binary runs:

hash -r
command -v nano
nano --version
/usr/local/bin/nano
 GNU nano, version 8.3

The version packaged by Ubuntu is still installed in /usr/bin/nano. apt keeps updating that copy and never touches the files under /usr/local.

If the project installs shared libraries into /usr/local/lib, refresh the dynamic linker cache so programs can find them:

sudo ldconfig

Installing to a self-contained prefix instead

For larger applications, installing each version into its own directory makes upgrades and removal trivial:

./configure --prefix=/opt/nano-${VERSION}
make -j"$(nproc)"
sudo make install

The binaries then live in /opt/nano-8.3/bin. Add that directory to PATH or create a symbolic link, for example sudo ln -s /opt/nano-8.3/bin/nano /usr/local/bin/nano. Removing the version later is a matter of deleting the directory and the link.

Step 7 - Uninstalling a source build

apt does not know about software you installed with make install, so it cannot remove it. Keep the source directory: Autotools projects provide an uninstall target that removes exactly the files make install created:

cd ~/src/nano-8.3
sudo make uninstall

Check that the system version is used again:

hash -r
command -v nano
/usr/bin/nano

Building projects that use CMake or Meson

Many modern projects do not ship a configure script. If the source tree contains a CMakeLists.txt, it uses CMake; if it contains meson.build, it uses Meson. Install both tools with:

sudo apt install cmake meson ninja-build

CMake

CMake builds in a separate directory, which keeps the source tree clean. From the project's top-level directory, configure, build and install:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local
cmake --build build -j"$(nproc)"
sudo cmake --install build

List the project's options with cmake -S . -B build -LH. CMake has no uninstall target by default, but it records every installed file in build/install_manifest.txt, which you can use to remove them:

xargs -d '\n' sudo rm -f < build/install_manifest.txt

Meson

Meson uses Ninja as its backend and also builds in a separate directory:

meson setup build --prefix=/usr/local --buildtype=release
meson compile -C build
sudo meson install -C build

Show the available options with meson configure build. To remove the installed files later, run sudo ninja -C build uninstall.

Troubleshooting

configure: error: ... not found or Package 'foo' was not found in the pkg-config search path: a development package is missing. Search for the header or .pc file with apt-file search foo.pc and install the -dev package that provides it.

fatal error: something.h: No such file or directory during make: same cause as above, detected later. Install the -dev package, then run ./configure again before make, because configure caches its results.

error while loading shared libraries: libfoo.so.1: cannot open shared object file: the library was installed under /usr/local/lib but the linker cache was not refreshed. Run sudo ldconfig.

The build is killed with no clear error: on servers with little RAM, parallel compilation of large C++ projects can exhaust memory and trigger the OOM killer. Check with sudo dmesg | grep -i oom, then build with fewer jobs (make -j2) or add swap.

The old version still runs after installing: your shell cached the old path. Run hash -r or open a new session.

Conclusion

You installed the build toolchain on Ubuntu 24.04, verified a signed source release, compiled it, installed it under /usr/local alongside the packaged version, and removed it cleanly. The same pattern (install -dev dependencies, configure with a prefix, build as a regular user, install with sudo) applies to Autotools, CMake and Meson projects alike.

As next steps, you can:

  • Subscribe to the project's release announcements, since source builds do not receive security updates through apt upgrade.
  • Keep each build in its own /opt/<name>-<version> prefix when you need several versions side by side.
  • Package your build as a .deb if you need to deploy it to many servers.