Revised guide for Debian 13. Native APT stack, no XAMPP, no third-party repository needed for PHP 8.4.
Target versions (official Debian 13 repositories):
| Component | Approximate version |
|---|---|
| Apache | 2.4.x |
| PHP | 8.4.x |
| MariaDB | 11.8.x |
| phpMyAdmin | 5.2.x |
1. Introduction
1.1. Objective
Set up a local development platform with:
- Apache2
- PHP 8.4
- MariaDB
- phpMyAdmin
Each component is installed and verified before moving on to the next.
1.2. Why Avoid XAMPP?
XAMPP is handy for getting started, but it's not very flexible under Linux: changing PHP versions, integrating services with systemd, or aligning with Debianβs package management practices can become cumbersome.
On Debian 13, PHP 8.4, MariaDB 11.8, and Apache are already in the official repositories. No need for the Sury repository for this stack.
2. Environment Preparation
2.1. Check Your Debian Version
cat /etc/os-release |
You should see VERSION_CODENAME=trixie (Debian 13).
2.2. Update Packages
sudo apt update && sudo apt upgrade -y |
3. Installation of Main Services
3.1. Apache
Apache serves your HTTP pages on port 80 by default.
Install the package first, otherwise systemctl will return Unit apache2.service does not exist:
sudo apt install apache2 -y |
sudo systemctl enable --now apache2 |
sudo systemctl status apache2 --no-pager |
The status should indicate active (running).
Browser verification (default port 80): http://localhost/
If the Apache page doesn't appear:
sudo systemctl start apache2 |
sudo systemctl status apache2 --no-pager |
Useful commands:
sudo systemctl stop apache2 |
sudo systemctl restart apache2 |
sudo systemctl reload apache2 |
3.1.1. Change HTTP port to 90 (optional)
An incalculable number of applications use port 80, which can lead to specific conflicts. If your Apache server starts after an application that already uses it, you may encounter issues.
On Debian, the port is configured in two places: global listening (ports.conf) and each Virtual Host. Do not change only one of them.
A. Global Listening:
sudo nano /etc/apache2/ports.conf |
Replace:
Listen 80 |
with:
Listen 90 |
Leave Listen 443 as is if the SSL section is present (unnecessary for this local HTTP guide).
B. Default Virtual Host:
sudo nano /etc/apache2/sites-available/000-default.conf |
Replace:
<VirtualHost *:80> |
with:
<VirtualHost *:90> |
C. Test the configuration, then reload:
sudo apache2ctl configtest |
sudo systemctl reload apache2 |
configtest should display Syntax OK.
D. Check that port 90 is listening:
ss -tlnp | grep ':90' |
- Browser access: http://localhost:90/
Use
http://, nothttps://. Apache listens to plain HTTP on port 90. If the browser forces HTTPS (Firefox's HTTPS-Only mode, HSTS, etc.), you'll get anSSL_ERROR_RX_RECORD_TOO_LONGerror. In this case: type explicitlyhttp://localhost:90/or temporarily disable HTTPS-Only for localhost.
From now on, all local URLs use port 90 (
http://localhost:90/,http://localhost:90/info.php,http://localhost:90/phpmyadmin).
If you create other vhosts later, also put <VirtualHost *:90> (or the chosen port) in each site file.
3.2. MariaDB
MariaDB replaces MySQL in Debian repositories. The API remains compatible for most PHP projects.
sudo apt install mariadb-server -y |
sudo systemctl enable --now mariadb |
sudo systemctl status mariadb --no-pager |
Secure the installation:
On Debian, MariaDB is already secured by default. The script also displays:
MariaDB is secure by default in Debian. Running this script is useless at best...
We still run it to clearly choose the root authentication mode:
sudo mariadb-secure-installation |
Two options exist. Choose one, note your choice, and follow the instructions for Option A or Option B in the rest of the guide.
| Option A β simple | Option B β safer | |
|---|---|---|
root authentication |
SQL password | unix_socket |
| Account for phpMyAdmin / apps | root + password (no separate dev user) |
dev + password (created right after the script) |
| Terminal admin access | mariadb -u root -p |
sudo mariadb |
Create a dev user? |
No | Yes (mandatory) |
Warning β Option A.
Logging into phpMyAdmin (and possibly other apps) withrootis the simplest path. It's also the least secure: a leaked root password equals access to all databases. This approach is fine for a personal local PC, but itβs less clean and poses more risks. Option B exists precisely for those who prefer never exposingrootto the web.
Option A β root + password (simple)
Responses in mariadb-secure-installation:
| Question | Response |
|---|---|
| Current password for root | Enter (blank) |
| Switch to unix_socket authentication | n |
| Change the root password? | Y β choose a password and note it down |
| Remove anonymous users? | Y |
| Disallow root login remotely? | Y |
| Remove test database? | Y |
| Reload privilege tables? | Y |
Check:
mariadb -u root -p |
If Option A for the rest of the tutorial: use only root + this password (phpMyAdmin, PDO examples, etc.).
Do not create an dev user.
Option B β unix_socket + dev user (more secure)
Responses in mariadb-secure-installation:
| Question | Response |
|---|---|
| Current password for root | Enter (blank) |
| Switch to unix_socket authentication | Y |
| Change the root password? | N |
| Remove anonymous users? | Y |
| Disallow root login remotely? | Y |
| Remove test database? | Y |
| Reload privilege tables? | Y |
Admin terminal (no SQL password for root) :
sudo mariadb |
Why a dev user in Option B?
phpMyAdmin can connect as root when root has an SQL password β that's Option A. In Option B, root is using unix_socket: MariaDB accepts root only if the Linux process is already root (sudo mariadb). Apache / phpMyAdmin run under www-data, so a web login as root fails. You need an SQL user with a password (dev).
Create immediately dev (web/app account for the rest of the tutorial if option B) :
CREATE USER 'dev'@'localhost' IDENTIFIED BY 'change_me_strong_password'; |
GRANT ALL PRIVILEGES ON *.* TO 'dev'@'localhost' WITH GRANT OPTION; |
FLUSH PRIVILEGES; |
EXIT; |
Replace change_me_strong_password with a real password and note it down.
Verify:
mariadb -u dev -p |
If you choose Option B for the rest of the tutorial: phpMyAdmin, PDO, etc. β use dev + this password. Never use root from the web.
More restrictive variant (single database):
CREATE DATABASE mon_projet; |
CREATE USER 'dev'@'localhost' IDENTIFIED BY 'change_me_strong_password'; |
GRANT ALL PRIVILEGES ON mon_projet.* TO 'dev'@'localhost'; |
FLUSH PRIVILEGES; |
EXIT; |
Details of script questions (common to both options)
Enter current password for root
Current root password to allow the script. On a fresh Debian install: often empty β Enter.
Switch to unix_socket authentication
- n β Option A (SQL password).
- Y β Option B (Linux socket /
sudo).
Change the root password?
- Option A: Y + set the password.
- Option B: n (not needed daily with the socket).
Remove anonymous users? β Y (unnecessary user accounts, regardless of mode)
Disallow root login remotely? β Y (root access only locally; "disallow" = prevent remote access)
Remove test database? β Y
Reload privilege tables? β Y
On the machine where this guide was followed live: Option A (
rootsimple, nodev). Option B is documented for those who choose it.
3.3. PHP 8.4
On Debian 13, PHP 8.4 is available natively. No Sury repository is required.
sudo apt install \ |
php8.4 \ |
php8.4-cli \ |
libapache2-mod-php8.4 \ |
php8.4-mysql \ |
php8.4-xml \ |
php8.4-mbstring \ |
php8.4-curl \ |
php8.4-zip \ |
php8.4-sqlite3 \ |
php8.4-gd \ |
php8.4-intl \ |
-y |
Reload Apache to enable the PHP module:
sudo systemctl restart apache2 |
Check the CLI:
php -v |
You should see a line like PHP 8.4.x.
Create a test file:
echo '<?php phpinfo();' | sudo tee /var/www/html/info.php |
Open http://localhost:90/info.php, verify PHP 8.4, then delete the file:
sudo rm /var/www/html/info.php |
Do not leave
info.phpin place outside of a trusted environment as it exposes server configuration.
3.4. phpMyAdmin
Mandatory order: install the package (Β§3.4.1) β verify that the files exist β only then enable Apache (Β§3.4.2).
Without the package, /etc/phpmyadmin/apache.conf does not exist: a ln creates a broken link and a2enconf fails with Conf phpmyadmin does not exist!.
3.4.1. Installation
sudo apt update |
sudo apt install phpmyadmin -y |
During installation (debconf screens):
- Choose the web server: apache2 (Space to check, Tab, Enter).
- Should dbconfig-common configure the phpMyAdmin database? β Yes.
MariaDB is already installed;
dbconfig-commonwill create for you the internal database and user that phpMyAdmin needs. Answer No only if you want to set everything up manually (unnecessary for a simple dev environment). - Set a password for the MySQL/MariaDB user
phpmyadmin(note it; this is not the same account asrootordevfor connecting to the web interface).
Checkpoint β proceed to 3.4.2 only if this is OK:
dpkg -l phpmyadmin | grep '^ii' |
ls -la /etc/phpmyadmin/apache.conf |
- The first command should show
ii phpmyadmin - The second should list the file (not "No files")
If phpmyadmin is not ii, stop and reinstall (sudo apt install phpmyadmin -y) before configuring Apache.
3.4.2. Enable the Apache Configuration
Do this only after completing checkpoint Β§3.4.1.
The package provides /etc/phpmyadmin/apache.conf. Apache should load it via conf-available β a2enconf.
Often, the installer has already created the link. Check:
ls -la /etc/apache2/conf-available/phpmyadmin.conf |
- If the file/link exists and points to
/etc/phpmyadmin/apache.conf, activate and reload only:
sudo a2enconf phpmyadmin |
sudo systemctl reload apache2 |
- Otherwise, create the link (with backup if a file already occupies the name):
# Abort if the package config is missing (do not create a broken symlink) |
test -f /etc/phpmyadmin/apache.conf || { |
echo "ERROR: /etc/phpmyadmin/apache.conf missing β finish Β§3.4.1 first" |
exit 1 |
} |
|
# Keep a copy if a file already occupies that name |
if [ -e /etc/apache2/conf-available/phpmyadmin.conf ]; then |
sudo cp -a /etc/apache2/conf-available/phpmyadmin.conf \ |
/etc/apache2/conf-available/phpmyadmin.conf.bak |
fi |
|
sudo ln -sf /etc/phpmyadmin/apache.conf \ |
/etc/apache2/conf-available/phpmyadmin.conf |
|
sudo a2enconf phpmyadmin |
sudo systemctl reload apache2 |
test -f: blocks if the package is not present (avoids broken symlinks).cp -a: true backup before replacement.ln -sf: link to the package configuration.
3.4.3. Access
URL: http://localhost:90/phpmyadmin
- If option A: login
root+ password frommariadb-secure-installation. Nodev. - If option B: login
dev+ password created in Β§3.2.
A web loginrootfails here because ofunix_socket(in option A,root+ pwd works).
3.4.4. User dev β reminder / repair (option B only)
If option A: skip this section. You remain on root.
If option B: dev was already created in Β§3.2. Here, just in case of forgetting or resetting the password:
sudo mariadb |
CREATE USER 'dev'@'localhost' IDENTIFIED BY 'change_me_strong_password'; |
GRANT ALL PRIVILEGES ON *.* TO 'dev'@'localhost' WITH GRANT OPTION; |
FLUSH PRIVILEGES; |
EXIT; |
Change the password for dev:
ALTER USER 'dev'@'localhost' IDENTIFIED BY 'new_password'; |
FLUSH PRIVILEGES; |
EXIT; |
A dedicated database (recommended for a project, always option B):
CREATE DATABASE mon_projet; |
CREATE USER 'dev'@'localhost' IDENTIFIED BY 'change_me_strong_password'; |
GRANT ALL PRIVILEGES ON mon_projet.* TO 'dev'@'localhost'; |
FLUSH PRIVILEGES; |
EXIT; |
3.4.5 Troubleshooting Connection
Connect as admin:
# Si option A : |
mariadb -u root -p |
|
# Si option B : |
sudo mariadb |
List users:
SELECT User, Host, plugin FROM mysql.user; |
- If option A:
roothas a password plugin β phpMyAdmin =root. - If option B:
rootgenerally hasplugin=unix_socketβ phpMyAdmin =dev.
Change a password:
-- Si option A : |
ALTER USER 'root'@'localhost' IDENTIFIED BY 'new_password'; |
|
-- Si option B : |
ALTER USER 'dev'@'localhost' IDENTIFIED BY 'new_password'; |
FLUSH PRIVILEGES; |
Exit:
EXIT; |
3.4.6 Quick Security (local)
For a local development machine, access via localhost is often sufficient. To restrict further, edit the Apache phpMyAdmin config and limit access, for example:
Require local |
Then:
sudo systemctl reload apache2 |
4 PHP Configuration
4.1 Check Active Version
php -v |
php -m |
4.2 Multiple PHP Versions (optional)
If you install other branches later (e.g., via Sury for PHP 8.5), switch the CLI with:
sudo update-alternatives --display php |
sudo update-alternatives --config php |
For Apache mod_php, enable only one PHP module at a time:
# Example: switch from php8.4 to another installed version |
sudo a2dismod php8.4 |
sudo a2enmod phpX.Y |
sudo systemctl restart apache2 |
Replace phpX.Y with the actual installed version.
4.3 Apache php.ini File
sudo nano /etc/php/8.4/apache2/php.ini |
After modification:
sudo systemctl restart apache2 |
Common local settings:
display_errors = On |
error_reporting = E_ALL |
upload_max_filesize = 64M |
post_max_size = 64M |
memory_limit = 256M |
5 Essential Files
5.1 Apache
| Role | Path |
|---|---|
| Main configuration | /etc/apache2/apache2.conf |
| Listening ports | /etc/apache2/ports.conf |
| Document root | /var/www/html/ |
| Available sites | /etc/apache2/sites-available/ |
| Enabled sites | /etc/apache2/sites-enabled/ |
| Default vhost | /etc/apache2/sites-available/000-default.conf |
| Access logs | /var/log/apache2/access.log |
| Error logs | /var/log/apache2/error.log |
Enable / disable a vhost:
sudo a2ensite nom-du-site.conf |
sudo a2dissite nom-du-site.conf |
sudo systemctl reload apache2 |
5.2 PHP
| Role | Path |
|---|---|
php.ini (Apache) |
/etc/php/8.4/apache2/php.ini |
php.ini (CLI) |
/etc/php/8.4/cli/php.ini |
| Extensions | see php -i | grep extension_dir |
5.3 MariaDB
| Role | Path |
|---|---|
| Server configuration | /etc/mysql/mariadb.conf.d/50-server.cnf |
| Data | /var/lib/mysql/ |
| Error logs | /var/log/mysql/error.log |
5.4 phpMyAdmin
| Role | Path |
|---|---|
| App configuration | /etc/phpmyadmin/config.inc.php |
| Apache configuration | /etc/phpmyadmin/apache.conf |
| Logs | /var/log/apache2/error.log and access.log |
6.Validation Checklist
# Services |
systemctl is-active apache2 |
systemctl is-active mariadb |
|
# Versions |
apache2 -v |
php -v |
mariadb --version |
|
# Ports |
ss -tlnp | grep -E ':90|:3306' |
- http://localhost:90/ responds
- Apache is listening on port 90 (not 80)
-
php -vdisplays 8.4.x - http://localhost:90/phpmyadmin opens
- If option A: phpMyAdmin works with
root+ password (nodev) - If option B: user
devcreated (Β§3.2) and phpMyAdmin works withdev+ password -
info.phphas been deleted
7. Characteristics of Debian 13
- PHP 8.4 is native: no need for the Sury repository or deprecated
apt-key. - MariaDB is at version 11.8, and the security script is called
mariadb-secure-installation. - Apache remains at version 2.4, with familiar tools like
a2enmod,a2ensite, andsystemctl. - This guide uses port HTTP 90 (
ports.conf+ Virtual Host). - MariaDB: Option A = simple
root(nodev); Option B =unix_socket+dev. Option B is available for those who prefer it based on warning A.
8. Possible Next Steps
For local projects with a custom domain name (monprojet.local), configure an Apache Virtual Host pointing to a dedicated folder (outside of /var/www/html if desired), then add the entry in /etc/hosts.
Detailed article here: Set Up an Apache VirtualHost on Debian 13 for Local PHP Projects
Comments
No approved comments yet.
Sign in with a commenter account to post a comment. Sign in.