/lemp-stack

πŸ€– LEMP w/ PHP7.X & MariaDB on Ubuntu 16/17/18 x64

Primary LanguageShell

Basic installation process of LEMP

Last update: 04/09/2020, tested on Ubuntu 20.04 with PHP7.4

If you are looking for the older versions of the PHP, πŸ‘€ at branches php7.2 or php7.1


πŸ”₯ Looking for cool t-shirts for web developers?
Check out my Devnull Clothing.


Overview

This document is a list of notes when installing several Ubuntu LEMP instances w/ PHP7.4. With some sort of imagination it can be considered as a step-by-step tutorial of really basic installation process of LEMP. I wrote it mainly for myself, but feel free to use it. The LEMP consists of:

  • Nginx
  • PHP7.4 (php-fpm)
  • MariaDB
  • Optional: git, munin, rabbitmq, supervisor, node.js, Let's Encrypt, postfix

Table of Contents

Essentials

Installation script

To automatically install essentials, you can use the πŸ‘‰ startup.sh script by downloading it and calling it with sudo sudo ./startup.sh. The file is deleted automatically.

Manual installation

If you want to have the installation in your hands, follow the manual installation. πŸ‘‡

add new user

adduser admin

allow su without password for this user

echo "admin    ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

try new user

su - admin
exit

add authorized keys for that user

su - admin
mkdir .ssh
nano .ssh/authorized_keys
chmod 700 .ssh/
chmod 600 .ssh/authorized_keys

disable password login for all users

# Optional
echo "PasswordAuthentication no" | sudo tee --append /etc/ssh/sshd_config
sudo systemctl reload sshd

Or disable the password for some users only (admin, user_tld)

# Optional
sudo nano /etc/ssh/sshd_config
> Match User admin,user_tld
>    PasswordAuthentication no
sudo systemctl reload sshd

Fix locale if you are getting "WARNING! Your environment specifies an invalid locale."

sudo echo 'LC_ALL="en_US.UTF-8"' >> /etc/environment
# Log out & in

Set the correct timezone

sudo dpkg-reconfigure tzdata

Configure & Update APT

sudo apt-get -y dist-upgrade ; sudo apt-get -y update ; sudo apt-get -y upgrade
sudo apt-get -y install unattended-upgrades software-properties-common apache2-utils fail2ban

Install security updates automatically

sudo dpkg-reconfigure -plow unattended-upgrades

Install essentials

sudo apt-get -y install mc htop

Replace rm with trash

This is optional but recommended. rm is a dangerous command therefore is recommended to replace it by safer version trash that instead of removing files moving them to a trash. More info here.

$ sudo apt-get -y install trash-cli
$ echo "alias rm='echo \"This is not the command you are looking for. Use <trash> instead.\"; false'" | sudo tee --append

Setup and configure Firewall

Open SSH port only.

sudo ufw allow 22 #OpenSSH
sudo ufw allow 80 #http
sudo ufw allow 443 #https
yes | sudo ufw enable
sudo ufw status

Webserver installation

You can skip steps 1-4 by downloading and running the lemp.sh script:

wget https://raw.githubusercontent.com/lucien144/lemp-stack/master/lemp.sh && chmod u+x lemp.sh
sudo ./lemp.sh

1. Install Nginx

sudo add-apt-repository -y ppa:nginx/development && sudo apt-get update
sudo apt-get -y install nginx

2. Install MariaDB

sudo apt-get -y install mariadb-server # Or MySQL: sudo apt-get install mysql-server
sudo service mysql stop # Stop the MySQL if is running.
sudo mysql_install_db
sudo service mysql start
sudo mysql_secure_installation

3. Install PHP7.4

sudo add-apt-repository -y ppa:ondrej/php && sudo apt-get update
sudo apt-get -y install php7.4

4. Choose and install PHP7.4 modules

sudo apt-cache search php7.4-*
sudo apt-get -y install php7.4-fpm php7.4-curl php7.4-gd php7.4-json php7.4-mysql php7.4-sqlite3 php7.4-pgsql php7.4-bz2 php7.4-mbstring php7.4-soap php7.4-xml php7.4-zip

5. Check the installed PHP version

php -v

6. Configure Nginx

Configure /etc/nginx/nginx.conf

worker_processes auto;
events {
        use epoll;
        worker_connections 1024; # ~ RAM / 2
        multi_accept on;
}

Default vhost

cd /etc/nginx/sites-available
sudo rm default
sudo wget https://raw.githubusercontent.com/lucien144/lemp-stack/master/nginx/sites-available/default
cd /etc/nginx/conf.d
sudo wget https://raw.githubusercontent.com/lucien144/lemp-stack/master/nginx/conf.d/gzip.conf

Setup default settings for all virtual hosts

sudo mkdir -p /etc/nginx/conf.d/server/
cd /etc/nginx/conf.d/server/
sudo wget https://raw.githubusercontent.com/lucien144/lemp-stack/master/nginx/conf.d/server/1-common.conf

Reload Nginx

sudo nginx -t && sudo nginx -s reload

Add new website, configuring PHP & Nginx & MariaDB

Steps 1. - 9. can be skipped by calling the add-vhost.sh. Just download add-vhost.sh, chmod u+x ./add-vhost.sh and call it sudo ./add-vhost.sh. The file is deleted automatically.

$ cd ~ && wget https://raw.githubusercontent.com/lucien144/lemp-stack/master/add-vhost.sh && chmod u+x add-vhost.sh
$ sudo ./add-vhost.sh

1. Create the dir structure for new website

sudo mkdir -p /var/www/vhosts/new-website.tld/{web,logs,ssl}

2. User groups and roles

$ sudo groupadd new-website
$ sudo useradd -g new-website -d /var/www/vhosts/new-website.tld new-website
$ sudo passwd new-website
$ sudo usermod -s /bin/bash new-website

You can switch users by using sudo su - new-website

3. Update permissions

sudo chown -R new-website:new-website /var/www/vhosts/new-website.tld
sudo chmod -R 0775 /var/www/vhosts/new-website.tld

4. Create new PHP-FPM pool for new site

sudo nano /etc/php/7.4/fpm/pool.d/new-website.tld.conf

5. Configure the new pool

[new-website]
user = new-website
group = new-website
listen = /run/php/php7.4-fpm-new-website.sock
listen.owner = www-data
listen.group = www-data
php_admin_value[disable_functions] = exec,passthru,shell_exec,system
php_admin_flag[allow_url_fopen] = off
pm = dynamic
pm.max_children = 5 # The hard-limit total number of processes allowed
pm.start_servers = 2 # When nginx starts, have this many processes waiting for requests
pm.min_spare_servers = 1 # Number spare processes nginx will create
pm.max_spare_servers = 3 # Number spare processes attempted to create
pm.max_requests = 500
chdir = /
5.1 Configuring pm.max_children
  1. Find how much RAM FPM consumes: ps -A -o pid,rss,command | grep php-fpm -> second row in bytes
    1. Reference: https://overloaded.io/finding-process-memory-usage-linux
  2. Eg. ~43904 / 1024 -> ~43MB per one process
  3. Calculation: If server has 2GB RAM, let's say PHP can consume 1GB (with some buffer, otherwise we can use 1.5GB): 1024MB / 43MB -> ~30MB -> pm.max_childern = 30
5.2 Configuring pm.start_servers, pm.min_spare_servers, pm.max_spare_servers
  1. pm.start_servers == number of CPUs
  2. pm.min_spare_servers = pm.start_servers / 2
  3. pm.max_spare_servers = pm.start_servers * 3

6. Restart PHP fpm and check it's running

sudo service php7.4-fpm restart
ps aux | grep new-site

7. Create new "vhost" for Nginx

sudo nano /etc/nginx/sites-available/new-site.tld

8. Configure the vhost

server {
    listen 80;

    root /var/www/vhosts/new-site.tld/web;
    index index.php index.html index.htm;

    server_name www.new-site.tld new-site.tld;

    include /etc/nginx/conf.d/server/1-common.conf;

    access_log /var/www/vhosts/new-site.tld/logs/access.log;
    error_log /var/www/vhosts/new-site.tld/logs/error.log warn;

    location ~ \.php$ {
        try_files $uri $uri/ /index.php?$args;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/var/run/php/php7.4-fpm-new-site.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

9. Enable the new vhost

cd /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/new-site.tld new-site.tld
sudo nginx -t && sudo nginx -s reload

10. MariaDB (MySQL)

sudo mysql
> CREATE DATABASE newwebsite_tld;
> CREATE USER 'newwebsite_tld'@'localhost' IDENTIFIED BY 'password';
> GRANT ALL PRIVILEGES ON newwebsite_tld.* TO 'newwebsite_tld'@'localhost';
> FLUSH PRIVILEGES;

Others

Git Aware Prompt

If you want to have nice git-aware prompt with some handy aliases, use this:

sudo su virtualhostuser
cd ~
wget https://gist.githubusercontent.com/lucien144/56fbb184b1ec01fae1adf2e7abb626b6/raw/0928548acb2ff1618054069f0ae7e60f92d76cc3/install.sh && cat install.sh | bash
bash

More information about aliases and other in this gist.

Git

sudo apt-get install git

Adminer

Adminer is a mostly MySQL database management tool. It's really tiny, simple & easy to use.

cd /etc/nginx/conf.d/server/
sudo wget https://raw.githubusercontent.com/lucien144/lemp-stack/master/nginx/conf.d/server/4-adminer.conf
sudo mkdir -p /var/www/html/adminer/
cd /var/www/html/adminer/
sudo wget https://www.adminer.org/latest.php -O index.php
sudo chmod a+x index.php
sudo htpasswd -c .htpasswd user
sudo nginx -t && sudo nginx -s reload

Adminer is now ready at http://{server.ip}/adminer/

Also, don't forget to change the username πŸ‘†.

Postfix (sending emails from PHP)

In case you cannot send emails from PHP and getting error (tail /var/log/mail.log) Network is unreachable, you need to switch Postfix from IPv6 to IPv6.

sudo apt-get install postfix
sudo nano /etc/postfix/main.cf

Now change the line inet_protocols = all to inet_protocols = ipv4 and restart postfix by sudo /etc/init.d/postfix restart.

You can also check if you have opened port 25 by netstat -nutlap | grep 25

Munin

1. Install

apt-get install munin-node munin

2. Configure Munin

  1. Uncomment #host 127.0.0.1 in /etc/munin/munin-node.conf
  2. Append following code to /etc/munin/munin-node.conf
[nginx*]
env.url http://localhost/nginx_status

3. Configure nginx /etc/nginx/sites-available/default

sudo nano /etc/nginx/sites-available/default
# Change listen 80 default_server; to
listen 80

#Change listen [::]:80 default_server; to
listen [::]:80

# Add settings for stub status to server {}
    location /nginx_status {
        stub_status on;
        access_log off;
        allow 127.0.0.1;
        deny all;
    }

# Add setting to access stats online

    location /stats {
        allow YOUR.IP.ADDRESS;
        deny all;
        alias /var/cache/munin/www/;
    }

4. Install plugins

cd /usr/share/munin/plugins
sudo wget -O nginx_connection_request https://raw.github.com/munin-monitoring/contrib/master/plugins/nginx/nginx_connection_request
sudo wget -O nginx_status https://raw.github.com/munin-monitoring/contrib/master/plugins/nginx/nginx_status
sudo wget -O nginx_memory https://raw.github.com/munin-monitoring/contrib/master/plugins/nginx/nginx_memory

sudo chmod +x nginx_request
sudo chmod +x nginx_status
sudo chmod +x nginx_memory

sudo ln -s /usr/share/munin/plugins/nginx_request /etc/munin/plugins/nginx_request
sudo ln -s /usr/share/munin/plugins/nginx_status /etc/munin/plugins/nginx_status
sudo ln -s /usr/share/munin/plugins/nginx_memory /etc/munin/plugins/nginx_memory

Restart Munin

sudo service munin-node restart

Rabbitmq

Install PHP extension

sudo apt-get install php-amqp

Install RabbitMQ

echo 'deb http://www.rabbitmq.com/debian/ testing main' | sudo tee /etc/apt/sources.list.d/rabbitmq.list
wget -O- https://www.rabbitmq.com/rabbitmq-release-signing-key.asc | sudo apt-key add -
sudo apt-get update
sudo apt-get install rabbitmq-server
sudo service rabbitmq-server status
sudo rabbitmq-plugins enable rabbitmq_management
sudo ufw allow 15672
sudo rabbitmqctl add_user admin *********
sudo rabbitmqctl set_user_tags admin administrator
sudo rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"
sudo rabbitmqctl delete_user guest
sudo service rabbitmq-server restart

Installing plugin

  1. Download the .ez plugin to /usr/lib/rabbitmq/lib/rabbitmq_server-{version}/plugins
  2. Enable the plugin by sudo rabbitmq-plugins enable {plugin name}

Supervisor

sudo apt-get install supervisor

Enable the web interface

echo "
[inet_http_server]
port=9001
username=admin
password=*********" | sudo tee --append /etc/supervisor/supervisord.conf

sudo service supervisor reload
sudo ufw allow 9001

The interface should be available on http://{SERVER_IP}:9001/

Node.js & NPM

sudo apt-get install nodejs
sudo apt-get install npm

If you are getting error /usr/bin/env: β€˜node’: No such file or directory run

sudo ln -s /usr/bin/nodejs /usr/bin/node

Composer

wget https://raw.githubusercontent.com/composer/getcomposer.org/76a7060ccb93902cd7576b67264ad91c8a2700e2/web/installer -O - -q | php -- --quiet
sudo mv composer.phar /usr/local/bin/composer

Reference: https://getcomposer.org/doc/faqs/how-to-install-composer-programmatically.md

Todo

Reference

Setting PHP-FPM

License

This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.