$ cat upgrade-php-74-to-80-on-ubuntu.md
Upgrade PHP 7.4 to 8.0 on Ubuntu
A self-hosted indie developer walks through upgrading PHP 7.4 to 8.0 on Ubuntu 18.04 without Laravel Forge, covering PPA setup, installing PHP 8.0 and extensions, purging old versions, and switching Nginx FastCGI sockets. The guide also flags pitfalls encountered along the way, including Composer install errors after the upgrade.
chasingcode.dev05 Mar 2021 · 8 min read · summary
Read on chasingcode.dev ↗I host several websites on a Linode VPS, originally provisioned with Laravel Forge. I have long since canceled my Forge subscription, and I do my own maintenance, manually. I may not be the quickest to upgrade to the latest version of anything, but I will do it eventually.
As an indie maker with zero revenue from my side projects, it wasn't making financial sense to pay for Forge. The biggest value Forge brought me was the initial provisioning of the instance. Afterwards, I continued to deploy code manually (how hard can git pull be?), and do my own basic server maintenance. I've been doing this successfully for the better part of the last 2 years.
This guide is about upgrading from PHP 7.4 to 8.0 on Ubuntu 18.04. As of this writing, I have yet to upgrade to Ubuntu 20.04, but it will happen soon(ish).
The instructions are partly based on this excellent article, but I've added many specifics, details, and pitfalls I encountered in my own situation. It wasn't exactly smooth sailing, as you'll see.
Prep
SSH into the instance, then...
# Check PHP version
php -v
PHP 7.4.15 (cli) (built: Feb 23 2021 15:12:05) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
with Zend OPcache v7.4.15, Copyright (c), by Zend Technologies
# List all PHP packages
dpkg -l | grep php | tee packages.txt
# List
ii php-common 2:81+ubuntu18.04.1+deb.sury.org+1 all Common files for PHP packages
ii php-igbinary 3.2.1+2.0.8-6+ubuntu18.04.1+deb.sury.org+1 amd64 igbinary PHP serializer
ii php-memcached 3.1.5+2.2.0-9+ubuntu18.04.1+deb.sury.org+1 amd64 memcached extension module for PHP, uses libmemcached
ii php-msgpack 2.1.2+0.5.7-6+ubuntu18.04.1+deb.sury.org+1 amd64 PHP extension for interfacing with MessagePack
ii php-pear 1:1.10.12+submodules+notgz+20210212-1+ubuntu18.04.1+deb.sury.org+1 all PEAR Base System
ii php7.4-bcmath 7.4.15-7+ubuntu18.04.1+deb.sury.org+1 amd64 Bcmath module for PHP
ii php7.4-cli 7.4.15-7+ubuntu18.04.1+deb.sury.org+1 amd64 command-line interpreter for the PHP scripting language
ii php7.4-common 7.4.15-7+ubuntu18.04.1+deb.sury.org+1 amd64 documentation, examples and common module for PHP
ii php7.4-curl 7.4.15-7+ubuntu18.04.1+deb.sury.org+1 amd64 CURL module for PHP
ii php7.4-dev 7.4.15-7+ubuntu18.04.1+deb.sury.org+1 amd64 Files for PHP7.4 module development
ii php7.4-fpm 7.4.15-7+ubuntu18.04.1+deb.sury.org+1 amd64 server-side, HTML-embedded scripting language (FPM-CGI binary)
ii php7.4-gd 7.4.15-7+ubuntu18.04.1+deb.sury.org+1 amd64 GD module for PHP
ii php7.4-igbinary 3.2.1+2.0.8-6+ubuntu18.04.1+deb.sury.org+1 amd64 igbinary PHP serializer
ii php7.4-imap 7.4.15-7+ubuntu18.04.1+deb.sury.org+1 amd64 IMAP module for PHP
ii php7.4-intl 7.4.15-7+ubuntu18.04.1+deb.sury.org+1 amd64 Internationalisation module for PHP
ii php7.4-json 7.4.15-7+ubuntu18.04.1+deb.sury.org+1 amd64 JSON module for PHP
ii php7.4-mbstring 7.4.15-7+ubuntu18.04.1+deb.sury.org+1 amd64 MBSTRING module for PHP
rc php7.4-memcached 3.1.5+2.2.0-9+ubuntu18.04.1+deb.sury.org+1 amd64 memcached extension module for PHP, uses libmemcached
ii php7.4-msgpack 2.1.2+0.5.7-6+ubuntu18.04.1+deb.sury.org+1 amd64 PHP extension for interfacing with MessagePack
ii php7.4-mysql 7.4.15-7+ubuntu18.04.1+deb.sury.org+1 amd64 MySQL module for PHP
ii php7.4-opcache 7.4.15-7+ubuntu18.04.1+deb.sury.org+1 amd64 Zend OpCache module for PHP
ii php7.4-pgsql 7.4.15-7+ubuntu18.04.1+deb.sury.org+1 amd64 PostgreSQL module for PHP
ii php7.4-readline 7.4.15-7+ubuntu18.04.1+deb.sury.org+1 amd64 readline module for PHP
ii php7.4-soap 7.4.15-7+ubuntu18.04.1+deb.sury.org+1 amd64 SOAP module for PHP
ii php7.4-sqlite3 7.4.15-7+ubuntu18.04.1+deb.sury.org+1 amd64 SQLite3 module for PHP
ii php7.4-xml 7.4.15-7+ubuntu18.04.1+deb.sury.org+1 amd64 DOM, SimpleXML, XML, and XSL module for PHP
ii php7.4-zip 7.4.15-7+ubuntu18.04.1+deb.sury.org+1 amd64 Zip module for PHP
ii php8.0-common 8.0.2-7+ubuntu18.04.1+deb.sury.org+1 amd64 documentation, examples and common module for PHP
ii php8.0-igbinary 3.2.1+2.0.8-6+ubuntu18.04.1+deb.sury.org+1 amd64 igbinary PHP serializer
ii php8.0-memcached 3.1.5+2.2.0-9+ubuntu18.04.1+deb.sury.org+1 amd64 memcached extension module for PHP, uses libmemcached
ii php8.0-msgpack 2.1.2+0.5.7-6+ubuntu18.04.1+deb.sury.org+1 amd64 PHP extension for interfacing with MessagePack
ii pkg-php-tools 1.35ubuntu1 all various packaging tools and scripts for PHP packages
Add PPAs
# Add ondrej/php PPA
sudo add-apt-repository ppa:ondrej/php # Press enter when prompted.
# CAVEATS:
# 1. If you are using php-gearman, you need to add ppa:ondrej/pkg-gearman
# 2. If you are using apache2, you are advised to add ppa:ondrej/apache2
# 3. If you are using nginx, you are advised to add ppa:ondrej/nginx-mainline or ppa:ondrej/nginx
sudo add-apt-repository ppa:ondrej/nginx
# Remove a repository (not needed here, listed for reference)
# sudo add-apt-repository --remove ppa:ondrej/nginx
# Check if PPA was added - should see a number of entries
grep ^ /etc/apt/sources.list /etc/apt/sources.list.d/* | grep ondrej/php
grep ^ /etc/apt/sources.list /etc/apt/sources.list.d/* | grep ondrej/nginx
sudo apt-get update
Install PHP 8.0
sudo apt install php8.0-common php8.0-cli -y
# Check version
php -v
PHP 8.0.2 (cli) (built: Feb 23 2021 15:13:59) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.2, Copyright (c) Zend Technologies
with Zend OPcache v8.0.2, Copyright (c), by Zend Technologies
# Check modules (each will appear on its own line, listed on a single line here for brevity)
php -m
[PHP Modules] calendar Core ctype date exif FFI fileinfo filter ftp gettext hash iconv igbinary json libxml memcached msgpack openssl pcntl pcre PDO Phar posix readline Reflection session shmop sockets sodium SPL standard sysvmsg sysvsem sysvshm tokenizer Zend OPcache zlib
[Zend Modules]
Zend OPcache
# Install additional extensions that were present in 7.4
# Note: no need to install php8.0-json; it's already provided by other packages
sudo apt install php8.0-{bcmath,curl,dev,fpm,gd,igbinary,imap,intl,mbstring,memcached,msgpack,mysql,opcache,pgsql,readline,soap,sqlite3,xml,zip}
Cleanup
Since I won't be using old versions of PHP (< 7.4), now is a good time to remove them.
# Purge old packages, in my case from PHP 5.6 thru 7.3
sudo apt purge '^php5.6.*'
sudo apt purge '^php7.0.*'
sudo apt purge '^php7.1.*'
sudo apt purge '^php7.2.*'
sudo apt purge '^php7.3.*'
Update Nginx config for each site
This is for Nginx servers only. Sorry, I can't help you with Apache.
Repeat the procedure for each site. There's a way to automate it, probably with sed but I don't do this often enough to warrant the
sudo vi /etc/nginx/sites-enabled/example.com
# Look for the following block
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; # change to php8.0-fpm.sock or even better to php8.0-fpm.sock
fastcgi_index index.php;
include fastcgi_params;
}
# Test Nginx config
sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
# Restart PHP FPM & Nginx
sudo service php8.0-fpm restart
sudo service nginx restart
Note The reason I recommend using the generic php-fpm.sock instead of php8.0-fpm.sock is because, on my server at least, php-fpm.sock is actually aliased to php8.0-fpm.sock. Run these commands to find out:
ls -al /var/run/php/php-fpm.sock # /var/run/php/php-fpm.sock -> /etc/alternatives/php-fpm.sock
ls -al /etc/alternatives/php-fpm.sock # /etc/alternatives/php-fpm.sock -> /run/php/php8.0-fpm.sock
I don't know the specifics of why it is so, but I assume it was done as part of the PHP 8 upgrade, which works really well for me as I don't have to worry about changing the Nginx config the next time I upgrade PHP.
Composer install errors?
Let's say that, after upgrading PHP to the shiny new 8.0, you want to run composer install --no-interaction --prefer-dist --optimize-autoloader in your Laravel project, and see the following errors:
Deprecation Notice: Required parameter $path follows optional parameter $schema in phar:///usr/local/bin/composer/vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/UndefinedConstraint.php:62
Deprecation Notice: Required parameter $path follows optional parameter $schema in phar:///usr/local/bin/composer/vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/UndefinedConstraint.php:108
Deprecation Notice: Method ReflectionParameter::getClass() is deprecated in phar:///usr/local/bin/composer/src/Composer/Repository/RepositoryManager.php:130
Deprecation Notice: Method ReflectionParameter::getClass() is deprecated in phar:///usr/local/bin/composer/src/Composer/Repository/RepositoryManager.php:130
Loading composer repositories with package information
Installing dependencies (including require-dev) from lock file
PHP Fatal error: Uncaught ArgumentCountError: array_merge() does not accept unknown named parameters in phar:///usr/local/bin/composer/src/Composer/DependencyResolver/DefaultPolicy.php:84
Stack trace:
#0 [internal function]: array_merge()
#1 phar:///usr/local/bin/composer/src/Composer/DependencyResolver/DefaultPolicy.php(84): call_user_func_array()
#2 phar:///usr/local/bin/composer/src/Composer/DependencyResolver/Solver.php(387): Composer\DependencyResolver\DefaultPolicy->selectPreferredPackages()
#3 phar:///usr/local/bin/composer/src/Composer/DependencyResolver/Solver.php(740): Composer\DependencyResolver\Solver->selectAndInstall()
#4 phar:///usr/local/bin/composer/src/Composer/DependencyResolver/Solver.php(231): Composer\DependencyResolver\Solver->runSat()
#5 phar:///usr/local/bin/composer/src/Composer/Installer.php(489): Composer\DependencyResolver\Solver->solve()
#6 phar:///usr/local/bin/composer/src/Composer/Installer.php(232): Composer\Installer->doInstall()
#7 phar:///usr/local/bin/composer/src/Composer/Command/InstallCommand.php(122): Composer\Installer->run()
#8 phar:///usr/local/bin/composer/v
source
This is a summary of an article first published by chasingcode.dev. The full piece is on their site.