Error

"pkg-config not found" — How to Fix

Install the `pkg-config` package using your system's package manager, as it is required by Cargo to locate native dependencies and their compiler flags.

The build script hits a wall

You run cargo build for a project that touches native libraries. The compiler stops dead. The error message screams pkg-config not found. You installed Rust. You installed Cargo. You didn't install anything called pkg-config. Where did this come from?

Rust code runs safely on its own. But many crates wrap C or C++ libraries. Those libraries live outside the Rust ecosystem. They sit in system directories like /usr/lib or /opt/local. The Rust compiler doesn't know where to look for them. It also doesn't know what flags to pass to the linker. pkg-config bridges that gap. It tells the build system exactly where libraries live and how to link them.

What pkg-config actually does

pkg-config is a command-line tool that reads metadata about installed libraries. When a Rust crate depends on a native library, its build.rs script usually calls pkg-config to query the system. The query looks like this:

pkg-config --cflags --libs openssl

The tool returns strings like -I/usr/include/openssl -L/usr/lib/x86_64-linux-gnu -lssl -lcrypto. Cargo passes these flags to rustc. The -I flag tells the compiler where to find header files. The -L flag tells the linker where to find library binaries. The -l flag names the libraries to link.

Think of pkg-config as a translator. Your Rust build speaks Rust. The C ecosystem speaks a different dialect of paths and flags. pkg-config translates between them. Without it, the compiler is deaf to your native dependencies.

The .pc file format

pkg-config doesn't magic up information. It reads plain text files with a .pc extension. Libraries install these files alongside their binaries. A typical .pc file looks like this:

# /usr/lib/x86_64-linux-gnu/pkgconfig/openssl.pc
prefix=/usr
exec_prefix=${prefix}
libdir=${prefix}/lib/x86_64-linux-gnu
includedir=${prefix}/include

Name: OpenSSL
Description: Secure Sockets Layer and cryptography libraries
Version: 1.1.1
Libs: -L${libdir} -lssl -lcrypto
Cflags: -I${includedir}

The file defines variables and lists the flags. When you run pkg-config --libs openssl, the tool substitutes the variables and outputs the Libs line. If the .pc file is missing, pkg-config fails. This usually happens when you install a library but forget the development package.

Minimal reproduction

You can trigger the error with a simple dependency. Add openssl to your Cargo.toml. This crate wraps the native OpenSSL library.

// Cargo.toml
// This crate depends on the native OpenSSL library.
// Cargo will invoke pkg-config to find it.
[dependencies]
openssl = "0.10"

Running cargo build triggers the build script inside openssl-sys. The build script calls pkg-config --libs openssl. If pkg-config isn't on your PATH, the build script panics. The output looks like this:

error: failed to run custom build command for `openssl-sys v0.9.90`

Caused by:
  process didn't exit successfully: `target/debug/build/openssl-sys-.../build-script-build` (exit status: 1)
  --- stderr
  thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: 
  "pkg-config not found. Please install pkg-config"', build.rs:5:21

This isn't a Rust compiler error like E0382. It's a build script failure. The Rust code hasn't even started compiling. The build infrastructure failed to locate a tool it needs.

Fixing the error

Install pkg-config using your system's package manager. The package name varies by OS.

# Ubuntu/Debian
# pkg-config is the tool; libssl-dev provides the headers for OpenSSL.
# Without the dev package, pkg-config cannot find the library metadata.
sudo apt-get update
sudo apt-get install -y pkg-config libssl-dev

# Fedora/RHEL
# The package name is usually pkgconf-pkg-config on newer Fedora.
# openssl-devel provides the headers.
sudo dnf install -y pkgconf-pkg-config openssl-devel

# macOS
# Homebrew installs pkg-config and the library.
# macOS often ships with older system libraries; Homebrew provides newer ones.
# You may need to set PKG_CONFIG_PATH if Homebrew installs to /opt/homebrew.
brew install pkg-config openssl

# Windows (MSYS2)
# MSYS2 is the standard environment for Rust on Windows.
# Use the mingw-w64 package to match the Rust toolchain.
pacman -S mingw-w64-x86_64-pkg-config mingw-w64-x86_64-openssl

After installation, verify the tool is available:

pkg-config --version

If this prints a version number, the tool is on your PATH. If it fails, check your shell configuration. Custom paths or shells like zsh or fish sometimes require you to reload the environment.

The hidden trap: development headers

Installing pkg-config is often not enough. You also need the development headers for the libraries you use. pkg-config only provides paths and flags. It does not install the headers.

On Debian and Ubuntu, library packages are split. The runtime library is libssl. The headers and .pc files are in libssl-dev. If you only install libssl, pkg-config finds nothing.

Convention aside: On Debian-based systems, development packages almost always end in -dev. On Red Hat-based systems, they end in -devel. On macOS with Homebrew, the package usually includes headers by default. On Windows with MSYS2, the -devel suffix is common.

Check your error message. If pkg-config is found but the build fails with "library not found" or "header not found", you are missing the dev package. Install it and retry.

Non-standard paths

Sometimes libraries are installed in non-standard locations. pkg-config searches a default list of directories. If your library lives elsewhere, pkg-config won't find it.

Set the PKG_CONFIG_PATH environment variable to point to the directory containing the .pc files.

# Linux/macOS
export PKG_CONFIG_PATH="/opt/my-lib/lib/pkgconfig:$PKG_CONFIG_PATH"

# Windows (MSYS2)
export PKG_CONFIG_PATH="/c/my-lib/lib/pkgconfig;$PKG_CONFIG_PATH"

This variable tells pkg-config to search additional directories. You can set it in your shell profile or pass it directly to Cargo.

PKG_CONFIG_PATH="/opt/my-lib/lib/pkgconfig" cargo build

Treat PKG_CONFIG_PATH as a last resort. If you have to set it, your library installation is fighting the build system. Fix the installation if possible.

Docker and CI environments

Minimal Docker images often lack pkg-config. If you build in a container, you must install it explicitly.

# Dockerfile
FROM rust:1.75-slim

# Install pkg-config and the library dev headers.
# The dev headers are required because pkg-config only points to them;
# it does not install them.
RUN apt-get update && apt-get install -y \
    pkg-config \
    libssl-dev \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY . .
RUN cargo build --release

In CI/CD workflows, add a step to install pkg-config before building.

# .github/workflows/rust.yml
- name: Install pkg-config
  run: |
    sudo apt-get update
    sudo apt-get install -y pkg-config

Check your -dev packages. pkg-config can't wave a magic wand to create headers that aren't there.

Pitfalls

Path issues. pkg-config is installed, but the shell doesn't see it. This happens after a fresh install or when switching shells. Run which pkg-config to check. If it returns nothing, your PATH is wrong. Reload your shell or add the directory to PATH.

Missing headers. pkg-config is found, but the build fails with missing headers. You installed the runtime library but not the development package. Install the -dev or -devel package.

Version mismatches. pkg-config finds a library, but the version is too old. The crate requires a newer version. Update the system library or use a vendored build.

Windows toolchain mismatch. On Windows, mixing MSVC and MinGW toolchains causes linking errors. Rust on Windows defaults to MSVC. If you install pkg-config via MSYS2, ensure you are using the x86_64-pc-windows-gnu target. Or use the MSVC-compatible packages if available.

Static vs dynamic. pkg-config usually returns flags for dynamic linking. If you want static linking, some libraries provide a --static flag. Check the crate documentation. Some crates support static linking via features.

Decision matrix

Use pkg-config when your system already has the native library installed and you want to link against the system version. This keeps your binary small and uses the OS-provided updates.

Reach for vendoring when you need reproducible builds across different machines and don't want to rely on system packages. Crates like openssl-sys support vendoring via features, which downloads the source and compiles it during the build.

Pick pre-built binaries when you are distributing a standalone executable and want to avoid system dependencies entirely. Some crates offer static linking features that bundle the library into your binary.

Use vcpkg or conan on Windows when you are working in a C++ heavy environment and need a package manager that integrates with Visual Studio or MSVC toolchains.

System packages save space. Vendoring saves sanity. Pick based on where your binary runs.

Where to go next