NPO Guide

A quick guide to Google Domains, Digital Ocean, NGINX & PHP-FPM.

@suchislife801

Overview


This guide was created with the idea that you should visit less than countless pages online in order to spin up a fast NGINX/PHP server Droplet on Digital Ocean. It makes an effort at explaing what you are doing every step of the way and WITHOUT extra words, advertisement or anything that may keep you from objectively understanding what the hell it is that you are doing. This is version 1 and it will be revised towards less and less text until it can't be any smaller.

You can buy a domain name from Google Domains starting at $9/year and spin up an Ubuntu Server 20.04(LTS) Digital Ocean Droplet starting at $5/month.

Minimum initital investment of $14.00.
Minimum recurring investment of $9/year & $5.00/month.

We will not be covering https as it would call for elements that are beyond the scope of this guide. Once you are finished you may run a test at securityheaders.com to visualize how far you've come and whatelse you can do in NGINX by editing the config file (more on this later).

Most of the content is in the format Acquire/Create, Install/Configure, Use/Maintain.


Cloud Service Providers
The three cloud service providers listed below are required in order to use this guide.
Gmail Account

gmail.com - Admin Email service provider. This is our parent admin email account. All three cloud services below are subscribed to using this Gmail parent admin account.

Google Domains

domains.google - Domain service provider and DNS Management tool. We are going to use our Gmail account to acquire domain names from here and manage everything about them here as well.

Configuring DNS Settings - A Record
Configuring DNS Settings - CNAME Record

Digital Ocean

digitalocean.com - Droplet and Managed Database Cluster service provider. We are going to use our Gmail account to sign-up for Digital Ocean and then use the control panel to create a project that will contain our Ubuntu OS Droplet (VPS).

Managed Databases will later be added to this Digital Ocean project as well.

About Droplets (Ubuntu OS)
About Managed Databases (MySQL)

Creating an Ubuntu 20.04(LTS) Droplet
Digital Ocean - Project

In the Digital Ocean control panel, every service you add must belong to a Project. You first create a project, then you add services to it. Some of the services you may add include Droplets (The Operating System) & Database Clusters (Managed MySQL Database).

Remember! Domains (DNS Management) is once again done through Google Domains.

Creating a Digital Ocean project

1. Login to Digital Ocean.

2. On the top left, click + New Project.

3. Enter the project Name (example1.com), Description and Purpose.

4. Click Create Project

Now that an empty Project has been created, we can begin to add services to it. The first service we will add is an Ubuntu Server 20.04(LTS) Droplet.

1. Operating System

The operating system we are going to use is Ubuntu Server 20.04(LTS). This version of Linux receives frequent updates and has a big community around to help you with questions. This is ideal for developers.

Installation

In this step, we will be adding a Ubuntu Server 20.04(LTS) Operating System which Digital Ocean offers as a Droplet.

Installing Digital Ocean Droplet

1. Login to Digital Ocean.

2. On the left, select the project you have created.

3. On the main page, select Create a Droplet.

Customizing Droplet Installation

1. Choose an image -> Ubuntu 20.04(LTS) x64

2. Choose a plan -> Starter @ $5.00/mo - 1GB/1CPU - 25GB SSD - 1000GB TRANSFER

3. Add block storage -> Not applicable

4. Choose a datacenter region - > New York, San Francisco, Amsterdam, Singapore, London, Frankfurt, Toronto, Bangalore

5. Select additional options -> Not applicable

6. Authentication -> One time password

7. Finalize and create
    - How many Droplets? -> 1
    - Choose a hostname -> ubuntu-s-1vcpu-1gb-sfo2-01

8. Add tags -> Optional

9. Select Project -> example1.com

10. Click Create Droplet.

Review

When this Droplet is created, it will literally contain nothing more than the Operating system. It will also be assigned a public IP address which is visible when you have the project selected in the control panel.

More specifically, Projects > example1.com > Resources > Droplets > example1.com

We are now ready to begin Ubuntu's initial setup which involves creating a system user with sudo privileges that we will use for system administration later on.

Security Warning!

The sudo prefix helps enforce best practices, running only commands that need to be run as root (such as software installation commands) without leaving you at a root shell where you may stay logged in or run other applications as root.

When you log in as your own user account, programs you run are restricted from writing to the rest of the system – they can only write to your home folder. You can’t modify system files without gaining root permissions. This helps keep your server secure.

2. SSH Terminal

When you first create a new Ubuntu 20.04(LTS) server, there are a few configuration steps that you should take early on as part of the basic setup. This will increase the security and usability of your server and will give you a solid foundation for subsequent actions.

Available in Linux and Windows, this command is used to start the SSH client program that enables secure connection to the SSH server on a remote machine. The `ssh` command is used from logging into the remote machine, transferring files between the two machines, and for executing commands on the remote machine.

1. To login to your Droplet, open up the command prompt CMD (Windows) or BASH (Linux) and type:

ssh root@droplet_ip_address

In the above snippet, droplet_ip_address is replaced with your Digital Ocean project assigned public IP address.

Accept the warning about host authenticity if it appears. If you are using password authentication, provide your root password to log in.

About Root

The root user is the administrative user in a Linux environment that has very broad privileges.

Because of the heightened privileges of the root account, you are discouraged from using it on a regular basis. This is because part of the power inherent with the root account is the ability to make very destructive changes, even by accident.

Also, everything installed by root is only allowed to be used by root. This is why we only use root to create users; nothing more.

The next step is to set up an alternative user account with a reduced scope of influence for day-to-day work. You’ll learn how to gain increased privileges during the times when you need them.

Creating a New User

Once you are logged in as root, we’re prepared to add the new user account that we will use to log in from now on.

This example creates a new user called user1, but you should replace it with a username that you like:

adduser user1

2. You will be asked a few questions, starting with the account password.

3. Enter a strong password and, optionally, fill in any of the additional information if you would like. This is not required and you can just hit ENTER in any field you wish to skip.

Granting Administrative Privileges

Now, we have a new user account with regular account privileges. However, we may sometimes need to do administrative tasks.

To avoid having to log out of our normal user and log back in as the root account, we can set up what is known as "superuser" or root privileges for our normal account. This will allow our normal user to run commands with administrative privileges by entering the prefix sudo before each command.

To add these privileges to our new user, we need to add the new user to the sudo group. By default, on Ubuntu 20.04(LTS), users who belong to the sudo group are allowed to use the sudo command.

As root, run this command to add your new user to the sudo group (substitute the highlighted word with your new user):

usermod -aG sudo user1

user1 is now part of the sudo group and will now inherit its privileges.

Removing ufw (Uncomplicated Fire Wall)

Lastly, we remove Ubuntu's default firewall -- ufw, to avoid conflict.

This is because we are going to use firewalld and there is only place for one firewall in an Operating System.

To remove and completely purge ufw without confirmation, type:

apt remove --purge ufw -y
Root account security

To change root account password, type:

sudo passwd root

To disable root account , type:

sudo passwd -dl root
Exit root!

From this point forward, we exit the terminal and log back in as a sudo user. Now, when logged in as user1, you can type sudo before commands to perform actions with superuser privileges.

Every important command typed by user1 is now preceded by the keyword sudo (superuser privileges).

Using Apt (Advanced Package Tool)
What is apt?

You might already know that Ubuntu is derived from Debian Linux. And Debian uses dpkg packaging system.

A packaging system is a way to provide programs and applications for installation. This way, you don’t have to build a program from the source code which, trust me, is not a pretty way to handle packages.

APT (Advanced Package Tool) is the command line tool to interact with the packaging system. There is already dpkg commands to manage it. But APT is a more friendly way to handle packaging. You can use it to find and install new packages, upgrade packages, remove the packages etc.

apt commands provide a command line way to interact with APT and manage packages.

In this step, we cover the relevant apt commands used in this guide.

Updating the package database requires superuser privileges so you’ll need to use sudo.

sudo apt update

When you run this command, you’ll see the package information being retrieved from various servers.

You’ll see three types of lines here, Hit, Get and Ign. Basically these are:

Hit: there is no change in package version from the previous version

Get: There is a new version available. It will download the information about the version (not the package itself). You can see that there is download information (size in kb) with the ‘get’ line in the screenshot above.

Ign: the package is being ignored. Either the package is way too recent that it doesn’t even bother to check or there was an error in retrieving the file but error was trivial and thus it is being ignored. Don’t worry, this is not an error.

Upgrade installed packages with apt

Once you have updated the package database, you can now upgrade the installed packages. The most convenient way is to upgrade all the packages that have available updates.

To upgrade the installed packages, type:

sudo apt upgrade

This will show you how many and which all packages are going to be upgraded.

There is another way to provide a complete upgrade by using the command below:

sudo apt full-upgrade

sudo apt full-upgrade works the same as sudo apt upgrade except that if system upgrade needs the removal of a package already installed on the system, it will do that. Whereas, the normal upgrade command won’t do this.

apt update vs apt upgrade

Though it sounds like when you do an sudo apt update, it will update the packages and you’ll get the latest version of the package. But that’s not true. sudo apt update only updates the database of the packages.

For example, if you have XYZ package version 1.3 installed, after sudo apt update, the database will be aware that a newer version 1.4 is available. When you do sudo apt upgrade after sudo apt update, it upgrades (or updates, whichever term you prefer) the installed packages to the newer version.

This is the reason why the fastest and the most convenient way to update Ubuntu system by using this command:

sudo apt update && sudo apt upgrade -y

To list installed packages, type:

sudo apt list --installed

To search for a package, type:

sudo apt search package_name_here

To show more about a package before installing or removing it, type:

sudo apt show package_name_here

To install a package without confirmation, type:

sudo apt install package_name_here -y

To remove libs and packages that were installed automatically to satisfy the dependencies of an installed package, type:

sudo apt autoremove

To remove and completely purge a package without confirmation, type:

sudo apt remove --purge package_name_here -y
Installing the Firewall
The firewall we will be using is firewalld 0.8.2-1

Installation

In this step, we will be adding firewalld 0.8.2-1 to our Ubuntu Droplet.

Firewalls provide a basic level of security for your server. These applications are responsible for denying traffic to every port on your server, except for those ports/services you have explicitly approved. Linux Redhat has a service called firewalld to perform this function. A tool called sudo firewalld-cmd is used to configure firewalld firewall policies.

1. To install firewalld without confirmation, type:

sudo apt install firewalld -y

2. To enable firewalld to start on system boot, type:

sudo systemctl enable firewalld

3. To start firewalld service, type:

sudo systemctl start firewalld

4. Check status of the service to make sure it started:

sudo systemctl status firewalld

5. To Restart the service, type:

sudo systemctl restart firewalld

6. To Confirm the service is running, type:

sudo firewall-cmd --state

Note that firewalld it is both active and enabled, meaning it will start by default if the server is rebooted.

sudo apt show package_name_here
firewalld-cmd

Now that the service is up and running, we can use the firewalld-cmd command to get and set policy information for the firewall.

7. First let’s list which services are already *permanently* allowed:

sudo firewall-cmd --permanent --list-all

To get a list of all services that can be enabled by name, type:

sudo firewall-cmd --get-services

To add a service that should be allowed, use the --add-service flag:

sudo firewall-cmd --add-service=http --permanent

The --permanent option means persist rules against server reboots.

This would add the http service and allow incoming TCP traffic to port 80.

Enable http & https

8. To enable both http and https on a single line, type:

sudo firewall-cmd --add-service={http,https} --permanent

9. The configuration will update after you reload the firewall:

sudo firewall-cmd --reload
Security Warning!

ALWAYS reload firewalld after making changes to the configuration.

Installing PHP-FPM (FastCGI Process Manager)
What is PHP-FPM?

The FPM (FastCGI Process Manager) is an alternative PHP FastCGI implementation with some additional features (mostly) useful for heavy-load sites. PHP-FPM has two major portion of configuration and also uses php.ini to load PHP configuration.

Global Directives (php-fpm.conf) & Pool Directives (default is www.conf, you can name it site you site's purpose eg. blog.conf, forum.conf, etc.)

Windows does not support FPM (FastCGI Process Manager).

Notice that the apt package manager recognizes php-fpm as the install keyword but after installation, Ubuntu recognizes it by php7.4-fpm instead. Yeah. You're welcome.

Installation

In this step, we will be adding php7.4-fpm to our Ubuntu Droplet.

1. To install php-fpm without confirmation, type:

sudo apt install php-fpm -y

Also install php-curl & php-mysql modules without confirmation:

sudo apt install php-curl -y
sudo apt install php-mysql -y

The two PHP modules listed above are required if you are going to use PHP PDO to communicate with your Digital Ocean Managed MySQL Database.

Next, we uncomment the following 3 lines in php.ini file to make php7.4-fpm aware that the modules we've just installed are to be used.

To edit php.ini, type:

sudo nano /etc/php/7.4/fpm/php.ini

Uncomment the following extensions:

;extension=curl
;extension=mysqli
;extension=pdo_mysql

After uncommenting, remember to save the php.ini file by pressing Ctrl + O to Save, Enter and Ctrl + X to return to the command prompt.

2. To enable php7.4-fpm to start on system boot, type:

sudo systemctl enable php7.4-fpm

3. To start php7.4-fpm service, type:

sudo systemctl start php7.4-fpm

4. Check status of the service to make sure it started:

sudo systemctl status php7.4-fpm

5. To Restart the service, type:

sudo systemctl restart php7.4-fpm

The PHP-FPM process manager is configured out of the box for the most part. To use it, simply start the process and you are ready to go.

php-fpm.conf

1. To list php processes and find the location of php-fpm.conf file, type:

sudo ps aux | grep php

Command description:

ps = process status

a = show processes for all users

u = display the process's user/owner

x = also show processes not attached to a terminal

| grep php = filter by keyword "php"

The command output should contain the path to php-fpm.conf file.

/etc/php/7.4/fpm/php-fpm.conf

Next we navigate to this file in order to find the unix socket file path php7.4-fpm.conf is using to accept incoming requests after it was installed.

2. To open the php-fpm.conf file, type:

sudo nano /etc/php/7.4/fpm/php-fpm.conf

At the end of the php-fpm.conf file, you should find the following line:

include = /etc/php/7.4/fpm/pool.d/*.conf

This means our PHP-FPM process manager is using configurations from the /pool.d/ directory.

www.conf

By default, php-fpm places a single file called www.conf inside the /pool.d/ directory described above.

This file is then loaded every time the php7.4-fpm service is started.

To open the www.conf file, type:

sudo nano /etc/php/7.4/fpm/pool.d/www.conf

When viewing the www.conf file, you can see that the php7.4-fpm (process manager) is using a UNIX socket file to accept incoming PHP script requests.

The address on which to accept FastCGI requests:

listen = /run/php/php7.4-fpm.sock

We will use this path & file name /run/php/php7.4-fpm.sock in the fastcgi_pass directive of an NGINX location block so that nginx handles all the HTML pages while php7.4-fpm handles all the PHP pages; coming up next, in web server setup guide.

Security Warning!

ALWAYS reload php7.4-fpm after making changes to php.ini file.

Installing NGINX Web Server
The web server is nginx 1.18.0

Installation

In this step, we will be adding nginx 1.18.0 to our Ubuntu Droplet.

1. To Show public IP address, type:

curl ifconfig.co

2. To install nginx without confirmation, type:

sudo apt install nginx -y

3. To enable nginx to start on system boot, type:

sudo systemctl enable nginx

4. To start nginx service, type:

sudo systemctl start nginx

5. Check status of the service to make sure it started:

sudo systemctl status nginx

6. To restart the service, type:

sudo systemctl restart nginx

nginx.conf

The nginx.conf file is loaded by nginx during startup and contains all of the directives required to run the server.

By default, nginx places the nginx.conf configuration file in the following directory:

/etc/nginx/nginx.conf

7. To open the nginx.conf file, type:

sudo nano /etc/nginx/nginx.conf

About the default nginx.conf file...

The default nginx.conf file is basic, for serving static files only and security at this point is non-existing.

Since we are not setting up an https server, we will replace the default configuration file with a more secure version. This is as close to secure as I can get you, using NGINX as an http-protocol only server.

Below is the nginx.conf file we are going to use instead.

Security Warning!

ALWAYS reload nginx after making changes to nginx.conf file.

Download Templates
The comments in the config file should be more than enough for you to learn what's going on. You get to look at the code and look at the comments side by side which I've found is much more helpful.

The nginx templates below achieve an A score @ securityheaders.com WITHOUT USING HTTPS. To achieve an A+ score, you have to use https header Strict-Transport-Security which is again part of an HTTPS implementation and not covered in this guide.

Download it. Look at it. Break it. And learn it.


Quick Download

1. Download template nginx.conf, type:

sudo curl -o /etc/nginx/nginx.conf https://www.npo.run/v1/templates/nginx.file

2. Download template fastcgi.conf, type:

sudo curl -o /etc/nginx/fastcgi.conf https://www.npo.run/v1/templates/fastcgi.file
View nginx.conf View fastcgi.conf

Quick Setup

After downloading config files to their locations, we create the site root and download the templates. Finally we test nginx.conf then restart OR reload nginx.

3. To create the site root used in the nginx.conf file, type:

sudo mkdir -p /var/www/example1.com/public

4. To download the index template index.html, type:

sudo curl -o /var/www/example1.com/public/index.html https://www.npo.run/v1/templates/index.file

5. To download the nginx logo for index.html, logo.png, type:

sudo curl -o /var/www/example1.com/public/logo.png https://www.npo.run/v1/templates/logo.file

6. To download the nginx favicon, favicon.ico, type:

sudo curl -o /var/www/example1.com/public/favicon.ico https://www.npo.run/v1/templates/favicon.file
Customizing error pages

7. To download custom error pages http-error.tar compressed directory to your /home directory for extraction, type:

sudo curl -o /home/http-error.tar https://www.npo.run/v1/templates/http-error.file

7.1 To extract downloaded http-error.tar directory to your site root /var/www/example1.com/public directory, type:

sudo tar xf /home/http-error.tar -C /var/www/example1.com/public

7.2 To remove downloaded http-error.tar file from your /home directory after extraction, type:

sudo rm -f /home/http-error.tar

7.3 To replace the absolute path for the nginx logo.png found inside each error page html file now located in the /http-error directory, type:

sudo sed -i 's/example1.com/example2.com/g' /var/www/example1.com/public/http-error/*.html

In the snipet above we loop through each html file inside the recently extracted /http-error directory, find the string example1.com and replace it with the string example2.com where example2.com is the domain name you are going to be using; that you actually own.

7.4 To rename the physical directory of your site to match/reflect this update:

sudo mv -T /var/www/example1.com /var/www/example2.com

7.5 To update nginx.conf to match/reflect this update:

sudo sed -i 's/example1.com/example2.com/g' /etc/nginx/nginx.conf
Restarting the engine...

8. To test the nginx.conf file before restart OR reload, type:

sudo nginx -t

9. To restart the service, type:

sudo systemctl restart nginx

9.1 OR To reload nginx.conf WITHOUT RESTARTING the service, type:

sudo systemctl reload nginx
Server Online

At this point we have nginx & php7.4-fpm online and ready to accept incoming requests.

To Show Digital Ocean Dropet public IP address, type:

curl ifconfig.co

To Visit your Digital Ocean Dropet, open up your browser and go to:

http://droplet_ip_address
Getting a domain for your site
Right now you got a Droplet running but it can only be reached via IP address eg. http://droplet_ip_address

After acquiring a domain from Google Domains, you need to point it to your http://droplet_ip_address.

Login to your Google Domains Control Panel, click on Get a new domain and follow the steps.

1. Once you have purchased a domain it will appear under the My domains tab.

2. Click on your domain to arrive at the Domain overview tab.

3. Click the DNS tab.

4. Scroll to the Custom resource records section.

5. To add an A Record select A from the drop down box to enter your droplet_ip_address.

5. To add a CNAME Record select CNAME from the drop down box to enter your example1.com.

Responsive image

DNS Propagation

While Google is quite fast at doing this, it may take some time for your domain DNS records to propagate through the internet.

You may use DNS Checker to verify your domain name has fully propagated.

Server + Domain Online
Light Maintenance
This section contains a list of linux commands pertaining to the scope of this guide.

Commands available for user account management, basic file and directory operations, file viewing commands, manage and monitor system, basic networking commands.

To login to your Droplet, open up the command prompt CMD (Windows) or BASH (Linux) and type:

ssh root@droplet_ip_address

To list all nginx access.log files, type:

ls /var/log/nginx/access.*

To truncate all nginx access.log files, type:

sudo truncate /var/log/nginx/access.* --size 0

To delete all nginx access.log files, type:

sudo rm -f /var/log/nginx/access.*

To list all nginx error.log files, type:

ls /var/log/nginx/error.*

To truncate all nginx error.log files, type:

sudo truncate /var/log/nginx/error.* --size 0

To delete all nginx error.log files, type:

sudo rm -f /var/log/nginx/error.*