I had a PHP website that was the last man standing on some shared hosting that was slow that didn’t have SSH access. I decided that since I would be moving the website anyway, why not try something different with it. The website in question is PHP/PDO/MySQL with no framework.

Having already tried AWS Lightsail App+OS, I wanted to experiment with the Lightsail “OS Only” option. What better thing to do than to install the Nginx web server on it? Starting from scratch with an OS Only box I would be able to take a look at another side of AWS Lightsail (without Bitnami) and also learn about Nginx and using a LEMP stack.

I created a new instance of Lightsail with Ubuntu 18.04 LTS. It was exactly the same as most VPS that only come with Linux installed. After installing Nginx on Lightsail, the version of PHP I got from by installing PHP-FPM was PHP 7.2.10.

Link… https://www.digitalocean.com/community/tutorials/how-to-migrate-from-an-apache-web-server-to-nginx-on-an-ubuntu-vps

But, we’ve jumped ahead. Let’s look at Linux…

First Things First: Linux

The first thing to do is to update and upgrade Linux. Sudo was already installed, so it’s straight into…

sudo apt-get update
sudo apt-get upgrade

I believe that while Debian is a pretty bare bones install, Ubuntu comes with a lot of stuff pre-installed such as sudo and nano, which is very convenient.

Nginx

With Linux updated, we can get the next part of the LEMP stack installed. That would be Nginx (Engine-X). This is the only part of the stack that I’ve not had much experience of in the past so was most interesting for me. I was expecting it to be slightly more different to Apache than it was. Apart from the different style of the config file...

sudo apt-get install nginx

Now, magically the unique domain you have in your Lightsail console will work in your browser, giving you a page like…

Welcome to nginx!
If you see this page, the nginx web server is successfully installed and working. Further configuration is required.

For online documentation and support please refer to nginx.org.
Commercial support is available at nginx.com.

Thank you for using nginx.

Doing some prep for when we change the DNS and point the domain name at this hosting, we should also make a config file…

The Nginx Config File

The config file should be the name of the site and should be created in /etc/nginx/sites-available. Copy the info from the default file across to the new site, changing the domain name. You can then set up a symlink to “sites-enabled”…

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

https://www.digitalocean.com/community/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-ubuntu-18-04

sudo unlink /etc/nginx/sites-enabled/default

Test the new Nginx config, then restart to load the new settings with…

sudo nginx -t
sudo systemctl reload nginx

Change permissions of the /var/www/htdocs/ folder then upload files. Convert htaccess…

https://winginx.com/en/htaccess

Something like this will eventually need the * removing for nginx…

<Files *.inc>
Deny From All
</Files>

becomes…

 location ~ .inc {
deny all;
}

# I also had to convert the "break" to "last" on the mod_rewrite...

rewrite ^/(.*)/$ /item.php?item=$1 last;

Then, add the code to the example.com config file and test it again. Any duplicates will need commenting out with #. In the config file \.php causes an error, remove the slash.

PHP

Now, we can install PHP and MySQL to complete our LEMP stack…

sudo apt install php-fpm php-mysql 

Luckily, the site I was moving had pretty modern PHP with nothing that needed fixing at all. Uploaded a file with the function phpinfo() on it to test that PHP is working. All good!

Nginx Default Log File

index.php not working! Look at the log file…

tail /var/log/nginx/error.log

Yes, the PHP was fine, it turns out that PDO was unhappy that I hadn’t added the database yet…

MariaDB

Finish off getting MariaDB installed, then check it’s working…

sudo apt install mariadb-client-core-10.1
sudo apt install mariadb-server
sudo systemctl status mariadb

I was getting the error, below…

ERROR 2002 (HY000): Can’t connect to local MySQL server through socket ‘/var/run/mysqld/mysqld.sock’ (2 “No such file or directory”)

So, I did a “locate” for the my.cnf file and the “mysqld.sock” file and added this to the mysql/mariadb config file, my.cnf…

socket  = /var/run/mysqld/mysqld.sock

Then…

sudo service mysql restart

Login for the first time with sudo…

sudo mariadb -uroot

Now you can create the database and database user for the app.

https://stackoverflow.com/questions/5376427/cant-connect-to-local-mysql-server-through-socket-var-mysql-mysql-sock-38

SSL Encryption

Pointed domain name at the public IP address with CloudFlare. Server-side SSL encryption to follow.

https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-18-04

Simple Password Authentication

https://www.tecmint.com/password-protect-web-directories-in-nginx/
sudo apt install apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd username

Then put the following in the “location” you waant to be protected in the config file…

auth_basic "Administrator Login";
auth_basic_user_file /etc/nginx/.htpasswd;

Then, test and restart NginX.

Force or Remove WWW

For some sites I prefer to keep the www in, so I did the opposite of this on this occasion…

server {
server_name www.example.com;
return 301 $scheme://example.com$request_uri;
}
server {
server_name example.com;
# […]
}

https://stackoverflow.com/questions/11323735/nginx-remove-www-and-respond-to-both

index.php Downloads instead of Displaying

Sometimes the index.php, or ay PHP files can start downloading instead of displaying normally in the browser. The fix for this is to pass the PHP scripts to the FastCGI server. Make sure you use the correct filepath, below is for PHP 7.2 but it will be different for different versions of PHP…

server {
listen 80;
listen [::]:80;
root /var/www/myApp;
index index.php index.html index.htm;
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/run/php/php7.2-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}

Debug Mode

To show extra info in the error.log file add the word “debug” to the error_log statement…

error_log  /etc/nginx/error.log debug;

Example nginx.conf file

This file is taken from here. Shows SSL encryption…

server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name www.example.com;
ssl on;
ssl_certificate /root/certs/APPNAME/APPNAME_nl.chained.crt;
ssl_certificate_key /root/certs/APPNAME/ssl.key;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:20m;
ssl_session_tickets off;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK';
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /root/certs/APPNAME/APPNAME_nl.chained.crt;
root /srv/users/serverpilot/apps/APPNAME/public;
access_log /srv/users/serverpilot/log/APPNAME/APPNAME_nginx.access.log main;
error_log /srv/users/serverpilot/log/APPNAME/APPNAME_nginx.error.log;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-SSL on;
proxy_set_header X-Forwarded-Proto $scheme;
include /etc/nginx-sp/vhosts.d/APPNAMEd/.nonssl_conf; include /etc/nginx-sp/vhosts.d/APPNAME.d/.conf;
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com;
ssl on;
ssl_certificate /root/certs/APPNAME/APPNAME_nl.chained.crt;
ssl_certificate_key /root/certs/APPNAME/ssl.key;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:20m;
ssl_session_tickets off;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK';
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /root/certs/APPNAME/APPNAME_nl.chained.crt;
root /srv/users/serverpilot/apps/APPNAME/public;
access_log /srv/users/serverpilot/log/APPNAME/APPNAME_nginx.access.log main;
error_log /srv/users/serverpilot/log/APPNAME/APPNAME_nginx.error.log;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-SSL on;
proxy_set_header X-Forwarded-Proto $scheme;
include /etc/nginx-sp/vhosts.d/APPNAME.d/.nonssl_conf; include /etc/nginx-sp/vhosts.d/APPNAME.d/.conf;
}

Conclusion

Website moved and working as it did before, but possibly slightly faster.

In just the short time I have been using it Nginx is already growing on me. I like the simplicity. I like that it is quite similar to Apache in some ways. And, I like the fact that it should be faster than Apache.

Not being able to use .htaccess files, and the Nginx config being different to Apache virtualhost files was not bad at all. A combination of using a htaccess-to-Nginx converter and Google/Stackoverflow has already taught me a lot of how to replicate what I might do with an .htaccess or virtualhost file with Nginx.

As expected, the “OS Only” version of AWS Lightsail was much more like a standard VPS and there was nothing too hard in setting it up and moving a site across and onto Nginx.

Previously, I looked at setting up Debian 8 with PHP 7.0. But now, Debian 9 is the current stable version and Debian 8 is only due to receive security updates until May 2018. Sounds like a good time to upgrade!

The reason I started to look into upgrading was that I found out that the highest version of Apache available on Debian 8 is Apache 2.4.10 which is not capable of running HTTP/2. Having recently upgraded a lot of my websites to HTTPS, I also wanted to upgrade them to HTTP/2 aswell. Debian 8’s version of Apache was not able to be upgraded without doing so manually. However, Debian 9’s default version of Apache is 2.4.25 which is HTTP/2 capable, it also has PHP 7.0 as the default and has upgraded MySQL to MariaDB. All the more reason to upgrade!

From here

For those who don’t know, Debian codenames are based on the characters in the famous animated movie Toy Story. This release is named after the glittery purple rubber toy octopus, Stretch.

Alternatives to upgrading to Debian 9 (Stretch)

Upgrading from Debian Jessie to Stretch will not be for everyone. Here are some possible alternatives.

Manually installing a higher version of Apache.

This is generally thought of as possible but not recommended. You also have the gradual winding down of Debian support from May 2018 which may lead to security risks.

Changing to another flavor of Linux with a version of Apache that is capable of running HTTP/2.

Ubuntu 16.04 is one version that can run HTTP/2 without too much tinkering.

Ubuntu is similar to Debian in terms of the shell commands. I have some websites that use Ubuntu 16.04 but for this particular website, I wanted to stay with Debian. Ubuntu is thought of as more experimental, while Debian is thought of as more stable and faster.

Ubuntu 16.04 LTS looks like it will be supported until April 2021 from here so it will not need updating any time soon. The next version with long-term support (LTS) is released next month (April 2018) so I may look into upgrading the servers running Ubuntu when that arrives.

I have used AWS in the past and I have built websites on FreeBSD and CentOS servers that I can remember, but currently I am mainly using Debian and Ubuntu with un-managed VPS. This experience has made me want to try some different flavors, or at least learn about how the other flavors are different. What I’ve read so far makes me think that Debian is a decent choice for this site. Here’s a comparison between Debian, Ubuntu and CentOS for webservers.

Changing from Apache to Nginx.

While you’re still left with the Debian support for Jessie winding down, changing to Nginx is probably a good move. Debian 9 with Nginx may well be faster than it would be with Apache. The version of Nginx you install on Debian 8 with Jessie Backports can run HTTP/2.

The problem with changing to Nginx on a live website is that it is completely different as a webserver to Apache. Nginx does not have .htaccess files and the setup will be different in ways that I’m not aware of yet. I wanted to upgrade in less than a day on this live website so this option was not right for me at the time. But, I do plan to use Nginx in the near future.

Upgrading from Debian 8 (Jessie) to Debian 9 (Stretch)

The official Debian guide to doing this manually in the command line is here. I decided against doing it this way, although it may have lead to less downtime. I decided a fresh install may cause less problems in the future.

I decided to just re-install in the hosting company portal. This meant backing everything up, then with the installation, everything was erased and had to be re-added. There is only one website on the VPS which is a WordPress blog. There was nothing particularly complicated about the server setup which made life a lot easier. The main thing I had to do were to get MariaDB installed and to import the backed up SQL file from the MySQL, I referred to this guide for that. I also had to get Lets Encrypt installed and set up the SSL encryption again.

One of the things I had to change from when I set up Debian 8 with PHP 7.0 was the way PHP 7.0 is installed/served. mpm_prefork is not compatible with HTTP/2 (mod_http2) so I had to use another way of installing PHP (from here).

I was rushing the installation through as quickly as I could because the website was down. I probably did some things in the wrong order and I did encounter some problems. However, after about 45 minutes I had to leave the house and do some chores. At that stage, the WordPress blog was basically up and running but mod_rewrite was not configured correctly yet and the SSL was not set up yet either.

When I got back I started to finish off getting the site working but almost as soon as I sat down the server locked me out of SFTP and SSH. I could not even access the shell on the control panel’s console. I actually thought I’d locked myself out with the UFW firewall. I’m not sure what the problem was but it turned out to just be temporary and I hadn’t locked myself out at all.

Everything went pretty smoothly really. I got the occasional error when trying to reboot but nothing major. Every time it was pretty obvious what the problem was without doing much, if any, investigating. The dreaded error message when Apache won’t restart is definitely becoming less scary these days since I started ignoring the files it tells you to look at and just go straight for the main error.log or whatever you have set up as the main error log file.

Learning New Stuff

I was expecting more problems from exporting the MySQL then importing it into MariaDB. Even if the import/export went smoothly I was expecting to have some issues with WordPress itself. All I can say is no problems whatsoever with the database or WordPress at all. Maybe it’s because I’ve done this kind of thing a few times before and this time I was actually ready and prepared for it.

Because the website is just a WordPress blog I am not going to be doing a huge amount of work with MariaDB on that website. But, one of the things I did take a look at was the MariaDB tuning: mysqltuner. I can’t wait to do some more work with MariaDB in future.

I did all the database backup, set up and importing in the shell. I didn’t use PHPmyAdmin at all but decided to install it at the end incase I ever needed to use it. Then, setting up PHPmyAdmin needed some more shell work with the MySQL command line and MariaDB. Because of all this, I feel like I’m a lot more confident with the MySQL command line.

The Unattended Upgrades file that came with my installation seems to be the same one used with Jessie. Referred to articles such as Unattended Upgrades in Debian 9 for what to keep and what to remove.

Unattended Upgrades will need to send emails to tell you when it needs a reboot unless you have it automatically reboot or just do not want emails. I had the Jessie server set up to send emails directly from the server. This time I decided to send the server emails with Mailgun using this guide.

With trying to get the website up-and-running normally as soon as possible I feel like I got to know Nano a lot better. I even edited the nano config file, /etc/nanorc, to tweak some of the things I have disliked about using Nano.

Issues Encountered

The Sudoers file.

After setting up a user and adding it to the group, sudo, I was getting errors every time I wanted to use a sudo command. I may have done this before but the sudoers file is located at /etc/sudoers

Something like…

root ALL=(ALL:ALL) ALL

Problem installing Python and Let’s Encrypt… dependencies.

Once I’d fixed the Sudoers file I think there may have been another obstacle to installing Python and the Lets Encrypt dependencies but I have completely forgotten what it was.

PHPmyAdmin could not create the phpmyadmin table or the ‘pma’ user.

I did not use PHPmyAdmin at all but installing it at the end caused a few problems that were easily fixed.

Outcome

All-in-all I’m very happy I got the upgrade done. And, I’m pleased I was able to do it fairly quickly despite having to leave the house against my will during the process then getting locked out. I’m looking forward to playing with and configuring Debian 9 Stretch on this VPS. And I think I chose the right way to do the upgrade. Upgrading from Debian 8 Jessie to Debian 9 Stretch on a live website through the shell can be risky and I didn’t want to get to a situation where nothing worked and I didn’t know how to fix it. The cherry on the cake is that I now have HTTP/2 working which was the main thing I wanted to add at the start, plus I have some other upgrades. Debian 9 should be ok for at least a year before I need to start thinking about upgrading again – Debian Version History.

Future

Now, having researched HTTP/2, Debian and Apache for this project I am keen to try Nginx very soon. Nginx is a similar speed as Apache for some things but is much faster in others so I am looking forward to seeing what a life without .htaccess files looks like.