Error

"the following required packages could not be found: OpenSSL" — How to Fix

Fix the missing OpenSSL error by installing the OpenSSL development libraries using your system's package manager.

The missing piece is not Rust code

You run cargo build on a fresh machine. The terminal fills with red text. The build script for openssl-sys panics with a message that reads "the following required packages could not be found: OpenSSL". You are writing Rust. You have not written a line of C. You did not add OpenSSL to your Cargo.toml. The build fails because a dependency you use, like reqwest or tokio, wraps the OpenSSL C library to handle TLS. The wrapper is Rust. The engine is C. The Rust compiler needs the C engine's headers to generate the calls. Without those headers, the compiler cannot verify the function signatures. The build stops.

The boundary between Rust and C

Rust's package manager handles Rust crates. It downloads source code, compiles it, and links it. It does not manage C libraries. C libraries are managed by your operating system's package manager. When a Rust crate provides bindings to a C library, the crate contains Rust code that declares the C functions. The actual implementation lives in a binary file like libssl.so or libssl.dylib.

The compiler needs two things to link against a C library. It needs the header files to know the function signatures. It needs the binary library to provide the implementation. The header files are usually installed in a "development" package. The binary library is in a "runtime" package. Many systems install the runtime package by default. The development package is often missing. The error you see means the build script cannot find the development files.

How the build script finds the library

The openssl-sys crate includes a build.rs file. This script runs before your code compiles. Its job is to locate OpenSSL on your system. The script uses a tool called pkg-config. pkg-config is a command-line utility that maintains a database of installed libraries. It reads metadata files with a .pc extension. These files describe where the headers and libraries live.

When openssl-sys runs, it calls pkg-config --cflags --libs openssl. If pkg-config finds the openssl.pc file, it returns the include paths and library paths. The build script passes these paths to the compiler. If pkg-config cannot find the file, it returns an error. The build script propagates that error as the "required packages could not be found" message.

The root cause is almost always one of two things. The OpenSSL development package is not installed. Or pkg-config itself is not installed. Minimal Docker images and fresh OS installs often lack pkg-config. The build script assumes it exists.

Minimal reproduction

Create a new project and add the openssl crate. This triggers the same build script logic as reqwest or other crates that depend on OpenSSL.

// Cargo.toml
[package]
name = "openssl-test"
version = "0.1.0"
edition = "2021"

[dependencies]
// WHY: This crate pulls in openssl-sys, which runs the build script
// that searches for the C library via pkg-config.
openssl = "0.10"
// src/main.rs
fn main() {
    // WHY: A simple call to prove the crate links.
    // If the build fails, this code never runs.
    let _ = openssl::ssl::SslConnector::builder(openssl::ssl::SslMethod::tls());
    println!("OpenSSL is available.");
}

Run cargo build. If the development files are missing, the output ends with:

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

Caused by:
  process didn't exit successfully: `target/debug/build/openssl-sys-.../build-script-build` (exit status: 101)
  --- stderr
  thread 'main' panicked at '
  Could not find directory of OpenSSL installation, and this `-sys` crate cannot
  proceed without this knowledge. If OpenSSL is installed and this crate had
  trouble finding it, you can set the `OPENSSL_DIR` environment variable for the
  compilation process.
  ...
  the following required packages could not be found:
          OpenSSL
  Make sure you have installed the required packages:
          libssl-dev
  ', /.../openssl-sys-0.9.103/build/main.rs:99:13

The error message even suggests the fix. The suggestion varies by platform. The underlying requirement is the same. Install the development headers and ensure pkg-config can find them.

Install the development package. The runtime binary is useless to the compiler.

Platform-specific fixes

The package names differ across distributions. The pattern is consistent. You need the library, the headers, and pkg-config.

Debian and Ubuntu

The development package is libssl-dev. The pkg-config tool is in a package named pkg-config. Both are often required.

sudo apt-get update
sudo apt-get install libssl-dev pkg-config

Fedora and RHEL

The development package is openssl-devel. The tool is pkg-config.

sudo dnf install openssl-devel pkg-config

macOS with Homebrew

Homebrew installs OpenSSL to a non-standard path. On Apple Silicon Macs, the path is /opt/homebrew. On Intel Macs, it is /usr/local. The pkg-config tool may not search these paths by default.

Install OpenSSL and pkg-config:

brew install openssl pkg-config

Set the environment variable so pkg-config finds the metadata:

# Apple Silicon
export PKG_CONFIG_PATH="/opt/homebrew/lib/pkgconfig"

# Intel
export PKG_CONFIG_PATH="/usr/local/lib/pkgconfig"

cargo build

Add the export to your shell profile to make it permanent. The path depends on your architecture. Check the output of brew --prefix openssl to find the correct directory.

Windows

Windows users typically use vcpkg or MSYS2. The openssl-sys crate has built-in support for vcpkg. If vcpkg is installed and OpenSSL is added to the vcpkg registry, the build script finds it automatically.

# Using vcpkg
vcpkg install openssl:x64-windows

If you use MSYS2, install the development packages via pacman and ensure pkg-config is available in the MSYS2 environment.

Check pkg-config. It is the bridge between Rust and C.

Convention aside: the pkg-config assumption

The Rust community assumes pkg-config is present on development machines. Tutorials and documentation rarely mention it. This assumption breaks on minimal containers and stripped-down servers. Always install pkg-config alongside C library dependencies. The overhead is negligible. The time saved debugging missing .pc files is substantial.

The fallback chain

The openssl-sys build script does not rely solely on pkg-config. It implements a probe chain to maximize compatibility. The script tries strategies in order.

  1. Check the OPENSSL_DIR environment variable. If set, the script uses this directory as the root for headers and libraries.
  2. Check OPENSSL_STATIC. If set, the script requests static linking.
  3. Invoke pkg-config.
  4. On Windows, check for vcpkg.
  5. Try cmake as a last resort.
  6. Fail with the error message.

This chain explains why setting OPENSSL_DIR works even when pkg-config fails. If you have OpenSSL installed in a custom location, you can bypass pkg-config entirely.

# Bypass pkg-config by pointing directly to the installation
export OPENSSL_DIR="/usr/local/opt/openssl"
cargo build

The OPENSSL_DIR variable must point to the directory containing include/openssl and lib. The script appends the subdirectories automatically.

Use OPENSSL_DIR when pkg-config is unavailable or misconfigured. Use pkg-config for standard installations where the metadata is correct.

Static versus dynamic linking

By default, openssl-sys produces a binary that dynamically links to the system OpenSSL. The binary depends on the shared library at runtime. This keeps the binary small. It also means the binary requires OpenSSL to be installed on the target machine.

Static linking bundles the OpenSSL library into your binary. The binary becomes larger. It no longer depends on the system library. This simplifies deployment. You can run the binary on a machine without OpenSSL installed.

Enable static linking with an environment variable. The static libraries must be available on your system. Most development packages include them.

# Request static linking
export OPENSSL_STATIC=1
cargo build

The build script passes flags to the linker to prefer static archives. If the static libraries are missing, the linker fails. Ensure your development package provides static files. On Debian, libssl-dev includes static libraries. On some systems, you may need a separate package.

Static linking is useful for containers and embedded targets. Dynamic linking is standard for desktop applications where the OS manages library updates.

Realistic example: Dockerfile configuration

Building in a Docker container is a common scenario where this error appears. The base Rust image often lacks development tools. The Dockerfile must install the dependencies before building.

# Dockerfile
FROM rust:1.75-slim

# WHY: Install development headers and pkg-config.
# The slim image does not include these by default.
# libssl-dev provides the OpenSSL headers and libraries.
# pkg-config allows the build script to locate them.
RUN apt-get update && apt-get install -y \
    libssl-dev \
    pkg-config \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY . .

# WHY: Build the project.
# The build script will now find OpenSSL via pkg-config.
RUN cargo build --release

This pattern ensures the build environment has everything the build script needs. The final image still contains the development packages. For a production image, use a multi-stage build to copy only the binary.

# Dockerfile (multi-stage)
FROM rust:1.75-slim AS builder
RUN apt-get update && apt-get install -y libssl-dev pkg-config
WORKDIR /app
COPY . .
RUN cargo build --release

FROM debian:bookworm-slim
COPY --from=builder /app/target/release/my-app /usr/local/bin/my-app
CMD ["my-app"]

The production stage does not need OpenSSL headers. It only needs the runtime library if dynamic linking is used. The debian:bookworm-slim image includes the runtime library. If you use static linking, you can use an even smaller base image like scratch.

Trust the build script, but provide the tools it expects.

Pitfalls and edge cases

pkg-config reports a version mismatch

The build script checks the OpenSSL version. If the installed version is too old, the build fails. The error message mentions the version requirement. Update OpenSSL to a supported version. The openssl crate supports OpenSSL 1.0.2, 1.1.x, and 3.x. Very old systems may ship with 1.0.1 or earlier. Upgrade the system or use a newer distribution.

E0463: cannot find library

If the build script succeeds but the linker fails with E0463, the headers were found but the library file is missing. This happens when pkg-config returns an incorrect library path. Check the output of pkg-config --libs openssl. Verify the path exists. Set OPENSSL_DIR to override the path if necessary.

macOS Homebrew path changes

Homebrew occasionally changes its prefix. If you upgrade Homebrew or switch machines, the path may differ. Always derive the path from brew --prefix openssl. Hardcoding /usr/local breaks on Apple Silicon. Hardcoding /opt/homebrew breaks on Intel. Use the command to get the correct path.

Cross-compilation

Cross-compiling for a different architecture requires the target version of OpenSSL. The build script cannot use the host OpenSSL. You must install the cross-compiled libraries and set OPENSSL_DIR to the target directory. Tools like cross handle this automatically by building OpenSSL in the target environment.

Cross-compilation is complex. Use a tool that manages the target toolchain and libraries.

Decision: when to use OpenSSL versus alternatives

Rust offers pure-Rust alternatives to OpenSSL. These alternatives eliminate the C dependency entirely. They simplify builds and deployment. The choice depends on your requirements.

Use the openssl crate when you need maximum compatibility with existing C ecosystems or specific algorithms not implemented in pure-Rust libraries. Use the openssl crate when you are wrapping C code that already links against OpenSSL. Use rustls when you want a pure-Rust TLS implementation with no external C dependencies. Use ring when you need low-level cryptographic primitives without the overhead of a full TLS stack. Reach for reqwest with default-features = false and features = ["rustls-tls"] to avoid OpenSSL entirely in HTTP clients. Reach for hyper with rustls for server-side TLS without C bindings.

Pure Rust is the path of least resistance for deployment.

Where to go next