TL;DR
PHP is trying to load an extension listed in php.ini, but the .so file is missing, in the wrong location, or compiled for a different PHP version. Either install the missing extension, comment out the stale entry in php.ini, or fix the path.
What Actually Happened
You're staring at this in your logs at 2 AM:
PHP Warning: PHP Startup: Unable to load dynamic library 'redis.so' (tried: /usr/lib/php/20210902/redis.so (/usr/lib/php/20210902/redis.so: cannot open shared object file: No such file or directory), /usr/lib/php/20210902/redis.so.so (/usr/lib/php/20210902/redis.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
PHP reads extension=redis.so (or extension=redis) from php.ini or a file in /etc/php/8.1/conf.d/, but the .so file doesn't exist on disk. Three scenarios cause this:
- Extension never installed — someone added the entry to
php.inibut never ranapt install. - PHP was upgraded — the extension directory changed (e.g.,
20200930→20210902), old.sofiles are gone, new ones aren't installed yet. - Extension manually removed — the package was purged but the
php.inientry was left behind.
Step 1 — Pinpoint the Broken Extension
Run PHP from the command line to see all startup warnings at once:
php -v 2>&1 | head -20
# or force all warnings:
php -r "echo 'ok';" 2>&1
Note the exact extension name and the path PHP tried. Then check whether the file exists:
# Replace 'redis.so' and the path with what your error shows
ls -la /usr/lib/php/20210902/redis.so
Also find all active PHP ini files — the extension entry could be anywhere:
php --ini
# Shows: Loaded Configuration File + Additional .ini files
# Search all of them for the broken extension name
grep -r "extension=redis" /etc/php/
Fix 1 — Install the Missing Extension
If the extension was never installed, install it from your distro's package manager. Always match the PHP version:
# Ubuntu / Debian
sudo apt update
sudo apt install php8.1-redis # redis example
sudo apt install php8.1-gd php8.1-mbstring php8.1-curl # common ones
# CentOS / RHEL (with Remi repo)
sudo dnf install php81-php-redis
# After install, verify the .so landed in the right place
ls /usr/lib/php/$(php -r 'echo PHP_MAJOR_VERSION.PHP_MINOR_VERSION . "0" . (PHP_MAJOR_VERSION >= 8 ? "" : "");')/
Restart PHP-FPM or Apache to apply:
sudo systemctl restart php8.1-fpm
# or
sudo systemctl restart apache2
Fix 2 — Remove the Stale php.ini Entry
If you removed the extension intentionally and don't need it anymore, comment out the entry instead of leaving it to spam your logs:
# Find the file containing the entry
grep -rl "extension=redis" /etc/php/8.1/
# Edit it and comment the line out
sudo nano /etc/php/8.1/mods-available/redis.ini
# Change: extension=redis.so
# To: ;extension=redis.so
On Debian/Ubuntu systems, the cleaner way is to disable the module link:
sudo phpdismod redis
sudo systemctl restart php8.1-fpm
Fix 3 — PHP Version Mismatch After Upgrade
This is the most common production cause. You upgraded from PHP 8.0 to 8.1, and the extension directory changed from 20200930 to 20210902. Old .so files don't carry over automatically.
Check what API version your PHP expects:
php -i | grep 'PHP Extension'
# Output: PHP Extension => 20210902
Then reinstall all extensions for the new version:
# List what was installed for the old version
dpkg -l | grep php8.0
# Reinstall for new version
sudo apt install php8.1-{mysql,redis,gd,mbstring,curl,xml,zip}
If you compiled an extension manually via PECL, you need to recompile it:
sudo pecl uninstall redis
sudo pecl install redis
# Or with specific version
sudo pecl install redis-5.3.7
Fix 4 — Wrong Extension Path in php.ini
If someone hardcoded an absolute path in the extension directive, it breaks when things move:
# Bad (hardcoded path)
extension=/usr/lib/php/20200930/redis.so
# Good (just the name — PHP resolves the path via extension_dir)
extension=redis.so
# or even shorter
extension=redis
Check what extension_dir PHP is actually using:
php -i | grep extension_dir
# extension_dir => /usr/lib/php/20210902
Verify the Fix
# No more startup warnings
php -r "echo 'clean';" 2>&1
# Confirm extension loaded
php -m | grep redis
# Or check via phpinfo
php -r "phpinfo();" | grep -A2 redis
# For PHP-FPM, check the process log
sudo journalctl -u php8.1-fpm -n 50 --no-pager
If you're running multiple PHP versions or SAPIs (CLI + FPM + Apache mod_php), each has its own php.ini — fix all of them, not just the one you found first.
Quick Reference — Common Extension Package Names
# Ubuntu/Debian (replace 8.1 with your version)
php8.1-mysql # mysqli, pdo_mysql
php8.1-pgsql # pdo_pgsql
php8.1-redis # redis
php8.1-memcached # memcached
php8.1-gd # image processing
php8.1-zip # zip
php8.1-xml # xml, simplexml, dom
php8.1-mbstring # multibyte strings
php8.1-curl # curl
php8.1-intl # internationalization
php8.1-soap # SOAP client
Prevention
Before a PHP version upgrade, dump a list of installed extensions so you can reinstall them all in one shot:
# Before upgrade
php -m > php-extensions-before.txt
# After upgrade, diff to see what's missing
php -m > php-extensions-after.txt
diff php-extensions-before.txt php-extensions-after.txt
For config file hygiene, keeping your php.ini and conf.d/ files tidy matters more than it seems. If your deployment configs are stored as YAML (Ansible vars, Helm values, etc.), the YAML ↔ JSON Converter on ToolCraft is handy for spotting malformed config before it hits the server — runs entirely in the browser, nothing uploaded.
Also consider pinning extension packages in your deployment scripts so upgrades don't silently drop them:
# In your provisioning script / Dockerfile
apt-get install -y \
php${PHP_VERSION}-mysql \
php${PHP_VERSION}-redis \
php${PHP_VERSION}-gd \
php${PHP_VERSION}-mbstring

