Header Ads

How To Install Linux, Nginx, MariaDB, PHP 7.2 (LEMP) Stack on Debian 9


Please find the updated working guide for LEMP stack here : https://dev.slickalpha.blog/2019/11/installing-lemp-stack-on-debian-buster.html

This guide includes all commands and codes that can be executed on a Linux terminal (console) to install a Nginx server (LEMP 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 LEMP 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

4. This guide uses putty for terminal.

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 LEMP 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

Update /etc/hosts (replace example.com and replace x.x.x.x [ipv4] and y:y::y:y:y:y [ipv6] with ip found on your vps dashboard).

nano /etc/hosts

x.x.x.x xhostname.example.com xhostname
y:y:y:y:y:y:y xhostname.example.com xhostname

Check Hostname


hostname -f

Set Localtime

dpkg-reconfigure tzdata

Install Nginx

apt update && apt upgrade

apt install nginx

Edit nginx.conf and uncomment "server_names_hash_bucket_size 64;"

nano /etc/nginx/nginx.conf

Adding package sources (For PHP 7.x only)

Note : PHP 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 && apt upgrade

Installing 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. Optionally you can also try the latest PHP 7.3 instead. Below are few general extensions, add more php packages as required. 

apt install php7.2-cli php7.2-cgi php7.2-fpm php7.2-mysql php7.2-json php7.2-curl php7.2-mbstring php7.2-zip

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

Set 1 for session.referer_check

Set 1 for session.cookie_httponly

Set 128 for session.sid_length

Set 6 for session.sid_bits_per_character

Set 1 for session.use_strict_mode

Set 1 for session.cookie_secure

For more session security settings visit the links below



Set as default and restart fpm to conclude

update-alternatives --set php /usr/bin/php7.2

systemctl restart php7.2-fpm

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)


Check status of MariaDB

systemctl status mariadb

Create server block for first site (Replace example.com)

apt update

nano /etc/nginx/sites-available/example.com

Replace block contents with following and save file. (Replace example.com)

server { 

     listen 80; 
     listen [::]:80; 
     server_name example.com www.example.com;
     root /var/www/example.com/site; index index.php index.html;
     location / { 
       try_files $uri $uri/ =404; 
     location ~ \.php$ {       

       include snippets/fastcgi-php.conf;
       fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
     location ~ /\.ht { 
       deny all; 

     location ~ /.well-known/acme-challenge/ {
       allow all;

Create directories for first site (replace example.com)

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

Enable the site

ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled

Check for errors & restart nginx

nginx -t

systemctl restart nginx

Create a php page to test (optional) (replace example.com)

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

     echo "Welcome to Slickalpha";

Restart Nginx

systemctl restart nginx

Repeat the three step procedure to create multiple sites.

  1. Create server block for site
  2. Create directories for site
  3. Enable the server block
Install certbot client to obtain ssl from letsencrypt.com

apt install certbot

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 -w /var/www/example.com/site -d example.com 

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

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

To get certificates for sub domains (optional)

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

Use --rsa-key-size 3072 parameter to change key size

Note down the path, similar to '/etc/letsencrypt/live/example.com/'

Check for four files having placed in /etc/letsencrypt/archive with 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 default server block to return 404

rm /etc/nginx/sites-available/default

nano /etc/nginx/sites-available/default

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 404;
    location ~ /\.ht {

        deny all;

Restart Nginx

systemctl restart nginx

To increase security, generate DH bit group

openssl dhparam -out /etc/ssl/certs/dhparam.pem 3072

https://en.wikipedia.org/wiki/Key_size#Asymmetric_algorithm_key_lengths -> "RSA claims that 1024-bit [asymmetric] keys are likely to become crackable some time between 2006 and 2010 and that 2048-bit keys are sufficient until 2030. An RSA key length of 3072 bits should be used if security is required beyond 2030. NIST key management guidelines further suggest that 15360-bit [asymmetric] RSA keys are equivalent in strength to 256-bit symmetric keys."

Create config snippet to point SSL key and cert (replace example.com)

nano /etc/nginx/snippets/ssl-example.com.conf

Add the following lines and save the file

ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

Create config snippet with encryption params

nano /etc/nginx/snippets/ssl-params.conf

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver valid=300s;
resolver_timeout 5s;
# Preloading HSTS is disabled. Uncomment to includes the
# the "preload" directive if you understand the implications.
#add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;

ssl_dhparam /etc/ssl/certs/dhparam.pem;

Take backup of config (replace example.com)

cp /etc/nginx/sites-available/example.com /etc/nginx/sites-available/example.com.bak

Edit server blocks for ssl-port 443

nano /etc/nginx/sites-available/example.com

server {

    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;

server {

    listen 443 ssl;

    listen [::]:443 ssl;
    include snippets/ssl-example.com.conf;
    include snippets/ssl-params.conf;

    server_name example.com www.example.com;
    root /var/www/example.com/site; index index.php index.html;
    location / { 
       try_files $uri $uri/ =404; 
    location ~ \.php$ {       

       include snippets/fastcgi-php.conf;
       fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
    location ~ /\.ht { 
       deny all; 


Check Nginx for errors & restart

nginx -t

systemctl restart nginx

Check ssl-rating for A+

In your browser:


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/example.com

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

chmod -R 755 /var/www

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

Note: ChrootDirectory is the one that is writable only by root (usually, one less than user directory)

Restart ssh to confirm changes

service ssh restart

apt update

systemctl restart nginx

Try authenticating using Filezilla to confirm access

Install Cron for automated updates

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 nginx

Install Iptables for network security

apt --reinstall install iptables

Flush existing (iptables for ipv4 and ip6tables for ipv6)

iptables -F

ip6tables -F

Note: After adding the following rules do not use the above command to flush instead use the following command. 

iptables-save | awk '/^[*]/ { print $1 } /^:[A-Z]+ [^-]/ { print $1 " ACCEPT" ; } /COMMIT/ { print $0; }' | iptables-restore

ip6tables-save | awk '/^[*]/ { print $1 } /^:[A-Z]+ [^-]/ { print $1 " ACCEPT" ; } /COMMIT/ { print $0; }' | ip6tables-restore

To show status

iptables -S

ip6tables -S


iptables --list


iptables -L -n

Create iptables script as shell script (replace x.x.x.x with your ipv4 address)

Note : the following rules do not guarantee anything, but provides basic protection from attacks.

Ports : remove port numbers or lines associated, to disable those ports
22 - SSH
80 - HTTP
443 - HTTPS
25, 465 - SMTP
587 - SMTPS
110 - POP3
995 - POP3S
143 - IMAP
993 - IMAPS

nano iptables.sh

iptables -P INPUT DROP
iptables -P FORWARD DROP

ip6tables -P INPUT DROP
ip6tables -P FORWARD DROP
ip6tables -P OUTPUT ACCEPT

iptables -A INPUT -p tcp --tcp-flags ALL NONE -j DROP

iptables -4 -A INPUT -s ! -i lo -j DROP
ip6tables -6 -A INPUT -s ::1/128 ! -i lo -j DROP

iptables -4 -A INPUT -m addrtype --dst-type BROADCAST -j DROP
iptables -4 -A INPUT -m addrtype --dst-type MULTICAST -j DROP
iptables -4 -A INPUT -m addrtype --dst-type ANYCAST -j DROP
iptables -4 -A INPUT -d -j DROP
iptables -A INPUT -p tcp ! --syn -m state --state NEW -j DROP
iptables -A INPUT -p tcp --tcp-flags ALL ALL -j DROP

iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
ip6tables -A INPUT -m conntrack --ctstate INVALID -j DROP

iptables -A INPUT -i lo -j ACCEPT
ip6tables -A INPUT -i lo -j ACCEPT

iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
ip6tables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

iptables -N SSHBRUTE
iptables -A SSHBRUTE -m recent --name SSH --set
iptables -A SSHBRUTE -m recent --name SSH --update --seconds 300 --hitcount 10 -m limit --limit 1/second --limit-burst 100 -j LOG --log-prefix "iptables[SSH-brute]: "
iptables -A SSHBRUTE -m recent --name SSH --update --seconds 300 --hitcount 10 -j DROP
iptables -A SSHBRUTE -j ACCEPT
ip6tables -N SSHBRUTE
ip6tables -A SSHBRUTE -m recent --name SSH --set
ip6tables -A SSHBRUTE -m recent --name SSH --update --seconds 300 --hitcount 10 -m limit --limit 1/second --limit-burst 100 -j LOG --log-prefix "iptables[SSH-brute]: "
ip6tables -A SSHBRUTE -m recent --name SSH --update --seconds 300 --hitcount 10 -j DROP
ip6tables -A SSHBRUTE -j ACCEPT

iptables -N ICMPFLOOD
iptables -A ICMPFLOOD -m recent --set --name ICMP --rsource
iptables -A ICMPFLOOD -m recent --update --seconds 1 --hitcount 6 --name ICMP --rsource --rttl -m limit --limit 1/sec --limit-burst 1 -j LOG --log-prefix "iptables[ICMP-flood]: "
iptables -A ICMPFLOOD -m recent --update --seconds 1 --hitcount 6 --name ICMP --rsource --rttl -j DROP
ip6tables -N ICMPFLOOD
ip6tables -A ICMPFLOOD -m recent --set --name ICMP --rsource
ip6tables -A ICMPFLOOD -m recent --update --seconds 1 --hitcount 6 --name ICMP --rsource --rttl -m limit --limit 1/sec --limit-burst 1 -j LOG --log-prefix "iptables[ICMP-flood]: "
ip6tables -A ICMPFLOOD -m recent --update --seconds 1 --hitcount 6 --name ICMP --rsource --rttl -j DROP
ip6tables -A ICMPFLOOD -j ACCEPT

iptables -A INPUT -p tcp --dport 22 --syn -m conntrack --ctstate NEW -j SSHBRUTE
iptables -A INPUT -p tcp --dport 22 --syn -m conntrack --ctstate ESTABLISHED -j ACCEPT
iptables -A INPUT -p tcp -m multiport --dports 80,443 --syn -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
ip6tables -A INPUT -p tcp -m multiport --dports 80,443 --syn -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -p tcp -m multiport --sports 22,80,443 --syn -m conntrack --ctstate ESTABLISHED -j ACCEPT
ip6tables -A OUTPUT -p tcp -m multiport --sports 80,443 --syn -m conntrack --ctstate ESTABLISHED -j ACCEPT

iptables -A INPUT -p tcp -m tcp -m multiport --dports 110,143,465,587,993,995 --syn -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -p tcp -m tcp -m multiport --sport 110,143,465,587,993,995 --syn -m conntrack --ctstate ESTABLISHED -j ACCEPT
ip6tables -A INPUT -p tcp -m tcp -m multiport --dports 110,143,465,587,993,995 --syn -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
ip6tables -A OUTPUT -p tcp -m tcp -m multiport --sport 110,143,465,587,993,995 --syn -m conntrack --ctstate ESTABLISHED -j ACCEPT
iptables -A INPUT -p tcp --dport 25 --syn -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -p tcp --sport 25 --syn -m conntrack --ctstate ESTABLISHED -j ACCEPT
ip6tables -A INPUT -p tcp --dport 25 --syn -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
ip6tables -A OUTPUT -p tcp --sport 25 --syn -m conntrack --ctstate ESTABLISHED -j ACCEPT

iptables -A INPUT -p udp --dport 1900 -j DROP
ip6tables -A INPUT -p udp --dport 1900 -j DROP
iptables -A INPUT -p udp --sport 53 -j DROP
ip6tables -A INPUT -p udp --sport 53 -j DROP

iptables -A INPUT -m limit --limit 1/second --limit-burst 100 -j LOG --log-prefix "iptables[DOS]: "
ip6tables -A INPUT -m limit --limit 1/second --limit-burst 100 -j LOG --log-prefix "iptables[DOS]: "

iptables -4 -A INPUT -p icmp --icmp-type 0  -m conntrack --ctstate NEW -j ACCEPT
iptables -4 -A INPUT -p icmp --icmp-type 3  -m conntrack --ctstate NEW -j ACCEPT
iptables -4 -A INPUT -p icmp --icmp-type 11 -m conntrack --ctstate NEW -j ACCEPT
ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 1   -j ACCEPT
ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 2   -j ACCEPT
ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 3   -j ACCEPT
ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 4   -j ACCEPT
ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 133 -j ACCEPT
ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 134 -j ACCEPT
ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 135 -j ACCEPT
ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 136 -j ACCEPT
ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 137 -j ACCEPT
ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 141 -j ACCEPT
ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 142 -j ACCEPT
ip6tables -6 -A INPUT -s fe80::/10 -p ipv6-icmp --icmpv6-type 130 -j ACCEPT
ip6tables -6 -A INPUT -s fe80::/10 -p ipv6-icmp --icmpv6-type 131 -j ACCEPT
ip6tables -6 -A INPUT -s fe80::/10 -p ipv6-icmp --icmpv6-type 132 -j ACCEPT
ip6tables -6 -A INPUT -s fe80::/10 -p ipv6-icmp --icmpv6-type 143 -j ACCEPT
ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 148 -j ACCEPT
ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 149 -j ACCEPT
ip6tables -6 -A INPUT -s fe80::/10 -p ipv6-icmp --icmpv6-type 151 -j ACCEPT
ip6tables -6 -A INPUT -s fe80::/10 -p ipv6-icmp --icmpv6-type 152 -j ACCEPT
ip6tables -6 -A INPUT -s fe80::/10 -p ipv6-icmp --icmpv6-type 153 -j ACCEPT

ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 128 -j ICMPFLOOD

iptables -A INPUT -p tcp -s x.x.x.x --sport 1024:65535 -d x.x.x.x --dport 3306 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -p tcp -s x.x.x.x --sport 3306 -d x.x.x.x --dport 1024:65535 -m state --state ESTABLISHED -j ACCEPT
iptables -A INPUT -p tcp --syn --dport 80 -m connlimit --connlimit-above 5 --connlimit-mask 32 -j REJECT --reject-with tcp-reset
iptables -A INPUT -p tcp --syn --dport 443 -m connlimit --connlimit-above 15 --connlimit-mask 32 -j REJECT --reject-with tcp-reset
iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -m limit --limit 150/second --limit-burst 160 -j ACCEPT
ip6tables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -m limit --limit 150/second --limit-burst 160 -j ACCEPT

Execute script to add rules to iptables

sh -x iptables.sh

Save the rules and restore it after every boot

iptables-save > /etc/iptables.rules

iptables-restore < /etc/iptables.rules

To restore it automatically after reboot, add to cron jobs

nano /etc/crontab

@reboot  root  iptables-restore < /etc/iptables.rules

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'@'';

Check mysql.user table for limited user and flush. 

SELECT User, Host, plugin FROM mysql.user;



Restart MariaDB to conclude

service mariadb restart

systemctl restart php7.2-fpm

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.7/phpMyAdmin-4.7.7-all-languages.tar.gz

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

Move and rename the directory (replace 'nothingtosee')

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

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

ls -al /usr/share/

rm phpMyAdmin-4.7.7-all-languages.tar.gz

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

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

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' with 32 chars)

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

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

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

Create password to the secure phpmyadmin page (using openssl)

openssl passwd

Enter a password and copy the resulting encrypted pass. And remember the entered pass for authentication

Create a file to store the username and password for phpmyadmin

nano /etc/nginx/pma_pass

Replace 'example_user' with a username & 'encrypted_password' with the encrypted one copied from previous step


Add the phpmyadmin within the working server block of the /example.com config (replace 'nothingtosee' & 'example.com')

nano /etc/nginx/sites-available/example.com

    location ~ ^/nothingtosee/(.+\.php)$ {
        auth_basic "Restricted Content";
        auth_basic_user_file /etc/nginx/pma_pass;
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
        #deny all;

    location ~ ^/nothingtosee/ {
        auth_basic "Restricted Content";
        auth_basic_user_file /etc/nginx/pma_pass;


Restart Nginx to conclude

service nginx restart

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

If there are session based errors, provide full access to sessions folder

sudo chmod 777 /var/lib/php/sessions

Adding Public Key Auth for SSH (for each user)

Install and open putty-keygen.exe

Set bits to 4096 and option as SSH-2 RSA and generate

Enter passphrase, save public key, save private key, keep window open

Open putty application, add private key to 'putty connection > ssh > auth', save the session and open session with the desired user (limited/root) and enter password

Create new directory for auth key

mkdir ~/.ssh; touch ~/.ssh/authorized_keys; chmod 700 ~/.ssh

Edit the file and copy public key directly from keygen and paste (ctrl -v)

nano ~/.ssh/authorized_keys

Increase security

chmod 600 ~/.ssh/authorized_keys

Exit and rerun putty, load saved session of desired user.

Make sure private key matches the saved public key for that user.

Enter passphrase to login

(Optional commands)

Turn off apache2 and reload nginx

service apache2 stop && service nginx reload

To Delete Limited User Account

deluser --remove-home username

To Delete any folders (created by mistake)

rm -r -f /path/

Do a update after every action

apt update

Restart PHP & Nginx & Update

systemctl restart php7.2-fpm nginx

apt-get update

To add default permission to files created using acl

apt install acl

setfacl -R -m d:u::rwx,d:g::rwx,d:o::rwx /var/www/example.com/site/777/

To change mysql root pass (login to mysql first)

mysql -u root -p

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

To check Nginx status and error log

systemctl status nginx.service

or tail -n 20 /var/log/nginx/error.log

or nano /var/log/nginx/error.log

Configuration for gZip compression

nano /etc/nginx/nginx.conf

        gzip on;
        gzip_http_version 1.1;
        gzip_vary on;
        gzip_comp_level 6;
        gzip_proxied any;
        gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript text/x-js;
        gzip_buffers 16 8k;

        gzip_disable "MSIE [1-6]\.(?!.*SV1)";

Configure browser caching (replace example.com)

nano /etc/nginx/sites-available/example.com

Add the following in server block

    location ~*  \.(jpg|jpeg|png|gif|ico|css|js)$ {
        expires 365d;


To install Memcached

apt -y install memcached netcat

Check and update configuration and enable memcached

nano /etc/memcached.conf

systemctl restart memcached

systemctl enable memcached

Check if working

echo "stats settings" | nc localhost 11211

Install php extension for memcached

apt install -y php7.2-memcached

Add memcached to php.ini & restart

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


systemctl restart php7.2-fpm

Text Replacer Tool (This -> That)