Set Up Nginx Server on Ubuntu
After creating a new instance on a VPS and establishing ssh access to this server environment, the next step would be launching a server to host website. The two most popular server protocols are Apache and Nginx. Apache used to be the dominant choice but in recent years Nginx has caught up quickly and is now more popular than Apache among the biggest websites in the world. We'll go with Nginx.
1) Associate domain name to server IP
First we need to tell hosting platform to point the domain name (for example site-name
) to the public IP of the virtual machine 123.456.789.000
(let's call it athena
). I'm on Linux distro Ubuntu 20.04.
In the DNS Zone file (managed by domain registrar such as Godaddy) for the domain name site-name
, create the following entries:
Type | Hostname | Value | TTL (seconds) |
---|---|---|---|
A | @ | 123.456.789.000 | 60 |
A | www | 123.456.789.000 | 60 |
Though TTL (Time To Live) is set at the lowest possible value of 60
seconds, the actual propagation of the new A record through out layers of the Internet may take longer than that. The next step is optional but would be helpful to speed up the process.
In athena
, open the hosts
configuration file
sudo vim /etc/hosts
Add this entry and save the file
123.456.789.000 site-name
Back to the local Mac (I'm on macOS Big Sur 11.2.0), flush the local DNS cache, so that local Mac would not continue to point the IP address to its old domain name.
sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder
2) Set up Nginx Server
Login to athea
via a user with root access and install Nginx.
sudo apt-get install nginx
To start Nginx
sudo systemctl start nginx
To stop Nginx
sudo systemctl stop nginx
To restart Nginx
sudo systemctl restart nginx
To reload Nginx without restarting
sudo systemctl reload nginx
Check if Nginx server is now running
service nginx status
or
systemctl is-active nginx
active
3) Set up firewall
Firewall is managed through UFW
(or Uncomplicated Firewall) utility. It should be installed by default as part of the standard packages from Ubuntu. If it's uninstalled for some reason, install it with
sudo apt-get install ufw
Enable firewall
sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup
When firewall is enabled, other connections will be lost. We need to tell UFW to allow access for Nginx and ssh.
sudo ufw allow 'Nginx Full'
sudo ufw allow ssh
Reload UFW to turn on the changes
sudo ufw reload
Check UFW status
sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
80,443/tcp (Nginx Full) ALLOW IN Anywhere
22/tcp ALLOW IN Anywhere
80,443/tcp (Nginx Full (v6)) ALLOW IN Anywhere (v6)
22/tcp (v6) ALLOW IN Anywhere (v6)
To stop the firewall
sudo ufw disable
UFW's configuration files can be accessed at /etc/ufw
on Ubuntu 20.04
.
4) Set up directory structure for the website
Usually the files that host websites are put under /var/www
in a typical Ubuntu instance.
mkdir -p /var/www
mkdir -p /var/www/site-name
mkdir -p /var/www/site-name/public_html
mkdir -p /var/www/site-name/log
mkdir -p /var/www/site-name/backups
Make sure Nginx can access www
with the right permission
chmod 0755 ~/var/www
5) Configure Nginx for the new website
cd /etc/nginx
Before making any changes, make a backup copy of the important configuration file nginx.conf
sudo cp nginx.conf nginx.conf.bak.YYYYMMDD
Take a look at the currently activated websites
cd /etc/nginx/sites-enabled/
ls -la
drwxr-xr-x 2 root root 4096 Jun 13 04:48 .
drwxr-xr-x 8 root root 4096 Jun 13 05:04 ..
lrwxrwxrwx 1 root root 34 Jun 13 04:48 default -> /etc/nginx/sites-available/default
No website is activated besides the default one. Let's enable a new website for site-name
.
Enter into sites-available
directory
cd /etc/nginx/sites-available
There is already a default
configuration file in this directory that can be used as a guide/reference as it explains many frequently used parameters.
Create a new configuration file for the website site-name
sudo vim site-name
Copy and paste the following into configuration file site-name
with appropriate changes. /var/www/site-name/public_html
is the path that contain the files for the public website where index.html
will be served to usher viewers into the landing page of the website. server-name
should be the exact domain name that you control, such as sitename
.
server {
listen 80;
server_name site-name www.site-name;
root /var/www/site-name/public_html;
index index.html;
}
Create a symbolic link to enable the website site-name
sudo ln -s /etc/nginx/sites-available/site-name /etc/nginx/sites-enabled/
When making future changes in the configuration file for site-name
, changes should be made in the site-name
configuration file in directory sites-available
. The link in sites-enabled
points to the configuration file in sites-available
directory.
6) Create a dummy landing page
Let's create a bare-bone website to test things out
sudo vim /var/www/site-name/public_html/index.html
Type something simple into index.html
Hello World! Welcome to Site-Name!
Open this website https://site-name
in a browser to check if the landing page can be loaded properly.
Though not showing anything meaningful yet at this point, the website is now up and running. All the changes to index.html
will be reflected when https://site-name
is visited or refreshed.
7) Turn off logs
Logging can take up substantial disk storage and system bandwidth. To turn it off
sudo vim /etc/nginx/nginx.conf
Find the logging settings and add #
to comment them out
##
# Logging Settings
##
#access_log /var/log/nginx/access.log;
#error_log /var/log/nginx/error.log;
8) Set up HTTPS
It's critically important to enable the secure HTTP protocol on a newly launched website, otherwise data will be transmitted in plain text. Every URL should now start with https
rather than good old http
. Increasingly the most popular browsers will deem an http
address as a security risk and even prevent it from being visited. In HTTPS, the communication protocol is encrypted using SSL (Secure Sockets Layer). In the past, enabling SSL could be quite a manual process as a certificate needs to be generated to authenticate the website. With the wonderful (and free!) Let's Encrypt tool, this pain is largely alleviated.
sudo apt-get install software-properties-common
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install python3-certbot-nginx
Run certbot
sudo certbot --nginx
Which brings up the below dialog. Certbot will automatically look for enabled sites in /etc/nginx
directory. Just follow the on-screen instructions to complete the setup.
Which names would you like to activate HTTPS for?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: site-name
2: www.site-name
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel):
If all goes well, you will see a congratulations message when the certificate is generated successfully for the chosen sites. Restart the Nginx server to make the new changes effective
sudo systemctl restart nginx
If need to renew the certificate
sudo certbot renew
When you install the certificates using certbot it automatically creates cron job to renew certificates. For Ubuntu this cron job can be found at /etc/cron.d/certbot
cat /etc/cron.d/certbot
# /etc/cron.d/certbot: crontab entries for the certbot package
#
# Upstream recommends attempting renewal twice a day
#
# Eventually, this will be an opportunity to validate certificates
# haven't been revoked, etc. Renewal will only occur if expiration
# is within 30 days.
#
# Important Note! This cronjob will NOT be executed if you are
# running systemd as your init system. If you are running systemd,
# the cronjob.timer function takes precedence over this cronjob. For
# more details, see the systemd.timer manpage, or use systemctl show
# certbot.timer.
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
0 */12 * * * root test -x /usr/bin/certbot -a \! -d /run/systemd/system && perl -e 'sleep int(rand(43200))' && certbot -q renew
This cron job gets triggered twice every day to renew certificates. certbot -q renew
will check if certificate is expiring in the next 30 days. If it is, it will auto renew it quietly without generating output. If it is not expiring, then it will not perform any action. The same information such as email, domain name, web server root will be provided when renewing the certificate.
9) Enable HTTP/2
It's a good practice to enable HTTP/2 for stronger security
sudo vim /etc/nginx/sites-available/site-name
Find the listen line for certbot
listen 443 ssl;
Change that to
listen 443 ssl http2;
Restart the Nginx server
sudo systemctl restart nginx
Voila! Now the website at https:///site-name.com
is up and running and presented with view from index.html
file with other contents (such as images) served from /var/www/site-name/public_html/
directory in a virtual instance hosted on server managed with public IP 123.456.789.000
. Next step would be turning index.html
into something for real and set up a continuous integration process for code deployment, but that would be another topic for another day.
1. Thanks for reading! 1082.xyz newsletter shares the learning and discovery for aspiring polymath, on crypto, blockchain, investment, startups, productivity hacks and technology in general. If you're forwarded this email, you can sign up for the free 1082.xyz newsletter here or just press the "Subscribe" button to receive the latest articles directly in your inbox. The free tier would be just fine to receive all available contents.
2. You're welcome to share your feedback and comments in the comment section. No email registration is needed. You can even do so anonymously.
3. If you find this article helpful, please share it with like-minded friends. Thank you.