How to Install Nginx with PHP + MySQL (LEMP) on Debian 9

This tutorial will show you the installation of the Nginx web server on Debian 9 (Stretch). Nginx (pronounced "engine x") is a free, open-source, high-performance HTTP server. Nginx is known for its stability, rich feature set, simple configuration, and low resource consumption. This tutorial shows the installation of Nginx with PHP support (through PHP-FPM) and MySQL and MariaDB. This setup is often referred to as LEMP = Linux + nginx (pronounced "engine x") + MySQL + PHP) .

Preliminary Note

In this tutorial, I use the hostname server1.example.com with the IP address 192.168.1.100. These settings might differ for you, so you have to replace them where appropriate. You should have a Debian 9 server, I will use the Debian minimal server as the base system for this tutorial.

Update the Debian System

It is recommended to update the package lists and install any pending updates before we start with the Nginx setup. Run the following commands to install any pending updates.

apt-get update
apt-get upgrade -y

I will use the nano editor later to edit configuration files. Nano can be installed with this command:

apt-get -y install nano

Install Nginx

Nginx is available as a package for Debian 9 which we can be installed with this command:

apt-get -y install nginx

Now start Nginx web server:

systemctl start nginx.service

Type in your web server's IP address or hostname into a browser (e.g. http://192.168.1.100), and you will see the following page:

Nginx Welcome Page

The default nginx document root on Debian Linux is /var/www/html.

Install MySQL or MariaDB

In this step, I will show you how to install MySQL or MariaDB. You are free to choose which database system you prefer. Just ensure that you install only one database engine and not MySQL and MariaDB together.

Install MySQL

The MySQL packages for Debian 9 can be obtained from Oracle directly. Oracle provides a MySQL repository package which integrates the Oracle MySQL repository into Debian so that we can install and update MySQL with apt. Get the MySQL apt repository package here in case the wget download below fails due to changes in the download URL.

cd /tmp
wget https://dev.mysql.com/get/mysql-apt-config_0.8.9-1_all.deb
dpkg -i mysql-apt-config_0.8.9-1_all.deb

Choose to configure 'OK' in the list, then focus the OK button at the footer and press enter. This will select the current stable version, which is MySQL 5.7.

Select current stable MySQL version

Now we'll update the package list and install the MySQL server and client package.

apt-get update
apt-get -y install mysql-community-client mysql-community-server

The MySQL installer will ask you to set a password for the MySQL root user. Choose a long and secure password as this pasword allows full administrative access to the MySQL database.

Set the MySQL root password

Re-enter the password as requested.

Install MariaDB

In order to install MariaDB, we run:

apt-get -y install mariadb-server mariadb-client

Unlike the MySQL installer, the MariaDB installer will not set a root password during installation. To secure the MariaDB installation, remove the anonymous user and to disable the test database, run this command:

mysql_secure_installation

Answer the questions as follows:

Change the root password? [Y/n] <-- y
New password: <-- Enter a new MySQL root password
Re-enter new password: <-- Repeat the MySQL root password
Remove anonymous users? [Y/n] <-- y
Disallow root login remotely? [Y/n] <-- y
Remove test database and access to it? [Y/n] <-- y
Reload privilege tables now? [Y/n] <-- y

Installing PHP

We can make PHP work in nginx through PHP-FPM (PHP FastCGI Process Manager). This is an alternative PHP FastCGI implementation with some additional features useful for sites of any size, especially busier sites. Install PHP 7 as follows:

apt-get -y install php7.0-fpm

PHP-FPM is a daemon process (with the systemd unit file php7.0-fpm.service) that runs a FastCGI server on the socket /var/run/php/php7.0-fpm.sock.

Configuring Nginx

The Nginx configuration is in /etc/nginx/nginx.conf which we open now:

nano /etc/nginx/nginx.conf

The configuration is easy to understand (you can learn more about it here: https://www.nginx.com/resources/wiki/start/topics/examples/full/ and here: https://www.nginx.com/resources/wiki/)

First set the keepalive_timeout to a reasonable value like 2 seconds:

[...]
keepalive_timeout 2;
[...]

The virtual hosts are defined in server {} containers. The default vhost is defined in the file /etc/nginx/sites-available/default - let's modify it as follows:

nano /etc/nginx/sites-available/default

[...]
server {
listen 80 default_server;
listen [::]:80 default_server;

# SSL configuration
#
# listen 443 ssl default_server;
# listen [::]:443 ssl default_server;
#
# Note: You should disable gzip for SSL traffic.
# See: https://bugs.debian.org/773332
#
# Read up on ssl_ciphers to ensure a secure configuration.
# See: https://bugs.debian.org/765782
#
# Self signed certs generated by the ssl-cert package
# Don't use them in a production server!
#
# include snippets/snakeoil.conf;

root /var/www/html;

# Add index.php to the list if you are using PHP
index index.php index.html index.htm index.nginx-debian.html;

server_name _;

location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}

# pass PHP scripts to FastCGI server
#
location ~ \.php$ {
include snippets/fastcgi-php.conf;

# With php-fpm (or other unix sockets):
fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
# With php-cgi (or other tcp sockets):
# fastcgi_pass 127.0.0.1:9000;
}

# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
location ~ /\.ht {
deny all;
}
} [...]

server_name _; makes this a default catchall vhost (of course, you can as well specify a hostname here like www.example.com).

I've added index.php to the index line. root /var/www/html; means that the document root is the directory /var/www/html.

The important part for PHP is the location ~ \.php$ {} stanza. Uncomment it like shown above to enable it. There are two fastcgi_pass lines included, uncomment just the one for the php-7.0-fpm.sock file.

Now save the file and reload Nginx:

systemctl reload nginx.service

Next open /etc/php/7.0/fpm/php.ini...

nano /etc/php/7.0/fpm/php.ini

... and set cgi.fix_pathinfo=0:

[...]
; cgi.fix_pathinfo provides *real* PATH_INFO/PATH_TRANSLATED support for CGI.  PHP's
; previous behaviour was to set PATH_TRANSLATED to SCRIPT_FILENAME, and to not grok
; what PATH_INFO is.  For more information on PATH_INFO, see the cgi specs.  Setting
; this to 1 will cause PHP CGI to fix its paths to conform to the spec.  A setting
; of zero causes PHP to behave as before.  Default is 1.  You should fix your scripts
; to use SCRIPT_FILENAME rather than PATH_TRANSLATED.
; http://php.net/cgi.fix-pathinfo
cgi.fix_pathinfo=0
[...]

... then you might want to rise the POST limit and the file upload limit:

post_max_size = 25M
upload_max_filesize = 20M

When you plan to upload large files by PHP then you should raise the values to even 500M or more. M stands here for Megabytes.

Finally, you might want to set the timezone to your local timezone. In my case, the timezone is:

date.timezone = 'Europe/Berlin'

You can find the list of supported timezones here.

Reload PHP-FPM to apply the changes:

systemctl reload php7.0-fpm.service

Now create the following PHP file in the document root /var/www/html/:

nano /var/www/html/info.php

<?php
phpinfo();

Now we call that file in a browser (e.g. http://192.168.1.100/info.php):

PHP-FPM info

As you see, PHP 7 is working, and it's working through FPM/FastCGI, as shown in the Server API line. If you scroll further down, you will see all modules that are already enabled in PHP. MySQL is not listed there which means we don't have MariaDB / MySQL support in PHP yet.

Getting MySQL / MariaDB Support in PHP

To get MySQL support in PHP, we can install the php7.0-mysqlnd package. It's a good idea to install some other PHP modules as well as you might need them for your applications. You can search for available PHP modules like this:

apt-cache search php7.0

Pick the ones you need and install them like this:

apt-get -y install php7.0-mysqlnd php7.0-curl php7.0-gd php7.0-intl php-pear php-imagick php7.0-imap php7.0-mcrypt php-memcache php7.0-intl php7.0-pspell php7.0-recode php7.0-sqlite3 php7.0-tidy php7.0-xmlrpc php7.0-xsl

Now reload PHP-FPM:

systemctl reload php7.0-fpm.service

Now reload http://192.168.1.100/info.php in your browser and scroll down to the modules section again. You should now find lots of new modules there, including the MySQLi and MySQLnd module:

PHP with builtin MySQL support

Making PHP-FPM use a TCP connection (optional)

By default, PHP-FPM is listening on the socket /var/run/php/php7.0-fpm.sock and that's the recommended and fastest way to connect PHP to Nginx. However, there might be setups where you want to let Nginx connect to PHP over the network. It is possible to make PHP-FPM use a TCP connection. To do this, open /etc/php/7.0/fpm/pool.d/www.conf...

nano /etc/php/7.0/fpm/pool.d/www.conf

... and make the listen line look as follows:

[...]
;listen = /var/run/php5-fpm.sock
listen = 127.0.0.1:9000
[...]

This will make PHP-FPM listen on port 9000 on the IP 127.0.0.1 (localhost). Make sure you use a port that is not in use on your system.

Then reload PHP-FPM:

systemctl reload php7.0-fpm.service

Next go through your Nginx configuration and all your vhosts and change the line fastcgi_pass unix:/var/run/php7.0-fpm.sock; to fastcgi_pass 127.0.0.1:9000;, e.g. like this:

nano /etc/nginx/sites-available/default

[...]
location ~ \.php$ {
include snippets/fastcgi-php.conf;

# With php-fpm (or other unix sockets):
# fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
# With php-cgi (or other tcp sockets):
fastcgi_pass 127.0.0.1:9000;
} [...]

Finally, reload Nginx:

systemctl reload nginx.service

Enable SSL and HTTP/2 in Nginx

Most websites today use SSL (TLS) to provide secure access. In this chapter, I'll show you how to create an SSL certificate an how to activate SSL in Nginx. You can either use a self-signed SSL certificate or request an officially signed SSL certificate from Let's encrypt. Let#s encrypt SSL certificates are available free of charge, but you must have a valid domain name that points to your server in DNS already. If you have no domain name yet or when your server is on a local network and not accessible from outside, then use a self-signed SSL certificate. Follow either the steps for a self-signed SSL certificate or the Let's encrypt certificate below but not both.

Create a self-signed SSL certificate

Create the SSL key file with the OpenSSL command:

openssl genrsa -out /etc/ssl/private/nginx.key 4096

Create the SSL certificate key with OpenSSL

Then create the self-signed SSL certificate:

openssl req -new -x509 -key /etc/ssl/private/nginx.key -out /etc/ssl/certs/nginx.pem -days 3650

The command will ask for details like country, state, city, company name and domain name.

Create an SSL cert with OpenSSL

Activate the self-signed SSL certificate in Nginx. To do so, edit the nginx.conf file again:

nano /etc/nginx/sites-available/default

and make the server part it look like this:

server {
listen 80 default_server;
listen [::]:80 default_server;

# SSL configuration

listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;

ssl on;
ssl_certificate_key /etc/ssl/private/nginx.key;
ssl_certificate /etc/ssl/certs/nginx.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
ssl_prefer_server_ciphers on;

# Note: You should disable gzip for SSL traffic.
# See: https://bugs.debian.org/773332
#
# Read up on ssl_ciphers to ensure a secure configuration.
# See: https://bugs.debian.org/765782
#
# Self signed certs generated by the ssl-cert package
# Don't use them in a production server!
#
# include snippets/snakeoil.conf;

root /var/www/html;

# Add index.php to the list if you are using PHP
index index.php index.html index.htm index.nginx-debian.html;

server_name _;

location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}

# pass PHP scripts to FastCGI server
#
location ~ \.php$ {
include snippets/fastcgi-php.conf;

# With php-fpm (or other unix sockets):
fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
# With php-cgi (or other tcp sockets):
# fastcgi_pass 127.0.0.1:9000;
}

# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
location ~ /\.ht {
deny all;
}
}

Restart Nginx to apply the changes.

systemctl restart nginx.service

Now open the https URL of your server in a browser, e.g. https://192.169.1.100/. You will get a security warning which you'll have to accept to proceed. Afterwards, you will see the Nginx start page, the warning icon in the URL bar of the browser indicates that we use a self-signed SSL certificate.

Use a free Let's Encrypt SSL certificate

In this chapter, I'll describe how to secure your Nginx server by using a free Let's encrypt SSL certificate. A prerequisite is that you own a domain name which points to the IP of the server where you install Nginx on at the moment.

Install Certbot, the Let's encrypt client that is used to obtain a free SSL certificate.

apt-get -y install certbot python-certbot-nginx

Edit the website configuration file /etc/nginx/sites-available/default and set your domain name(s) in the server_name line:

nano /etc/nginx/sites-available/default

The line should look like this after editing:

server_name example.com;

Replace example.com with your own domain name. If you have multiple domain names or subdomains, then add them separated by whitespace.

server_name example.com www.example.com otherdomain.tld;

Now we request an SSL certificate from let's encrypt by using the nginx plugin from Certbot.

certbot --nginx -d example.com

Multiple domains can be added by repeating the -d option. Example:

certbot certonly --webroot -d example.com -d www.example.com

Certbot will ask you for an email address where renewal notifications shall be sent to. Enter a valid email address here:

Enter email address (used for urgent renewal and security notices) (Enter 'c' to cancel): [email protected]

Accept the license terms by entering 'A'.

-------------------------------------------------------------------------------
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v01.api.letsencrypt.org/directory
-------------------------------------------------------------------------------
(A)gree/(C)ancel: A

Here the further dialog that requests the SSL certificate. I've added my answers in red.

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for example.com
http-01 challenge for www.example.com

Select the webroot for example.com:
-------------------------------------------------------------------------------
1: Enter a new webroot
-------------------------------------------------------------------------------
Press 1 [enter] to confirm the selection (press 'c' to cancel): 1
Input the webroot for example.com: (Enter 'c' to cancel):/var/www/html

Select the webroot for www.example.com:
-------------------------------------------------------------------------------
1: Enter a new webroot
2: /var/www/html
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
Waiting for verification...
Cleaning up challenges
Generating key (2048 bits): /etc/letsencrypt/keys/0000_key-certbot.pem
Creating CSR: /etc/letsencrypt/csr/0000_csr-certbot.pem

IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at
/etc/letsencrypt/live/example.com/fullchain.pem. Your cert will
expire on 2018-04-23. To obtain a new or tweaked version of this
certificate in the future, simply run certbot again. To
non-interactively renew *all* of your certificates, run "certbot
renew"
- If you like Certbot, please consider supporting our work by:

Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le

There is an alternate method by using the '--nginx' option in Certbot, but this method does not seem to work at the moment due to the TLS-SNI-01 problem in Let's encrypt. The webroot method, as shown above, works fine though.

The newly generated SSL certificate is in a subfolder of /etc/letsencrypt/live/ folder. The exact path is shown in the Certbot output.

Now we'll add this SSL certificate in our Nginx website file. Edit the Nginx default file:

nano /etc/nginx/sites-available/default

and change the SSL section like this:

[...]
server {
listen 80 default_server;
listen [::]:80 default_server;

# SSL configuration

listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;

ssl on;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
ssl_prefer_server_ciphers on;

# Note: You should disable gzip for SSL traffic.
# See: https://bugs.debian.org/773332
#
# Read up on ssl_ciphers to ensure a secure configuration.
# See: https://bugs.debian.org/765782
#
# Self signed certs generated by the ssl-cert package
# Don't use them in a production server!
#
# include snippets/snakeoil.conf;

root /var/www/html;

# Add index.php to the list if you are using PHP
index index.php index.html index.htm index.nginx-debian.html;

server_name _;

location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}

# pass PHP scripts to FastCGI server
#
location ~ \.php$ {
include snippets/fastcgi-php.conf;

# With php-fpm (or other unix sockets):
fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
# With php-cgi (or other tcp sockets):
# fastcgi_pass 127.0.0.1:9000;
}

# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
location ~ /\.ht {
deny all;
}
} [...]

Replace 'example.com' in the SSL certificate path with your own domain name. Restart Nginx to apply the changes.

systemctl restart nginx.service

Virtual Machine image

This tutorial is available as ready to use virtual machine in OVA / OVF format for Howtoforge subscribers. The VM format is compatible with VMWare and Virtualbox and probably some other tools that can import this format. You can find the download link in the right menu on the top. Click on the file name to start the download.

The login details of the VM are:

SSH Login

Username: administrator
Password: howtoforge

Run 'su' to become root user, the root password is 'howtoforge' as well.

The virtual machine image uses MySQL as the database server.

MySQL Login

Username: root
Password: howtoforge

Please change the passwords after the first boot.

The VM is configured for the static IP 192.168.1.100, the IP can be changed in the file /etc/network/interfaces.

Share this page:

Suggested articles

1 Comment(s)

Add comment

Comments

From: helios842000 at: 2018-02-15 14:32:53

hi, a possibility to add ispconfig3? thx