DEV Community

Michael Oteng
Michael Oteng

Posted on

Patching Critical PHP Vulnerabilities on Ubuntu 24.04: A Production Laravel Walkthrough

On 21 October 2026, two critical PHP vulnerabilities, CVE-2026-109618 and CVE-2026-109619, prompted a review of the production infrastructure behind SwiftVecto, a web-based platform offering browser utilities and file-processing tools.

The following morning, I carried out security maintenance on the Ubuntu server hosting the application.

What initially appeared to be a straightforward PHP upgrade turned into a useful reminder about distribution-maintained packages, security backports, backups, and the difference between a successful package installation and a healthy production application.

This article documents the actual maintenance process, including the commands used, the results obtained, and the checks performed before returning the server to normal operation.

For security reasons, application-specific directory paths, database names and service identifiers have been replaced with generic examples.

1. Understanding the vulnerability and the available fix

The two PHP vulnerabilities were addressed through security updates released by the PHP project and Linux distribution maintainers.

The important question was not simply whether the server was running the latest upstream PHP release.

It was whether the installed packages contained the security fixes required to address the vulnerabilities.

This distinction matters because Ubuntu, like other Linux distributions, can backport security fixes into existing package versions.

For Ubuntu 24.04 LTS, security notice USN-8948-1 identified the corrected PHP 8.3 package revision as:

8.3.6-0ubuntu0.24.04.13
Enter fullscreen mode Exit fullscreen mode

That was the version I needed to verify against the production environment.

Rather than replacing Ubuntu's PHP installation with packages from an alternative repository, the preferred approach was to use the distribution's maintained security packages.

2. Establishing the production environment

The environment consisted of:

Component Configuration
Hosting DigitalOcean Droplet
Operating system Ubuntu 24.04.4 LTS
Application framework Laravel
Web server Nginx
PHP runtime PHP 8.3-FPM
Database MySQL
Background processing Supervisor-managed Laravel queue worker

I started by checking the operating system and PHP version:

cat /etc/os-release
php -v
Enter fullscreen mode Exit fullscreen mode

The PHP command reported:

PHP 8.3.6 (cli)
Enter fullscreen mode Exit fullscreen mode

At first glance, that looked concerning.

The upstream PHP project had released newer versions containing the security fixes, while the server was still reporting PHP 8.3.6.

However, the upstream version number alone was not sufficient to determine whether the Ubuntu installation was patched.

There were two reasons.

First, php -v reports the command-line runtime, which is not necessarily the runtime serving web requests.

Second, Ubuntu may retain the same upstream PHP version while incorporating backported security fixes.

The next step was to inspect the actual installed packages.

3. Checking the installed PHP package revisions

I queried the PHP packages using:

dpkg-query -W \
  -f='${Package} ${Version}\n' \
  'php8.3*' 2>/dev/null
Enter fullscreen mode Exit fullscreen mode

The relevant installed packages were at:

8.3.6-0ubuntu0.24.04.12
Enter fullscreen mode Exit fullscreen mode

This included:

  • php8.3-cli
  • php8.3-fpm
  • php8.3-common
  • php8.3-opcache

Ubuntu's security notice identified .13 as the corrected revision.

The server was running .12.

That established that the relevant security update had not yet been installed.

Why the package revision matters

Consider these two versions:

8.3.6-0ubuntu0.24.04.12
8.3.6-0ubuntu0.24.04.13
Enter fullscreen mode Exit fullscreen mode

Both contain the upstream version number 8.3.6.

However, they are different Ubuntu package revisions.

The final revision incorporates distribution-specific changes, which can include security backports.

For administrators maintaining Ubuntu systems, this is why checking only php -v can be misleading.

The exact package revision must be compared against the vendor's security advisory.

4. Refreshing the package indexes

I checked the versions available through APT:

apt-cache policy php8.3-cli php8.3-fpm
Enter fullscreen mode Exit fullscreen mode

Initially, the installed and candidate versions were identical:

Installed: 8.3.6-0ubuntu0.24.04.12
Candidate: 8.3.6-0ubuntu0.24.04.12
Enter fullscreen mode Exit fullscreen mode

The patched revision was not yet visible in the local package indexes.

I refreshed the indexes:

sudo apt update
Enter fullscreen mode Exit fullscreen mode

Running the policy check again showed:

Installed: 8.3.6-0ubuntu0.24.04.12
Candidate: 8.3.6-0ubuntu0.24.04.13
Enter fullscreen mode Exit fullscreen mode

The patched version was now available through the configured Ubuntu repositories.

There was no need to introduce a third-party repository or migrate to another PHP release branch.

This kept the maintenance focused on the relevant security update.

5. Simulating the upgrade before changing production

Before installing anything, I ran an APT simulation:

sudo apt-get -s install --only-upgrade \
  php8.3-cli \
  php8.3-fpm \
  php8.3-common \
  php8.3-opcache
Enter fullscreen mode Exit fullscreen mode

APT reported:

14 upgraded, 0 newly installed,
0 to remove and 69 not upgraded.
Enter fullscreen mode Exit fullscreen mode

The proposed changes included the four explicitly requested packages and their related PHP extensions.

These included components used for MySQL connectivity, XML processing, ZIP handling, image processing and other application functionality.

All 14 packages were scheduled to move from revision .12 to .13.

The simulation provided two useful assurances:

  1. The update could be resolved without installing additional packages or removing existing ones.
  2. The proposed changes were limited to the expected PHP packages and extensions.

The 69 other packages were outside the scope of this targeted PHP update and would require separate review.

For production maintenance, I prefer to understand exactly what the package manager intends to change before approving an installation.

6. Creating backups before applying the patches

Before proceeding, I created a DigitalOcean Droplet snapshot.

That provided a server-level recovery option if the maintenance introduced an unexpected infrastructure problem.

However, I also wanted a separate logical database backup.

The application uses MySQL on the same server, so relying exclusively on a Droplet snapshot would not have been my preferred approach.

I first verified the database service:

systemctl is-active mysql
Enter fullscreen mode Exit fullscreen mode

The result was:

active
Enter fullscreen mode Exit fullscreen mode

I then created a restricted backup directory.

The path below is illustrative rather than the actual production location:

sudo install -d -m 700 /root/app-backups
Enter fullscreen mode Exit fullscreen mode

A compressed logical database backup can be created using:

set -o pipefail

BACKUP="/root/app-backups/database-$(date +%Y%m%d-%H%M%S).sql.gz"

mysqldump \
  --single-transaction \
  --quick \
  --no-tablespaces \
  --routines \
  --triggers \
  --events \
  app_database | gzip > "$BACKUP"
Enter fullscreen mode Exit fullscreen mode

Here, app_database is a placeholder for the actual database name.

The --single-transaction option is useful for producing a consistent logical backup of InnoDB tables without locking them for the entire dump, provided concurrent schema changes are avoided.

The pipefail setting ensures that a failure in mysqldump is not concealed by a successful exit from gzip.

After creating the backup, I checked the compressed file:

gzip -t "$BACKUP"
Enter fullscreen mode Exit fullscreen mode

I also inspected the end of the SQL dump:

zcat "$BACKUP" | tail -n 8
Enter fullscreen mode Exit fullscreen mode

The actual backup contained the MySQL completion marker:

-- Dump completed on 2026-10-22  4:28:45
Enter fullscreen mode Exit fullscreen mode

The compressed backup was approximately 866 KB.

Restricting access to database backups is also important:

chmod 600 "$BACKUP"
Enter fullscreen mode Exit fullscreen mode

The backup passed the compression integrity check, and the completion marker was present.

These checks do not replace a proper restoration test, but they provide useful evidence that the backup completed successfully.

With the Droplet snapshot and database backup available, the update could proceed with a more reasonable recovery position.

7. Applying the PHP security update

I ran the targeted installation:

sudo apt-get install --only-upgrade \
  php8.3-cli \
  php8.3-fpm \
  php8.3-common \
  php8.3-opcache
Enter fullscreen mode Exit fullscreen mode

APT upgraded the expected 14 PHP packages.

These included:

  • PHP CLI and PHP-FPM
  • PHP common components and OPcache
  • MySQL and SQLite extensions
  • cURL, GD and internationalisation extensions
  • Multibyte string, XML and ZIP extensions
  • Other installed PHP 8.3 components

The installation completed successfully, and PHP-FPM restarted during the update.

I then checked the installed package revisions:

dpkg-query -W \
  -f='${Package} ${Version}\n' \
  php8.3-cli \
  php8.3-fpm \
  php8.3-common \
  php8.3-opcache
Enter fullscreen mode Exit fullscreen mode

All four packages reported:

8.3.6-0ubuntu0.24.04.13
Enter fullscreen mode Exit fullscreen mode

This matched the corrected revision identified in Ubuntu's security notice.

The PHP vulnerabilities had been addressed through Ubuntu's security-maintained packages without changing the application's PHP major or minor version.

8. Verifying PHP-FPM and background processing

Installing the packages was only part of the maintenance.

The next step was to verify that the services required by the application were still functioning.

I checked PHP-FPM:

systemctl status php8.3-fpm --no-pager
Enter fullscreen mode Exit fullscreen mode

The service reported:

Active: active (running)
Enter fullscreen mode Exit fullscreen mode

I reviewed its recent journal entries:

journalctl -u php8.3-fpm \
  --since "15 minutes ago" \
  --no-pager -n 40
Enter fullscreen mode Exit fullscreen mode

The inspected output showed normal service restarts during the package installation, with no startup errors.

The application also uses Laravel queues for background processing.

These workers are managed by Supervisor, so I checked their status:

sudo supervisorctl status
Enter fullscreen mode Exit fullscreen mode

The production worker was running.

This is an important check for Laravel applications.

A website may respond successfully to HTTP requests while queued jobs remain unprocessed because a background worker has stopped.

Checking the homepage alone would not establish that the application was functioning as expected.

9. Checking the website and application logs

I tested the public HTTPS endpoint:

curl -sS -o /dev/null \
  -w 'HTTP status: %{http_code}\n' \
  https://swiftvecto.com/
Enter fullscreen mode Exit fullscreen mode

The response was:

HTTP status: 200
Enter fullscreen mode Exit fullscreen mode

That confirmed the homepage was responding successfully.

I also reviewed the Laravel application logs for evidence of new errors following the update.

For readers adapting this procedure, the application log can be inspected using a command such as:

tail -n 50 /var/www/example-app/storage/logs/laravel.log
Enter fullscreen mode Exit fullscreen mode

The path above is an example and should be replaced with the relevant application directory.

When reviewing logs after maintenance, I find it useful to distinguish between historical errors and errors that began during or after the change window.

An existing error should not automatically be attributed to the latest update simply because it appears in the log.

In this case, the inspected log contained no newly recorded production errors attributable to the PHP maintenance.

That was reassuring, but it was not a substitute for testing individual application features.

An HTTP 200 response verifies that a particular endpoint responded successfully. It does not prove that every controller, database operation, queued job or file-processing function works correctly.

Infrastructure verification and application regression testing are separate responsibilities.

10. Identifying a pending kernel update

During the maintenance, Ubuntu also reported that the running kernel was older than the installed kernel.

The running kernel was:

6.8.0-134-generic
Enter fullscreen mode Exit fullscreen mode

The newer installed kernel was:

6.8.0-147-generic
Enter fullscreen mode Exit fullscreen mode

This was separate from the PHP vulnerability remediation.

However, because the server was already undergoing controlled maintenance, I decided to complete the pending kernel activation.

Before rebooting, I checked that the essential services were configured to start automatically:

systemctl is-enabled \
  nginx php8.3-fpm mysql supervisor
Enter fullscreen mode Exit fullscreen mode

All four returned:

enabled
Enter fullscreen mode Exit fullscreen mode

I checked their current running states:

systemctl is-active \
  nginx php8.3-fpm mysql supervisor
Enter fullscreen mode Exit fullscreen mode

All four were active.

I also verified the installed kernel package:

dpkg-query -W \
  -f='${Package} ${Status} ${Version}\n' \
  linux-image-6.8.0-147-generic
Enter fullscreen mode Exit fullscreen mode

The result confirmed:

linux-image-6.8.0-147-generic install ok installed 6.8.0-147.147
Enter fullscreen mode Exit fullscreen mode

Finally, I checked for failed systemd units:

systemctl --failed --no-pager
Enter fullscreen mode Exit fullscreen mode

The result was:

0 loaded units listed.
Enter fullscreen mode Exit fullscreen mode

With the pre-reboot checks completed, I initiated a controlled reboot:

sudo systemctl reboot
Enter fullscreen mode Exit fullscreen mode

11. Post-reboot verification

Once the server became available again, I reconnected and verified the active kernel:

uname -r
Enter fullscreen mode Exit fullscreen mode

The output was:

6.8.0-147-generic
Enter fullscreen mode Exit fullscreen mode

I checked the essential services:

systemctl is-active \
  nginx php8.3-fpm mysql supervisor
Enter fullscreen mode Exit fullscreen mode

All four returned:

active
Enter fullscreen mode Exit fullscreen mode

The Supervisor-managed Laravel queue worker had restarted successfully.

I repeated the public website check:

curl -sS -o /dev/null \
  -w 'HTTP status: %{http_code}\n' \
  https://swiftvecto.com/
Enter fullscreen mode Exit fullscreen mode

The response remained:

HTTP status: 200
Enter fullscreen mode Exit fullscreen mode

Finally:

systemctl --failed --no-pager
Enter fullscreen mode Exit fullscreen mode

Again returned:

0 loaded units listed.
Enter fullscreen mode Exit fullscreen mode

The new kernel was active, the essential services had recovered, and the public application remained accessible.

12. What this maintenance exercise reinforced

There are several lessons worth taking from this process.

The upstream version number is not the whole story

PHP 8.3.6 may appear outdated when compared with a newer upstream release.

On Ubuntu, however, security fixes can be backported into distribution-maintained packages.

The correct approach is to compare the exact installed package revision against the relevant Ubuntu security notice.

Simulate package changes before applying them

APT's simulation mode provides an opportunity to inspect dependency changes, package removals and the overall upgrade scope.

It is a simple step that can prevent avoidable surprises.

Maintain more than one recovery option

A Droplet snapshot and a logical MySQL backup serve different purposes.

Having both available before modifying production infrastructure improves recovery options if something goes wrong.

Neither should be considered fully validated without an appropriate restoration test.

Check background services as well as HTTP responses

Laravel applications may depend on queue workers, scheduled tasks and other processes that are not exercised by a homepage request.

Checking Nginx and PHP-FPM is necessary, but it may not be sufficient.

Keep security maintenance focused

A critical security update is not the ideal time to introduce unrelated application changes.

The PHP update was handled as a targeted maintenance operation, with unrelated work left for separate review.

A successful update is not a complete regression test

A successful package installation, active services, healthy queue workers and an HTTP 200 response are all useful indicators.

They do not prove that every application feature is functioning correctly.

That distinction should remain clear when documenting production maintenance.

Conclusion

The PHP security update on SwiftVecto's production infrastructure was completed successfully.

The server moved from Ubuntu's PHP package revision:

8.3.6-0ubuntu0.24.04.12
Enter fullscreen mode Exit fullscreen mode

To:

8.3.6-0ubuntu0.24.04.13
Enter fullscreen mode Exit fullscreen mode

This addressed the vulnerabilities identified in Ubuntu's security notice.

Fourteen PHP packages were upgraded, PHP-FPM restarted successfully, and the Laravel queue worker remained operational.

A pending kernel update was also activated through a controlled reboot.

Following the reboot, Nginx, PHP-FPM, MySQL and Supervisor were all active, the website returned HTTP 200, and systemd reported no failed services.

The maintenance did not constitute a complete application regression test or a penetration test, but it provided clear evidence that the required PHP security packages had been installed and that the essential production infrastructure was functioning afterwards.

For me, the main takeaway is that security patching is not simply about installing a newer software version. It is about establishing what is affected, verifying the vendor's fix, preparing a recovery path, controlling the change, and checking the system afterwards.


References

Top comments (0)