Troubleshooting

error while loading shared libraries: cannot open shared object file — how to fix it

A program cannot find a .so file it was linked against. Find what is missing with ldd, install the right package on any distro, or fix the library path.

Published ~8 min read

error while loading shared libraries: libfoo.so.1: cannot open shared object file: No such file or directory means the program was linked against a library that the system cannot find. Run ldd on the program to list every missing .so file, find the package that ships each one and install it. If the library is already on disk in a directory of its own, register that directory with ldconfig instead of exporting LD_LIBRARY_PATH everywhere.

quick fix
# 1. which libraries are missing?
ldd /path/to/program | grep 'not found'

# 2. which package ships one?
apt-file search libfoo.so.1            # Debian, Ubuntu (needs apt-file, see below)
dnf provides '*/libfoo.so.1'           # Rocky, AlmaLinux, RHEL, Fedora

# 3. library sitting in a custom directory? register it once
echo /opt/app/lib | sudo tee /etc/ld.so.conf.d/app.conf && sudo ldconfig

What does the error mean?

When you start a dynamically linked program, the dynamic linker — on x86-64 glibc systems /lib64/ld-linux-x86-64.so.2 — loads every library the program lists as a dependency before the program’s own code runs. If one of them is in none of the directories it searches, it stops with this message. The program never starts, and the shell reports exit status 127, the same code as “command not found”.

Each library is named by its soname, such as libssl.so.3, and the number is part of the name. libssl.so.1.1 and libssl.so.3 are different libraries with incompatible interfaces. That is why the popular trick of symlinking the version you have to the name the program wants does not work: the program either fails on missing symbols a moment later or crashes in the middle of something.

On Alpine and other musl systems the same problem reads differently: Error loading shared library libfoo.so.1: No such file or directory (needed by ./prog). The fixes below apply all the same.

Which library is missing?

ldd prints the full dependency tree and marks what it cannot resolve:

any Linux
ldd ./prog | grep 'not found'
#   libfoo.so.1 => not found

Do not point ldd at a binary you do not trust. Its manual page warns that, in some circumstances, it may execute the program to get the answer, and recommends objdump -p for untrusted files. readelf works just as well. Both come with binutils, and both show only the direct dependencies, not the whole tree:

safe for untrusted binaries
readelf -d ./prog | grep NEEDED
objdump -p ./prog | grep NEEDED

Which package ships the library?

Debian and Ubuntu

apt-file searches the contents of every package in your configured repositories, installed or not:

Debian · Ubuntu
sudo apt update && sudo apt install -y apt-file
sudo apt-file update
apt-file search libssl.so.3
# libssl3: /usr/lib/x86_64-linux-gnu/libssl.so.3

sudo apt install -y libssl3

Library packages carry the soname in their name, and on Debian 13 and Ubuntu 24.04 many gained a t64 suffix in the 64-bit time transition — there the OpenSSL library package is libssl3t64. Let apt-file tell you rather than guessing. If apt cannot find the package at all, see Unable to locate package.

Rocky, AlmaLinux, RHEL and Fedora

Rocky · Alma · RHEL · Fedora
dnf provides '*/libcrypt.so.1'
sudo dnf install -y libxcrypt-compat

Alpine

Alpine packages declare the libraries they provide as so: names, and apk search matches them. Refresh the index first; container images ship without one.

Alpine
apk update
apk search -e 'so:libstdc++.so.6'
apk add libstdc++

Install the package by its name, as above, rather than with apk add so:…. Alpine’s own documentation advises against the so: form for installs, because the next soname bump makes apk upgrade fail instead of updating the library.

Which missing libraries come up most often?

libssl.so.1.1 or libcrypto.so.1.1: cannot open shared object file

The program was built against OpenSSL 1.1, and your system only has OpenSSL 3: Ubuntu 22.04 and 24.04 and Debian 12 and 13 ship nothing older. OpenSSL 1.1.1 reached end of life in September 2023. The fix is a build of the program that is linked against OpenSSL 3 — usually just its current release, or the vendor’s package for your distribution.

libcrypt.so.1

Rocky and AlmaLinux 9 moved to a newer libcrypt, and the old soname lives in a separate package. On Debian and Ubuntu, libcrypt1 comes with the base system.

Rocky · Alma 9
sudo dnf install -y libxcrypt-compat

32-bit programs: SteamCMD and game servers

A 32-bit program needs 32-bit libraries, and a 64-bit server has none by default. The tell is that the “missing” library, often libstdc++.so.6, is clearly installed. file shows the architecture:

Debian · Ubuntu
file ./server        # "ELF 32-bit LSB executable, Intel 80386" = 32-bit

sudo dpkg --add-architecture i386
sudo apt update
sudo apt install -y lib32gcc-s1 lib32stdc++6
sudo apt install -y libfoo1:i386      # any other 32-bit library, by package name

Valve’s SteamCMD documentation lists lib32gcc-s1 as the dependency for a manual install on Ubuntu and Debian; its packaged route enables the i386 architecture first. Game servers you then download through SteamCMD may need more 32-bit libraries, which the game’s own server documentation names. On Rocky and AlmaLinux the equivalents are glibc.i686 and libstdc++.i686.

glibc programs on Alpine

Most prebuilt Linux binaries are built against glibc, and Alpine uses musl. Such a program either fails with Error loading shared library or, when even the glibc loader is missing, with a baffling sh: ./prog: not found although the file is right there. apk add gcompat adds a compatibility layer that is enough for simple programs. For anything substantial, use a Debian-based image such as debian:12-slim, or the vendor’s own image.

libGL.so.1 in a Python container

This one usually comes from opencv-python, which expects desktop graphics libraries. On a server you rarely need them: install opencv-python-headless instead. If you do need the full package, install libgl1. The old name libgl1-mesa-glx from many tutorials no longer exists on Debian 13 or Ubuntu 24.04.

“GLIBC_2.34 not found” is a different problem

version ‘GLIBC_2.34’ not found (required by ./prog) means the library is there, but it is too old. The program was built on a distribution with a newer glibc than yours. Anything compiled against glibc 2.34 or later usually needs at least GLIBC_2.34, so it fails on Debian 11, Ubuntu 20.04 and RHEL 8. Compare the two numbers:

any glibc system
ldd --version | head -1                                       # what you have
objdump -T ./prog | grep -o 'GLIBC_[0-9.]*' | sort -Vu | tail -1   # what it needs

For reference: Rocky and AlmaLinux 9 have glibc 2.34, Ubuntu 22.04 has 2.35, Debian 12 has 2.36, Ubuntu 24.04 has 2.39 and Debian 13 has 2.41. Never replace the system glibc by hand — everything on the machine depends on it. Get a build made for your release, build the program from source (you will need a compiler toolchain), or run it on a newer distribution or in a container based on one.

What if the library is in /opt or /usr/local/lib?

Software installed by hand often brings its own libraries in a directory the linker does not search. Register the directory once and refresh the linker cache:

glibc systems
echo /opt/app/lib | sudo tee /etc/ld.so.conf.d/app.conf
sudo ldconfig

# check that the linker now knows the library
ldconfig -p | grep libfoo

Debian and Ubuntu already list /usr/local/lib (in /etc/ld.so.conf.d/libc.conf), so after a make install there you only need sudo ldconfig. RHEL-family systems usually do not search /usr/local/lib or /usr/local/lib64; add them with a file like the one above. Alpine has no linker cache at all: musl reads its search path from /etc/ld-musl-x86_64.path and, without that file, searches /lib, /usr/local/lib and /usr/lib.

When is LD_LIBRARY_PATH the right tool?

For a single program or a single service, LD_LIBRARY_PATH is fine. Set it where that program starts, not globally:

one process or one service
LD_LIBRARY_PATH=/opt/app/lib ./prog

# systemd unit, in the [Service] section
Environment=LD_LIBRARY_PATH=/opt/app/lib
  • It is inherited by every child process, which can load the wrong library there.
  • The linker ignores it for setuid and setcap programs.
  • sudo drops it, so sudo ./prog fails even though ./prog works.
  • In /etc/environment or a global profile it changes library lookup for every program on the machine. Use ldconfig instead.

Why does it only fail inside my Docker image?

Multi-stage builds are the usual culprit. The program is compiled in a full image and copied into a slim, Alpine or distroless image that does not have its runtime libraries. Check from outside, provided the image contains ldd:

check an image
docker run --rm --entrypoint ldd myimage /app/server
  • Keep the families matched. Build on debian:12, run on debian:12-slim, and install the runtime packages there, for example libssl3 and ca-certificates (libssl3t64 on Debian 13).
  • Do not build on glibc and run on Alpine. Build in an Alpine image if the result has to run on Alpine.
  • Go: CGO_ENABLED=0 produces a static binary that needs no shared libraries at all.

If the error says Permission denied rather than “cannot open shared object file”, the library was found and something else is wrong — see bash: Permission denied. To reproduce a library problem cleanly, a fresh Standard KVM VPS with the distribution the binary was built for is ready in about a minute.

Frequently asked

How do I find which package provides a .so file?

On Debian and Ubuntu install apt-file, run 'sudo apt-file update', then 'apt-file search libfoo.so.1'. On Rocky, AlmaLinux and RHEL run dnf provides '*/libfoo.so.1'. On Alpine run 'apk update' and then 'apk search -e so:libfoo.so.1'. Install the package it names.

Is it safe to install libssl1.1 from an old release?

No. OpenSSL 1.1.1 reached end of life in September 2023, and a package copied by hand from an old Ubuntu or Debian release never receives security updates. Use a build of the program linked against OpenSSL 3 or the vendor's package for your release; on Rocky and AlmaLinux 9 the distribution's own compat-openssl11 package is a maintained stopgap.

Should I use LD_LIBRARY_PATH or ldconfig?

ldconfig for a directory every program should find: put it in a file under /etc/ld.so.conf.d and run 'sudo ldconfig'. LD_LIBRARY_PATH only for one program or one service, set where it starts — it is inherited by child processes, ignored for setuid programs and dropped by sudo.

What does 'GLIBC_2.34 not found' mean?

The program was built against a newer glibc than your system has. Anything compiled against glibc 2.34 or later usually needs at least GLIBC_2.34, so it fails on Debian 11, Ubuntu 20.04 or RHEL 8. Do not replace the system glibc; get a build for your release, compile it yourself, or run it on a newer distribution or in a container.

Related articles

All troubleshooting