Install a LAMP stack on Debian 13 and Debian 12: Apache2, PHP 8, MariaDB and phpMyAdmin
Apache2, PHP 8, MariaDB and phpMyAdmin on Debian 13 or Debian 12: the complete setup step by step, including hardening and troubleshooting.
Do you want to install Apache2, PHP 8, a MySQL database and phpMyAdmin, in other words a complete LAMP stack, on your Debian 13 (Trixie) or Debian 12 (Bookworm) server? This guide takes you through the whole setup step by step, from the web server to a secured database login.
Important note about the database server: Debian does not ship a mysql-server package, in no current release. On Debian you always install MariaDB. MariaDB grew out of MySQL, speaks the same protocol and the same query language, and tools such as phpMyAdmin or the PHP extension php-mysql work with it unchanged. So whenever this guide mentions MySQL databases, MariaDB is what runs underneath.
Requirements
You need SSH access with root privileges (or a user with sudo permissions) on a current Debian. Debian 13 "Trixie" and Debian 12 "Bookworm" are recommended. Debian 10 reached its end of support in June 2024 and no longer receives regular security updates, which makes it unsuitable for production use.
Start by refreshing the package list and installing the available updates:
apt update
apt upgrade -y
Install Apache2 and the base packages
Install the web server together with the helper packages you will need in the following steps:
apt install -y nano curl wget unzip ca-certificates apt-transport-https lsb-release gnupg apache2
Then open http://<your-server-IP>/ in your browser. If the Apache2 default page shows up, the web server is already running.
Install PHP 8
Which PHP version you get from the package sources depends on your distribution. That is why a command with a fixed version number such as apt install php8.4 fails on Debian 12, where only PHP 8.2 is available. This overview shows the version shipped by each release (as of July 2026):
| Distribution | PHP version from the package sources |
|---|---|
| Debian 13 | PHP 8.4 |
| Debian 12 | PHP 8.2 |
| Debian 11 | PHP 7.4 |
For that reason, use the meta packages without a version number. They automatically pull in the version that matches your distribution and behave the same way on every Debian:
apt install -y php php-cli php-common php-curl php-gd php-intl php-mbstring php-mysql php-opcache php-readline php-xml php-zip php-bz2 libapache2-mod-php
If you prefer to run PHP through FPM instead of the Apache module, the same principle applies. Here too the meta package gets you there reliably, while versioned names such as php8.4-fpm only exist on Debian 13:
apt install -y php-fpm
To see which version was actually installed, run:
php -v
If you need a version that your Debian release does not provide (PHP 8.4 on Debian 12, for example), add the PHP repository maintained by Ondřej Surý. The signing key is installed through its own keyring package and is tied to exactly this repository in the sources line via signed-by=:
curl -sSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb
dpkg -i /tmp/debsuryorg-archive-keyring.deb
echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" | tee /etc/apt/sources.list.d/php.list
apt update
The once common route via apt-key add has been deprecated since Debian 11 and should no longer be used, because a key stored that way would be valid for every package source on the system.
Only this package source makes the versioned package names available across all Debian releases. After that you can install the PHP version you want together with the usual extensions:
apt install -y php8.4 php8.4-cli php8.4-common php8.4-curl php8.4-gd php8.4-intl php8.4-mbstring php8.4-mysql php8.4-opcache php8.4-readline php8.4-xml php8.4-zip php8.4-bz2 libapache2-mod-php8.4
If several PHP versions end up running side by side, define which one Apache2 should use:
a2dismod php8.2
a2enmod php8.4
systemctl restart apache2
Use php -v to check which version is active on the command line.
Install and secure MariaDB as the database server
As mentioned at the beginning, there is no mysql-server package on Debian. The database server is called MariaDB here, and the release depends on your Debian version:
| Distribution | Database server from the package sources |
|---|---|
| Debian 13 | MariaDB 11.8 |
| Debian 12 | MariaDB 10.11 |
| Debian 11 | MariaDB 10.5 |
Install the server and start the hardening step right after that:
apt install -y mariadb-server mariadb-client
mysql_secure_installation
The script asks several questions. Current MariaDB versions first ask whether to switch to unix_socket authentication. That option makes sense, because the database user root can then only log in as the system user root. Answer y to all remaining questions (remove anonymous users, disallow remote login for root, drop the test database, reload the privileges). Also set a strong database password.
Then check whether the service is running:
systemctl status mariadb
Install phpMyAdmin
You have two options for phpMyAdmin. The phpmyadmin package is part of the package sources in every current Debian version and can be installed in a single step:
apt install phpmyadmin -y
Convenient, but the version often lags behind what the developers currently ship. If you want the latest release, download phpMyAdmin directly instead. That is exactly the route the following steps take:
cd /usr/share && wget https://www.phpmyadmin.net/downloads/phpMyAdmin-latest-all-languages.zip -O phpmyadmin.zip && unzip phpmyadmin.zip && rm phpmyadmin.zip && mv phpMyAdmin-*-all-languages phpmyadmin && chmod -R 0755 phpmyadmin
Next, create the configuration file:
cp /usr/share/phpmyadmin/config.sample.inc.php /usr/share/phpmyadmin/config.inc.php
phpMyAdmin needs a random value as its blowfish_secret, which is used to encrypt the session data in the cookie. Generate such a value:
openssl rand -base64 32
Then enter it in the configuration file:
nano /usr/share/phpmyadmin/config.inc.php
The relevant line reads:
$cfg['blowfish_secret'] = 'ENTER_THE_GENERATED_VALUE_HERE';
Connect phpMyAdmin to Apache2
Create a dedicated Apache2 configuration file for phpMyAdmin:
nano /etc/apache2/conf-available/phpmyadmin.conf
The contents for phpmyadmin.conf:
#PHPMyAdmin Apache2 configuration
Alias /phpmyadmin /usr/share/phpmyadmin
<Directory /usr/share/phpmyadmin>
Options SymLinksIfOwnerMatch
DirectoryIndex index.php
</Directory>
<Directory /usr/share/phpmyadmin/templates>
Require all denied
</Directory>
<Directory /usr/share/phpmyadmin/libraries>
Require all denied
</Directory>
<Directory /usr/share/phpmyadmin/setup/lib>
Require all denied
</Directory>
Enable the configuration, reload Apache2 and create the temporary directory with the right permissions:
a2enconf phpmyadmin && systemctl reload apache2 && mkdir -p /usr/share/phpmyadmin/tmp/ && chown -R www-data:www-data /usr/share/phpmyadmin/tmp/
Create a database user
Log in to the database as the system user root:
mysql -u root
Now create your own database user. Do not use "root", "admin", "user" or "username" as the name, pick a user name of your own and a long, random password:
#IMPORTANT! REPLACE USER AND PASSWORD WITH YOUR OWN CREDENTIALS.
CREATE USER 'USER'@'localhost' IDENTIFIED BY 'PASSWORD';
GRANT ALL PRIVILEGES ON *.* TO 'USER'@'localhost' WITH GRANT OPTION;
FLUSH PRIVILEGES;
For individual applications you should additionally create users with limited privileges that may only access their own database:
CREATE DATABASE myapp;
CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'PASSWORD';
GRANT ALL PRIVILEGES ON myapp.* TO 'appuser'@'localhost';
FLUSH PRIVILEGES;
You can leave the database console at any time with:
exit
Test the LAMP stack
Open phpMyAdmin at http://<your-server-IP>/phpmyadmin and log in with the user you just created. If the login works, Apache2, PHP and MariaDB are working together correctly.
You can also verify the PHP integration with a short test file:
echo "<?php phpinfo();" > /var/www/html/info.php
After opening http://<your-server-IP>/info.php, make sure you delete the file again, because it reveals details about your server configuration:
rm /var/www/html/info.php
Security notes for production use
A freshly installed LAMP stack is not secured yet. You should add these points:
- phpMyAdmin is a popular target. Restrict access to your own IP address by using
Require ip 203.0.113.10in the Directory block instead of leaving it open to everyone. - Set up HTTPS so that passwords are not transmitted in clear text. Without encryption, your credentials can be read on the way to the server.
- Keep phpMyAdmin up to date. Because you installed it manually,
apt upgradedoes not update it automatically. - Create regular database backups and store them off the server.
Common errors and how to fix them
The browser downloads the PHP file instead of running it: The PHP module is not active in Apache2. Enable it with a2enmod php8.4 and restart the web server with systemctl restart apache2.
phpMyAdmin reports "The configuration file now needs a secret passphrase": The value for blowfish_secret is missing in config.inc.php. Generate it with openssl rand -base64 32 and enter it there.
"Access denied for user" when logging in to phpMyAdmin: After mysql_secure_installation the database user root normally uses unix_socket authentication and therefore cannot log in through the web interface. Use the additional user you created.
Apache2 no longer starts after a configuration change: Check the configuration with apachectl configtest, the output names the file and line of the error.
Frequently asked questions
Why does Debian install MariaDB instead of MySQL?
Which PHP version does Debian ship?
How do I get a newer PHP version than the one Debian provides?
Why does the browser download the PHP file instead of running it?
Why can I not log in to phpMyAdmin as root?
2023-2026 KernelHost GmbH. All rights reserved. This guide is protected by copyright. Republishing it on other websites, in whole, in part or in edited form, is not permitted without our written consent. Quoting with a source credit and a link is expressly welcome.

