There are plenty of reasons to compile OpenSSL from source on a Debian or Ubuntu system. Maybe you need a specific version for application compatibility, want to enable FIPS 140-2/140-3 validated cryptography, or require custom cipher suites that the distribution packages do not ship. Whatever the reason, building OpenSSL yourself gives you full control over the TLS stack running on your server.

This guide walks through the entire process of building a custom OpenSSL installation on Ubuntu 24.04, Ubuntu 22.04, Debian 13 (Trixie), and Debian 12 (Bookworm). We install it under /opt/openssl-custom so it sits alongside the system OpenSSL without breaking anything – the distro packages remain untouched.

Why Compile OpenSSL From Source?

Before jumping into the build, here are the most common scenarios where a custom OpenSSL build makes sense:

  • Specific version requirements – Some applications (legacy or cutting-edge) require an exact OpenSSL version that your distro does not provide.
  • FIPS compliance – Government and enterprise environments often mandate FIPS 140-2 or 140-3 validated modules. The FIPS module must be built from a specific OpenSSL source tarball with particular configure flags.
  • Custom cipher suites – You may need to enable or disable specific ciphers, curves, or protocols at compile time for security hardening or regulatory compliance.
  • Performance tuning – Compiling with architecture-specific optimizations can improve TLS throughput on high-traffic servers.
  • Testing and development – Developers working on TLS-aware applications often need multiple OpenSSL versions available for testing.

Step 1 – Check the Current OpenSSL Version

Start by checking what version of OpenSSL is already installed on your system. This gives you a baseline and helps confirm that your custom build is separate from the system installation.

openssl version -a

On Ubuntu 24.04, you will typically see OpenSSL 3.0.x. On Debian 12, it is also OpenSSL 3.0.x. Older Ubuntu 22.04 systems ship with OpenSSL 3.0.2.

Take note of the output. After completing the custom build, you will compare this against the new binary to confirm everything worked.

which openssl

The system binary lives at /usr/bin/openssl. Our custom build will go to /opt/openssl-custom/bin/openssl, keeping things clean and separate.

Step 2 – Install Build Dependencies

Update the package index and install the toolchain required to compile OpenSSL from source.

sudo apt update
sudo apt install -y build-essential checkinstall zlib1g-dev libffi-dev libssl-dev wget perl

Here is what each package provides:

  • build-essential – GCC, G++, make, and other core compilation tools.
  • checkinstall – Lets you create a .deb package from the compiled source for cleaner uninstallation later.
  • zlib1g-dev – Compression library headers needed by OpenSSL for TLS compression support.
  • libffi-dev – Foreign function interface library, useful if you later build Python against this OpenSSL.
  • libssl-dev – System OpenSSL development headers (needed by some build scripts during configuration).
  • perl – OpenSSL’s Configure script is written in Perl.

Verify the compiler is available:

gcc --version
make --version

Both commands should return version information without errors.

Step 3 – Download the OpenSSL Source Tarball

Head to the official OpenSSL download page at https://www.openssl.org/source/ to identify the version you need. In this guide, we use OpenSSL 3.3.2 as an example – replace the version number with whatever release you require.

cd /usr/local/src
export OPENSSL_VERSION="3.3.2"
sudo wget https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz

Verify the download completed:

ls -lh /usr/local/src/openssl-${OPENSSL_VERSION}.tar.gz

You should see the tarball with a reasonable file size (typically 15-18 MB for OpenSSL 3.x).

Step 4 – Verify the GPG Signature

Never skip signature verification when building security-critical software from source. Download the signature file and the OpenSSL release signing keys.

cd /usr/local/src
sudo wget https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz.asc

Import the OpenSSL release signing keys from the official repository:

wget -qO- https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz.sha256 | head -1

Verify the SHA-256 checksum first:

echo "$(wget -qO- https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz.sha256)  openssl-${OPENSSL_VERSION}.tar.gz" | sha256sum --check

You should see openssl-3.3.2.tar.gz: OK in the output. If the check fails, do not proceed – re-download the tarball.

For full GPG verification, import the OpenSSL team keys and check the .asc signature:

gpg --keyserver keyserver.ubuntu.com --recv-keys BA5473A2B0587B07FB27CF2D216094DFD0CB81EF
gpg --verify openssl-${OPENSSL_VERSION}.tar.gz.asc openssl-${OPENSSL_VERSION}.tar.gz

A “Good signature” message confirms the tarball is authentic. A warning about the key not being certified with a trusted signature is normal – it just means you have not personally signed the OpenSSL team’s key.

Step 5 – Extract and Configure the Source

Extract the tarball and enter the source directory:

cd /usr/local/src
sudo tar -xzf openssl-${OPENSSL_VERSION}.tar.gz
cd openssl-${OPENSSL_VERSION}

Run the Configure script with a custom prefix to install under /opt/openssl-custom. This keeps the build completely isolated from the system OpenSSL.

sudo ./config \
  --prefix=/opt/openssl-custom \
  --openssldir=/opt/openssl-custom/ssl \
  shared \
  zlib \
  -Wl,-rpath,/opt/openssl-custom/lib64

Here is what each flag does:

  • –prefix=/opt/openssl-custom – Sets the installation directory for binaries, libraries, and headers.
  • –openssldir=/opt/openssl-custom/ssl – Sets the directory for OpenSSL configuration files and certificates.
  • shared – Builds shared libraries (.so files) in addition to static ones.
  • zlib – Enables zlib compression support.
  • -Wl,-rpath – Embeds the library path in the binary so it finds the correct shared libraries at runtime.

If you need FIPS support, add the enable-fips flag:

sudo ./config \
  --prefix=/opt/openssl-custom \
  --openssldir=/opt/openssl-custom/ssl \
  shared \
  zlib \
  enable-fips \
  -Wl,-rpath,/opt/openssl-custom/lib64

Verify the configuration completed without errors by checking the output – you should see a summary of enabled features and the target platform.

Step 6 – Compile and Install OpenSSL

Compile the source. Use the -j flag with nproc to parallelize the build across all available CPU cores:

sudo make -j$(nproc)

This typically takes 2-5 minutes depending on your hardware. Watch for any errors in the output.

Run the test suite to confirm the build is functional:

sudo make test

All tests should pass. A few skipped tests are normal (usually related to features not enabled in the build). Any failures should be investigated before proceeding.

Install the compiled binaries:

sudo make install

Verify the installation directory was populated:

ls -la /opt/openssl-custom/bin/
ls -la /opt/openssl-custom/lib64/

You should see the openssl binary in the bin directory and shared library files (libssl.so, libcrypto.so) in lib64.

Step 7 – Configure the Dynamic Linker

Register the custom OpenSSL shared libraries with the system’s dynamic linker so applications can find them at runtime.

echo "/opt/openssl-custom/lib64" | sudo tee /etc/ld.so.conf.d/openssl-custom.conf
sudo ldconfig

Verify the libraries are registered:

ldconfig -p | grep /opt/openssl-custom

You should see entries for libssl.so and libcrypto.so pointing to your custom installation path.

Step 8 – Verify the Custom OpenSSL Installation

Run the custom OpenSSL binary and check the version:

/opt/openssl-custom/bin/openssl version -a

The output should show the version you compiled (e.g., OpenSSL 3.3.2) and the correct OPENSSLDIR path /opt/openssl-custom/ssl.

Compare it with the system OpenSSL:

echo "System OpenSSL:"
/usr/bin/openssl version

echo "Custom OpenSSL:"
/opt/openssl-custom/bin/openssl version

Both should work independently. The system version remains unchanged.

Check the linked libraries to confirm the binary uses the correct shared objects:

ldd /opt/openssl-custom/bin/openssl

The libssl.so and libcrypto.so entries should point to /opt/openssl-custom/lib64/.

Test a TLS connection to confirm the build is fully functional:

/opt/openssl-custom/bin/openssl s_client -connect www.google.com:443 -brief

You should see a successful TLS handshake with protocol and cipher information.

Step 9 – Running Side-by-Side With System OpenSSL

The custom build lives entirely under /opt/openssl-custom and does not interfere with the system OpenSSL. This is the recommended approach for production servers where you want stability and control.

If you want to use the custom OpenSSL as the default for your user session, add it to your PATH:

echo 'export PATH="/opt/openssl-custom/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

Verify the change:

which openssl
openssl version

To make the custom OpenSSL available system-wide for all users (use with caution):

sudo bash -c 'echo "export PATH=/opt/openssl-custom/bin:\$PATH" > /etc/profile.d/openssl-custom.sh'

Important: Do not replace or symlink over the system OpenSSL binary at /usr/bin/openssl. Many system packages depend on the distribution-provided version. Replacing it will break package management and potentially lock you out of the server.

Step 10 – Configure Applications to Use Custom OpenSSL

Most applications that link against OpenSSL need to be told where to find your custom build. This is done through environment variables or compile-time flags.

Environment Variables for Runtime

Set these environment variables before launching an application that should use the custom OpenSSL:

export LD_LIBRARY_PATH=/opt/openssl-custom/lib64:$LD_LIBRARY_PATH
export PKG_CONFIG_PATH=/opt/openssl-custom/lib64/pkgconfig:$PKG_CONFIG_PATH

Compile-Time Flags for Building Software

When compiling software that links against OpenSSL, pass the custom paths:

CFLAGS="-I/opt/openssl-custom/include" \
LDFLAGS="-L/opt/openssl-custom/lib64 -Wl,-rpath,/opt/openssl-custom/lib64" \
./configure --with-openssl=/opt/openssl-custom

Step 11 – Build Python Against Custom OpenSSL

Python’s ssl module links against OpenSSL at compile time. If you need Python to use your custom OpenSSL, you must compile Python from source as well.

Install additional Python build dependencies:

sudo apt install -y libreadline-dev libncursesw5-dev libsqlite3-dev \
  tk-dev libgdbm-dev libc6-dev libbz2-dev liblzma-dev

Download and extract the Python source (using Python 3.12 as an example):

cd /usr/local/src
sudo wget https://www.python.org/ftp/python/3.12.8/Python-3.12.8.tgz
sudo tar -xzf Python-3.12.8.tgz
cd Python-3.12.8

Configure Python with your custom OpenSSL:

sudo ./configure \
  --prefix=/opt/python-custom \
  --with-openssl=/opt/openssl-custom \
  --with-openssl-rpath=auto \
  --enable-optimizations

Build and install:

sudo make -j$(nproc)
sudo make altinstall

Verify Python is using the custom OpenSSL:

/opt/python-custom/bin/python3.12 -c "import ssl; print(ssl.OPENSSL_VERSION)"

The output should show your custom OpenSSL version.

Step 12 – Build curl Against Custom OpenSSL

The system curl uses the distribution’s OpenSSL. To build curl with your custom version:

cd /usr/local/src
export CURL_VERSION="8.11.1"
sudo wget https://curl.se/download/curl-${CURL_VERSION}.tar.gz
sudo tar -xzf curl-${CURL_VERSION}.tar.gz
cd curl-${CURL_VERSION}

Configure curl to use your custom OpenSSL:

sudo ./configure \
  --prefix=/opt/curl-custom \
  --with-openssl=/opt/openssl-custom \
  LDFLAGS="-L/opt/openssl-custom/lib64 -Wl,-rpath,/opt/openssl-custom/lib64" \
  CPPFLAGS="-I/opt/openssl-custom/include"
sudo make -j$(nproc)
sudo make install

Verify the custom curl build:

/opt/curl-custom/bin/curl --version

The SSL library line should reference your custom OpenSSL version and path.

/opt/curl-custom/bin/curl -I https://www.google.com

A successful HTTPS response confirms the custom TLS stack is working correctly.

Troubleshooting

Configure fails with “Perl not found”

OpenSSL’s build system requires Perl. Install it:

sudo apt install -y perl

Error: “Cannot open shared object file” at runtime

This means the dynamic linker cannot find the custom shared libraries. Confirm the ldconfig configuration is correct:

cat /etc/ld.so.conf.d/openssl-custom.conf
sudo ldconfig
ldconfig -p | grep openssl-custom

If the conf file is missing or empty, recreate it:

echo "/opt/openssl-custom/lib64" | sudo tee /etc/ld.so.conf.d/openssl-custom.conf
sudo ldconfig

Wrong OpenSSL version reported after installation

If openssl version still shows the old system version, your PATH is not updated. Check which binary is being called:

which openssl
echo $PATH

Make sure /opt/openssl-custom/bin appears before /usr/bin in your PATH if you want the custom version to take priority.

Make test failures

If specific tests fail during make test, check whether you have all required build dependencies installed. Missing zlib headers are a common cause:

dpkg -l | grep zlib1g-dev

Also ensure you have enough disk space and memory. OpenSSL tests can be resource-intensive.

Permission denied during make install

The install target writes to /opt/openssl-custom, which requires root privileges. Always run the install step with sudo:

sudo make install

Applications still use system OpenSSL after building against custom version

Use ldd to check which libraries the application is actually loading:

ldd /path/to/your/application | grep ssl

If the output points to /usr/lib instead of /opt/openssl-custom/lib64, the application was not compiled with the correct LDFLAGS and RPATH. Rebuild the application with the flags shown in Step 10.

Cleaning up a failed build

If you need to start the build over, clean the source directory first:

cd /usr/local/src/openssl-${OPENSSL_VERSION}
sudo make clean
sudo make distclean

Then re-run the configuration and build steps from Step 5.

Uninstalling the Custom OpenSSL Build

Since we installed to an isolated prefix, removal is straightforward:

sudo rm -rf /opt/openssl-custom
sudo rm /etc/ld.so.conf.d/openssl-custom.conf
sudo ldconfig

Remove the PATH entry from ~/.bashrc or /etc/profile.d/openssl-custom.sh if you added one earlier.

Conclusion

You now have a custom OpenSSL installation running alongside the system version on your Ubuntu or Debian server. The build lives under /opt/openssl-custom where it stays isolated from system packages, and you can point individual applications at it through environment variables or compile-time flags.

Remember to monitor the OpenSSL security advisories page and rebuild when security patches are released. Unlike distribution packages, custom builds do not receive automatic updates through apt – maintaining them is your responsibility.

10 COMMENTS

  1. Centos 7:
    I had to add -lz to the linker variable in Makefile
    CNF_EX_LIBS=-ldl -pthread -lz
    …unsure if this is the best place for it but at least the linker finished.

  2. The \$PATH syntax escapes the $ which means that the modified PATH variable will not actually be a variable. This will mess up people’s PATH environment variable.

  3. Hello, I was following a guide of this website to install python on my system.
    How To Install Python 3.11 on CentOS 7 / RHEL 7 this guide said to update openssl and suggested to use this guide. I followed this guide and i updated openssl. The problem is that my webmin application is not working any more. Im not able to connect via browser. And I can not use it by CLI:
    [root@locahost ~]# systemctl status webmin
    Unit webmin.service could not be found.
    What happened?

  4. I followed this on my RHEL machine and broke it. Yum
    #rpm -Va
    rpm: symbol lookup error: /lib64/librpmio.so.8: undefined symbol: EVP_md2, version OPENSSL_1_1_0

    yum error:

    ImportError: /lib64/libk5crypto.so.3: undefined symbol: EVP_KDF_ctrl, version OPENSSL_1_1_1b

LEAVE A REPLY

Please enter your comment!
Please enter your name here