Debian

How To Install PHP 8.2 on Debian 13 / 12

Debian 12 ships PHP 8.2 as its default branch, but Debian 13 moved on to PHP 8.4 and has no php8.2 packages at all. To install PHP 8.2 on Debian 13 you need OndΕ™ej SurΓ½’s repository, which builds every stable PHP branch from 5.6 to 8.5. PHP 8.2 gets upstream security fixes only until 31 December 2026, so plan the move to a newer branch. On Ubuntu, follow the PHP 8.2 guide for Ubuntu instead.

Original content from computingforgeeks.com - post 132863

Below we add the Sury repository, install PHP 8.2 and its modules, check it against PHP2Shell and connect Nginx or Apache. In October 2026 we ran the install and the PHP2Shell check on fresh Debian 13 and Debian 12 systems, plus a real upgrade of an unpatched Debian 12 server, and the output blocks come from those runs.

These PHP 8.2 changes, released in December 2022, still decide whether older code runs on it:

Install PHP 8.2 on Debian 13 or 12

The path is the same on both releases: add the repository, install the packages, confirm the build is patched, then connect a web server. Run the commands as a regular user with sudo rights. The repository line fills in your release codename, trixie or bookworm, so the same commands serve both.

Add the Sury PHP repository

Debian 13 needs this repository because trixie carries no php8.2 packages. On Debian 12 it is optional. Bookworm’s own php8.2 is the same branch, and Debian’s LTS team shipped the PHP2Shell fix for it through bookworm-security, so after the system update below you can skip straight to the install step if you would rather stay on Debian packages. Older releases are a different story, because both are out of Debian support. Sury still publishes the patched 8.2.35 build for Debian 11 (bullseye), so that box can pull a fixed PHP 8.2 for free, while Debian’s own PHP 7.4 there gets the PHP2Shell fix only through Freexian’s paid ELTS. Debian 10 (buster) has no Sury repository at all any more, and we found no free patched PHP 8.2 for it. Once a server runs Debian 13, the current PHP guide for Debian covers the distribution’s own PHP 8.4.

Upgrade a Debian 11 or Debian 10 server one release at a time (10 to 11, 11 to 12, then 12 to 13), because Debian supports upgrades only from the previous release, and the last hop is covered in the Debian 12 to 13 upgrade guide. That beats keeping PHP alive on a dead release. Nothing else on those systems gets security fixes either, so a patched PHP does not make the rest of the box safe.

Bring the system up to date before adding anything, because the repository builds link against current Debian libraries and a stale system is where dependency errors come from.

sudo apt update && sudo apt -y upgrade

The older version of this guide installed software-properties-common and fetched the key with wget. On a fresh Debian 13 system both fail: apt answers Error: Unable to locate package software-properties-common because trixie dropped the package, and a minimal install has no wget. Neither is needed. Install the three small tools the setup really uses.

sudo apt install -y lsb-release ca-certificates curl

Sury publishes its signing key as a small Debian package, which drops the keyring into /usr/share/keyrings/ and keeps it updated when the key rotates, so you never edit key files by hand.

curl -sSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb
sudo dpkg -i /tmp/debsuryorg-archive-keyring.deb

Add the repository, signed by that keyring, with the same keyring path and file name as Sury’s own README. lsb_release -sc fills in your release codename.

echo "deb [signed-by=/usr/share/keyrings/debsuryorg-archive-keyring.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/php.list

Refresh the package index so apt reads the new source.

sudo apt update

The output must include two lines from packages.sury.org, the release file and the package list. A signature error at this point means the keyring package did not install, so rerun the dpkg -i line before going further:

Hit:1 http://deb.debian.org/debian trixie InRelease
Hit:2 http://deb.debian.org/debian trixie-updates InRelease
Hit:3 http://deb.debian.org/debian-security trixie-security InRelease
Get:4 https://packages.sury.org/php trixie InRelease [6129 B]
Get:5 https://packages.sury.org/php trixie/main amd64 Packages [287 kB]
Fetched 293 kB in 0s (1109 kB/s)

Now confirm where PHP 8.2 will come from, because on Debian 12 two archives offer the same branch.

apt-cache policy php8.2

On Debian 13 the only candidate is Sury’s build, and the version string carries the 21 October 2026 snapshot date:

php8.2:
  Installed: (none)
  Candidate: 8.2.35-1+0~20261021.94+debian13~1.gbp8bff78
  Version table:
     8.2.35-1+0~20261021.94+debian13~1.gbp8bff78 500
        500 https://packages.sury.org/php trixie/main amd64 Packages

Debian 12 lists three versions: Sury’s build with debian12 in the suffix as the candidate, Debian’s patched 8.2.35-1~deb12u1 from bookworm-security, and the unpatched 8.2.32-1~deb12u1 from bookworm main. Sury’s version sorts highest, so apt takes it. If you skipped the repository and your candidate reads 8.2.32, the bookworm-security suite is missing from your sources and apt will install the vulnerable build.

Install PHP 8.2 packages

Install the php8.2 package. On a fresh server it pulls in Apache with libapache2-mod-php8.2 and starts Apache straight away, which suits an Apache host. If you plan to run Nginx, let it happen anyway: the Nginx section below stops and disables Apache.

sudo apt install -y php8.2

Debian 13’s apt prints the new summary format. Look for apache2 and libapache2-mod-php8.2 in the dependency list:

Installing:
  php8.2

Installing dependencies:
  adduser                  libgdbm6t64             perl
  apache2                  libgpm2                 perl-modules-5.40
  apache2-bin              libjansson4             php-common
  apache2-data             liblua5.4-0             php8.2-cli
  apache2-utils            libncursesw6            php8.2-common
  libapache2-mod-php8.2    libperl5.40             php8.2-opcache
  libapr1t64               libproc2-0              php8.2-readline
  libaprutil1-dbd-sqlite3  libsodium23             procps
  libaprutil1-ldap         libtext-charwidth-perl  psmisc
  libaprutil1t64           libtext-wrapi18n-perl   sensible-utils
  libargon2-1              libxml2                 ssl-cert
  libedit2                 media-types             ucf
  libgdbm-compat4t64       netbase

Summary:
  Upgrading: 0, Installing: 39, Removing: 0, Not Upgrading: 0
  Download size: 17.5 MB

Check the version of PHP the command line now runs. On a server with several branches installed, php points at whichever one Debian’s alternatives system picked, so this is also how you see which branch scripts and cron jobs will use.

php -v

The first line names the release and the build date. Debian 12 prints the same 8.2.35 release with a slightly different build time:

PHP 8.2.35 (cli) (built: Oct 21 2026 08:20:13) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.2.35, Copyright (c) Zend Technologies
    with Zend OPcache v8.2.35, Copyright (c), by Zend Technologies

Treat the version line as a hint only. Debian’s own packages can carry a security fix without changing the upstream number, so the next check reads the package itself.

Check PHP 8.2 for PHP2Shell (CVE-2026-109618 and CVE-2026-109619)

PHP2Shell is a pair of critical remote code execution bugs in PHP itself, CVE-2026-109618 and CVE-2026-109619, which php.net scored CVSS v4.0 9.2 in its 20 October pre-announcement. Upstream fixed both on 21 October 2026 in PHP 8.2.35, alongside 8.5.12, 8.4.27 and 8.3.36. Both sources in this guide already ship the fix for 8.2: Sury’s 8.2.35 build on Debian 13 and 12, and Debian 12’s own 8.2.35-1~deb12u1 from bookworm-security. The full write-up is in the PHP security advisory.

The changelog is the reliable check, because the packager names the CVEs in the entry that fixes them:

zgrep -E 'CVE-2026-10961[89]' /usr/share/doc/php8.2-common/changelog.Debian.gz

Sury’s build prints this line on both releases, Debian 13 and Debian 12.

   + (CVE-2026-109618, CVE-2026-109619): Avoid state corruption via

Debian 12’s own package words it differently, and either line means the server is patched. No output at all means the installed build is older than the fixed one:

    + Fix CVE-2026-109618 and CVE-2026-109619: State corruption via bailout.

Some minimal and container images strip /usr/share/doc, so the changelog is not there to read. This check compares the installed version with the fixed one and needs no documentation files. It assumes php8.2-common is installed: on a server without it, dpkg-query prints no packages found matching php8.2-common and the line falls through to a misleading VULNERABLE.

dpkg --compare-versions "$(dpkg-query -W -f='${Version}' php8.2-common)" ge 8.2.35 && echo patched || echo VULNERABLE

Our fresh installs printed patched, and the Debian 12 server still on 8.2.32 printed the other answer:

VULNERABLE

A fresh install from this guide can stop here and move on to the modules. On an existing server that came back vulnerable, upgrade every PHP 8.2 package. The Apache module and the embed library start with lib, so the glob misses them and they are named explicitly. Apt skips any of them that are not installed:

sudo apt update
sudo apt install -y --only-upgrade 'php8.2*' libapache2-mod-php8.2 libphp8.2-embed

The upgrade alone is not enough. On our Debian 12 test system the package restarted php8.2-fpm by itself, but Apache kept its PID and kept serving the old mod_php build: a request right after the upgrade still answered 8.2.32 apache2handler. Restart the PHP handler you actually use. With PHP-FPM, whether behind Nginx or Apache, that is the FPM service.

sudo systemctl restart php8.2-fpm

With mod_php, restart Apache, and if both are installed, run both restarts. Long-running PHP CLI workers, such as queue consumers under supervisor or their own systemd unit, hold the old code in memory too, so restart those services as well:

sudo systemctl restart apache2

Now prove the running service is newer than the package on disk. This prints the last time PHP 8.2 was installed or upgraded, including rotated logs:

zgrep -hE ' (install|upgrade) php8.2-(common|fpm):' /var/log/dpkg.log* | sort | tail -1

On the Debian 12 test server it showed the move from 8.2.32 to 8.2.35:

2026-10-22 19:30:41 upgrade php8.2-common:amd64 8.2.32-1~deb12u1 8.2.35-1~deb12u1

Compare it with the moment the restarted service came up. Swap apache2 for php8.2-fpm on an FPM server:

systemctl show -p ActiveEnterTimestamp apache2

The timestamp has to be later than the dpkg line, as it is here:

ActiveEnterTimestamp=Thu 2026-10-22 19:31:01 UTC

Finally request a PHP page through the web server, such as the two-line test page from the Nginx or Apache section below. After the restart our Debian 12 test system answered 8.2.35 apache2handler, and an FPM site reports 8.2.35 fpm-fcgi. The CLI cannot tell you this, because php -v starts a new process and always shows the package on disk.

Install PHP 8.2 extensions

PHP 8.2 extensions are libraries that add functionality PHP does not have natively, and each one is its own php8.2- package, so you install only what your application needs. This line installs the cli,zip,mysql,bz2,curl,mbstring,intl,common modules, which covers most database-backed web applications.

sudo apt install -y php8.2-{cli,zip,mysql,bz2,curl,mbstring,intl,common}

Any other module can be installed using the command syntax:

sudo apt install -y php8.2-<extension-name>

Replace <extension-name> with the module you need, for example php8.2-intl.

Use PHP 8.2 with Nginx or Apache

You can use PHP with either Nginx or Apache web server to create your dynamic and interactive web pages. Pick one per server: both want port 80, and both paths below end with the same test page so you can see which PHP handler answered.

Using PHP 8.2 with Nginx

With Nginx, PHP code is typically executed by a separate process, such as PHP-FPM (FastCGI Process Manager). PHP-FPM is a daemon that listens for incoming PHP requests and runs them in a separate process. Nginx acts as a reverse proxy, forwarding incoming requests to PHP-FPM to be executed.

The php8.2 package left Apache running on port 80, and Nginx cannot bind the port while Apache holds it. Stop and disable Apache first:

sudo systemctl disable --now apache2

Install Nginx and the FPM package. Debian starts both services as part of the install:

sudo apt install -y nginx php8.2-fpm

Create the document root for the site first. Nginx starts without it, but every request then answers 404, which looks like a PHP problem when it is not.

sudo mkdir -p /var/www/mysite

The old version of this guide pasted the server block into /etc/nginx/nginx.conf. Give the site its own file instead, so package upgrades never touch it and the default site stays separate:

sudo vim /etc/nginx/sites-available/mysite

Paste the block below and change server_name to your domain. The fastcgi_pass line points at the PHP 8.2 FPM socket:

server {
    listen 80;
    server_name mysite.example.com;
    root /var/www/mysite;
    index index.php index.html;

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
    }
}

Enable the site and validate the configuration before any reload, because a reload with a broken file is refused and the reason is easy to miss.

sudo ln -s /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled/
sudo nginx -t

Both lines must report success. A failed test leaves the running configuration untouched, and the error names the file and line to fix:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Reload Nginx so it picks up the new site.

sudo systemctl reload nginx

Check that both services are up before testing a page, so a failed request later points at the configuration and not at a stopped daemon.

systemctl is-active nginx php8.2-fpm

Two lines of active mean Nginx and FPM are serving. Anything else, such as failed, sends you to journalctl -u nginx or journalctl -u php8.2-fpm for the reason:

active
active

Create a test page that prints the PHP version and the handler that ran it:

sudo vim /var/www/mysite/index.php

Add these two lines.

<?php
echo PHP_VERSION, " ", PHP_SAPI, "\n";

Request it through Nginx. The Host header lets you test before DNS points at the server:

curl -s -H "Host: mysite.example.com" http://127.0.0.1/

Our Debian 13 server answered with the patched release and the FPM handler:

8.2.35 fpm-fcgi

A 502 response means Nginx cannot reach the socket, so check that php8.2-fpm is running. The PHP source coming back as plain text means the location block did not match, usually because the site file is not linked into sites-enabled.

Using PHP 8.2 with Apache

With Apache, PHP code is typically executed by the mod_php module, which loads PHP into Apache itself. The mod_php is an Apache module responsible for processing PHP code directly within the Apache process.

If you installed the php8.2 package earlier, Apache and mod_php are already in place, and this line just marks them as manually installed. Run it anyway on a server that only has the CLI packages:

sudo apt install -y apache2 libapache2-mod-php8.2

Next enable mod_php module which allows Apache to process PHP code:

sudo a2enmod php8.2

The package enables the module and switches Apache to the prefork MPM on install, so on our Debian 12 test system the command only confirmed it:

Considering dependency mpm_prefork for php8.2:
Considering conflict mpm_event for mpm_prefork:
Considering conflict mpm_worker for mpm_prefork:
Module mpm_prefork already enabled
Considering conflict php5 for php8.2:
Module php8.2 already enabled

Restart Apache web server so a running instance loads the module.

sudo systemctl restart apache2

Create the same test page in Apache’s default document root.

sudo vim /var/www/html/info.php

Add the two lines again.

<?php
echo PHP_VERSION, " ", PHP_SAPI, "\n";

Then request it.

curl -s http://127.0.0.1/info.php

The handler name proves mod_php ran the request.

8.2.35 apache2handler

Delete the test page once you have seen the answer, because it tells anyone who finds it which PHP build you run.

Move Apache to PHP-FPM and remove old PHP

mod_php ties PHP to the prefork MPM, one process per connection. PHP-FPM lets Apache use the event MPM and restarts separately from the web server, which is what most Apache hosts should run. Order matters when you switch: enable the FPM side first and drop mod_php last, so a failure halfway never leaves Apache serving your PHP source as plain text. On our Debian 12 test system this ran as one line:

sudo apt install -y php8.2-fpm && sudo a2enmod proxy_fcgi setenvif && sudo a2enconf php8.2-fpm && sudo a2dismod php8.2 mpm_prefork && sudo a2enmod mpm_event && sudo systemctl restart apache2

Confirm Apache is on the event MPM now.

sudo a2query -M

It must print event. If it still says prefork, mod_php is loaded and the switch did not finish:

event

Request the test page again with curl -s http://127.0.0.1/info.php, and the handler now reads fpm-fcgi:

8.2.35 fpm-fcgi

If you are moving up from an older branch such as PHP 8.1 under FPM, disable its Apache configuration so requests go to the 8.2 pool. Our test system never had the 8.1 configuration enabled, so there the command only printed Conf php8.1-fpm already disabled:

sudo a2disconf php8.1-fpm

Once your sites answer from 8.2, remove the PHP 8.1 packages. Quote the glob so the shell passes it to apt untouched, and add libapache2-mod-php8.1 to the line if that module was installed, since the glob does not match it:

sudo apt purge -y 'php8.1*'

On our test server apt removed the six 8.1 packages, php -v kept answering 8.2.35, and both apache2 and php8.2-fpm stayed active. Check the same three things on yours before you call the switch finished.

Recommended books to read:

Conclusion

PHP 8.2 is now running from a patched build on Debian 13 or 12, behind Nginx or Apache. Upstream security support for the branch ends on 31 December 2026, so treat this install as a bridge: rerun the changelog check after every PHP security release, restart the PHP handler each time, and move your applications to a newer branch before the end of the year, when 8.2 stops getting fixes from php.net.

Keep reading

Configure Samba File Share on Debian 13 / 12 Debian Configure Samba File Share on Debian 13 / 12 Backup and Restore Linux Systems with Timeshift Debian Backup and Restore Linux Systems with Timeshift Install KVM on Debian 13 / Debian 12: Complete Guide KVM Install KVM on Debian 13 / Debian 12: Complete Guide Install HestiaCP on Ubuntu 26.04/24.04 and Debian 13/12 Web Hosting Install HestiaCP on Ubuntu 26.04/24.04 and Debian 13/12 Patch PHP2Shell (CVE-2026-109618) on Debian 13 / 12 Debian Patch PHP2Shell (CVE-2026-109618) on Debian 13 / 12 Install Node.js 18 on Ubuntu / Debian / Linux Mint Debian Install Node.js 18 on Ubuntu / Debian / Linux Mint

Leave a Comment

Press ESC to close