Header Ads

How to Install LAMP Server (PHP 7.2) on Debian 9 VPS

Please find updated working guide for LAMP stack here : https://dev.slickalpha.blog/2020/05/installing-lamp-stack-on-debian-10-buster.html

This guide includes all commands and codes that can be executed on a Linux terminal (console) to install a LAMP stack on Debian 9 (Stretch), along with PhpMyAdmin and free ssl certificates.

Basic Linux Commands

Print Working Directory


Change Directory (to root)

cd /

List Current Directory

ls -ahl

-a : all the files
-h : human readable
-l : long list

Change Directory (to var)

cd var

Change Directory (to previous)

cd ..

Text Editior (nano)

Prefix a file path or file name with 'nano' and execute command

To save changes on nano, press ctrl+x and type 'y' (without quotes) and press enter. To discard changes type 'n' instead of 'y'.

Temporarily elevate to root privileges

Prefix a command with 'sudo' and execute command

Prerequisites to install LAMP stack on Debian 9

1. Have deployed a Debian 9 image on your new server and have your root password ready

2. Have pointed your domain (example.com) to your server public ip address

3. Have booted your server and is up and running

Tips & Instructions

1. To copy code from here, use ctrl-c, and to paste it on putty, mouse-right-click once on putty

2. Replace all place holders such as 'example.com', 'example_user', 'nothingtosee' and so on with desired strings.

3. From Debian 9 onwards, you can use 'apt' instead of 'apt-get'.

Steps to install LAMP stack on Debian 9 (Stretch)

Set preference to ipv4 (optional)

nano /etc/gai.conf

uncomment "precedence ::ffff:0:0/96 100" by removing preceding pound

Update & Upgrade Linux

apt update && apt upgrade

When prompted, type y and press enter.

Install sudo (if doesn't exist, and optional)

apt install sudo

Set Hostname (replace xhostname)

hostnamectl set-hostname xhostname

Check if Host File exists

nano /etc/hosts

Update /etc/hosts (replace example.com and xhostname. Add ipv4 and ipv6 with ip found on your vps dashboard)

nano /etc/hosts localhost
[your_ipv_4] xhostname.example.com xhostname
[your_ipv_6] xhostname.example.com xhostname

Check Hostname


hostname -f

Set Localtime

dpkg-reconfigure tzdata

Install Apache 2 Server

apt update && apt upgrade

apt install apache2 apache2-doc apache2-utils

Turn off KeepAlive 

nano /etc/apache2/apache2.conf

Edit 'KeepAlive On' to 'KeepAlive Off'

Edit and enable Prefork module

nano /etc/apache2/mods-available/mpm_prefork.conf

Set MaxConnectionsPerChild to a valid number (eg.100) to prevent memory leaks. Edit other values to fine tune apache.

a2dismod mpm_event

a2enmod mpm_prefork

systemctl restart apache2

Disable default apache virtual host 

a2dissite 000-default.conf

Add virtual host configuration and save file (replace example.com)

nano /etc/apache2/sites-available/example.com.conf

<VirtualHost *:80>
    ServerAdmin webmaster@example.com
    ServerName example.com
    ServerAlias www.example.com
    AddType application/x-httpd-php .php
    DocumentRoot /var/www/example.com/site/
    DirectoryIndex index.php index.html index
    ErrorLog /var/www/example.com/logs/error.log
    CustomLog /var/www/example.com/logs/access.log combined

Create directories for your public site and logs (replace example.com)

mkdir -p /var/www/example.com/site

mkdir /var/www/example.com/logs

Enable the site and reload apache (replace example.com)

a2ensite example.com.conf

systemctl reload apache2

Install Maria DB Server

Note: Maria DB is same as MySql, just the name. 

apt -y install mariadb-server mariadb-client

Secure Maria DB Server (set Y for all and set root pass)


Adding package sources (For PHP 7.2 only)

Note : PHP 7.2 Packages are obtained from packages.sury.org

apt install apt-transport-https lsb-release ca-certificates

wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg

echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/php.list

apt update

Install PHP 7.2 with FastCGI

Note : PHP 7.2 does not come with Mcrypt, use 'text replacer' at the bottom of this post to change 7.2 to 7.0 for Mcrypt. 

apt install php7.2-cli php7.2-cgi php7.2-fpm php7.2-mysql php7.2-json php7.2-curl libapache2-mod-php7.2

apt update

Securing PHP : Edit php.ini, and uncomment and set fix_pathinfo to 0

nano /etc/php/7.2/fpm/php.ini

Set 0 and uncomment (remove preceding semicolon) cgi.fix_pathinfo=0

systemctl restart php7.2-fpm

Create a index file to check php working (replace example.com)

nano /var/www/example.com/site/index.php

Add following lines to test

echo "Welcome";

And save file and run it on browser http://example.com

To install phpmyadmin

Get the latest pure binary file (.tar.gz) from https://www.phpmyadmin.net/downloads/

wget https://files.phpmyadmin.net/phpMyAdmin/4.7.6/phpMyAdmin-4.7.6-all-languages.tar.gz

tar xvf phpMyAdmin-4.7.6-all-languages.tar.gz

Move and rename the directory (replace 'nothingtosee')

mv phpMyAdmin-4.7.6-all-languages /usr/share/

mv -T /usr/share/phpMyAdmin-4.7.6-all-languages /usr/share/nothingtosee

ls -al /usr/share/

Create Symbolic link to phpmyadmin (replace 'nothingtosee' & 'example.com')

ln -s /usr/share/nothingtosee /var/www/example.com/site

Visit http://example.com/nothingtosee/ to see phpmyadmin page. 

To setup phpmyadmin config.inc file, rename the sample file (replace 'nothingtosee')

mv /usr/share/nothingtosee/config.sample.inc.php /usr/share/nothingtosee/config.inc.php

Set blowfish_secret and change _host to '' in config file and save (replace 'bf_secret')

nano /usr/share/nothingtosee/config.inc.php

$cfg['blowfish_secret'] = 'bf_secret';

$cfg['Servers'][$i]['host'] = '';

Add password to the phpmyadmin page (replace example_user with username and enter a password for auth)

htpasswd -c -B /etc/apache2/pma_pass example_user

Edit hosts file to reflect addition (replace example.com) and add the following within VirtualHost tag

nano /etc/apache2/sites-available/example.com.conf

<Directory "/var/www/example.com/site/nothingtosee">
    AuthType Basic
    AuthName "Restricted Content"
    AuthUserFile /etc/apache2/pma_pass
    Require valid-user

systemctl restart apache2

Visit http://example.com/nothingtosee/ to see auth. 

Creating MariaDB database for Limited user

Login in to MariaDB (with root pass)

mariadb -u root -p

Create a database for limited user (replace db_name, db_user, db_pass)


CREATE USER 'db_user' IDENTIFIED BY 'db_pass';

GRANT ALL PRIVILEGES ON db_name.* TO 'db_user'@'' IDENTIFIED BY 'db_pass'

Configure mysql.user table for remote access. 

SELECT User, Host, plugin FROM mysql.user;

The database should look like this. If the host or plugin is set different, then update the table to look like this. (replace example_user only)

UPDATE mysql.user SET plugin = 'mysql_native_password' WHERE User='example_user';

DELETE FROM mysql.user WHERE Host = '%';



Restart MariaDB to conclude

service mariadb restart

systemctl restart php7.2-fpm

Adding a limited access SFTP user to access site files

Create a new group (replace example_group)

addgroup --system example_group

Add Limited User Account (replace example_user and enter password for user)

adduser example_user

Mod limited_user to the group with root directory (replace example.com)

usermod -g example_group -d /var/www/example.com/site -s /sbin/nologin example_user

Restrict and give limited user r(4)/w(2)/x(1) permissions

chown -R root:root /var/www/

chmod -R 755 /var/www/example.com/

chown -R example_user:example_group /var/www/example.com/site

chmod -R 775 /var/www/example.com/site

Edit sshd_config

nano /etc/ssh/sshd_config
Comment out following line in sshd_config (add a preceding pound)

#Subsystem sftp /usr/lib/openssh/sftp-server

Add following lines to sshd_config (replace example.com & example_group)

Subsystem sftp internal-sftp

Match Group example_group
ChrootDirectory /var/www/example.com
X11Forwarding no
AllowTcpForwarding no

ForceCommand internal-sftp

Restart ssh to confirm changes

service ssh restart

apt update

systemctl restart apache2

Try authenticating using Filezilla to confirm access

Also try logging into phpmyadmin with the limited user credentials to operate db. 

Install UFW for Firewall

apt install ufw

Edit config file and set 'IPV6=yes' if using ipv6

nano /etc/default/ufw

ufw default deny incoming

ufw default allow outgoing

Allow tcp 22 or 2222 for ssh access

ufw allow 22/tcp

ufw allow www

ufw allow ftp

To update disable and enable (enable turn it on, make sure ssh is enabled)

ufw disable

ufw enable

To check status of firewall

ufw status verbose

Install certbot client to obtain ssl from letsencrypt.com

apt install certbot

Add following block within site config for webroot  (replace example.com)

nano /etc/apache2/sites-available/example.com.conf

    AliasMatch ^/.well-known/acme-challenge/(.*)$ /var/www/example.com/.well-known/acme-challenge/$1
    Alias /.well-known/acme-challenge/ /var/www/example.com/.well-known/acme-challenge/
    <Directory "/var/www/example.com/.well-known/acme-challenge/">
        Options None
        AllowOverride None
        ForceType text/plain
        RedirectMatch 404 "^(?!/\.well-known/acme-challenge/[\w-]{43}$)"

Getting a certificate using webroot and certbot. (Enter an email for updates)

To test a certificate using staging environment before using production environment (optional)

certbot certonly --staging --webroot --agree-tos -d example.com -w /var/www/example.com/

To get a production level certificate using webroot (Limited to 5 attempts/hr)

certbot certonly --webroot --agree-tos -d example.com -w /var/www/example.com/

To get certificates for sub domains (optional)

certbot certonly --webroot --agree-tos -d example.com -d www.example.com -w /var/www/example.com/

Check for four symbolic links in /etc/letsencrypt/live/example.com

·  cert.pem: Your domain's certificate
·  chain.pem: The Let's Encrypt chain certificate
·  fullchain.pem: cert.pem and chain.pem combined
·  privkey.pem: Your certificate's private key

ls -l /etc/letsencrypt/live/example.com

Replace port 80 with 443 on host file and add a redirect for port 80 (replace example.com)

nano /etc/apache2/sites-available/example.com.conf

The final site config file should similar to this, with ssl config (replacing example.com & nothingtosee)

<VirtualHost *:80>
    ServerAdmin webmaster@example.com
    ServerName example.com
    DocumentRoot /var/www/example.com/site/
    Redirect permanent / https://example.com/

<VirtualHost *:443>
    ServerAdmin webmaster@example.com
    ServerName example.com
    AddType application/x-httpd-php .php
    DocumentRoot /var/www/example.com/site/
    DirectoryIndex index.php index.html index
    ErrorLog /var/www/example.com/logs/error.log
    CustomLog /var/www/example.com/logs/access.log combined

    SSLEngine On
    SSLCertificateFile /etc/letsencrypt/live/example.com/cert.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
    SSLCertificateChainFile /etc/letsencrypt/live/example.com/chain.pem
    Header always set Strict-Transport-Security "max-age=15768000"

    RequestHeader append "X-Forwarded-Proto" "https"
    RequestHeader set "X-Forwarded-Ssl" "on"

    <Directory "/var/www/example.com/site/nothingtosee">
        AuthType Basic
        AuthName "Restricted Content"
        AuthUserFile /etc/apache2/pma_pass
        Require valid-user
    AliasMatch ^/.well-known/acme-challenge/(.*)$ /var/www/example.com/.well-known/acme-challenge/$1
    Alias /.well-known/acme-challenge/ /var/www/example.com/.well-known/acme-challenge/
    <Directory "/var/www/example.com/.well-known/acme-challenge/">
        Options None
        AllowOverride None
        ForceType text/plain
        RedirectMatch 404 "^(?!/\.well-known/acme-challenge/[\w-]{43}$)"

SSLProtocol all -SSLv2 -SSLv3
SSLHonorCipherOrder on
SSLCompression off
SSLSessionTickets off

SSLUseStapling on
SSLStaplingResponderTimeout 5
SSLStaplingReturnResponderErrors off
SSLStaplingCache shmcb:/var/run/ocsp(128000)

Enable apache2 ssl and headers module and restart apache

sudo a2enmod ssl

a2enmod headers

systemctl restart apache2

To get a rating on the ssl, visit the following site (replace example.com)


Add 443/https port to ufw firewall

ufw allow https

Delete http if necessary (http pages will not load)

ufw delete allow www

Check status of ufw

ufw status verbose

Install Cron for automated updates

apt update

apt install cron

Restrict all user access. Allow access for example_user (if necessary)

echo ALL /etc/cron.deny

echo example_user /etc/cron.allow

Add following two lines to crontab for letsencrypt ssl auto renewal 

crontab -e

30 1 * * * /usr/bin/certbot renew >> /var/log/le-renew.log

35 1 * * * /bin/
systemctl restart apache2

Note: All commands were tested successfully on a VPS running Debian 9. Write us, if you need support or need an installation. 

Optional Items

To support .htaccess and rewrites within site, add this to the configuration file, at the bottom.(replace example.com)

nano /etc/apache2/apache2.conf

<Directory /var/www/example.com/site>
    Options Indexes FollowSymLinks
    AllowOverride All
    Require all granted

a2enmod rewrite

service apache2 restart

To install Mcrypt (For PHP <=7.0 only)

apt install php7.0-mcrypt php-mbstring php7.0-mbstring php-gettext

phpenmod mcrypt

phpenmod mbstring

systemctl restart php7.0-fpm

service apache2 restart

To Delete Limited User Account

deluser --remove-home username

To Delete any folders (created by mistake)

rm -r -f /path/

To change mysql root pass (use root pass)

mysql -u root -p

ALTER USER 'root'@'localhost' IDENTIFIED WITH 'mysql_native_password' BY 'password';

Text Replacer Tool (This -> That)