Skip to content

Instantly share code, notes, and snippets.

@manifestinteractive
Last active October 8, 2020 04:45
Show Gist options
  • Save manifestinteractive/37f765c61ebcf00aa2bf5d534035fa7f to your computer and use it in GitHub Desktop.
Save manifestinteractive/37f765c61ebcf00aa2bf5d534035fa7f to your computer and use it in GitHub Desktop.
DigitialOcean Nginx Config Files

DigitialOcean New Droplet Setup.

My setup for creating a new Droplet is specific for my needs, which is usually just nginx with SSL & HTTP2. I am also usually running more than one website on a droplet, so I typically setup nginx to use Virtual Hosts. I also like using Zsh, and specifically, Oh My Zsh.

Create a new Ubuntu 20.04 Droplet

Follow DitigalOceans suggested Initial Server Setup with ubuntu 16.04 before proceeding.

Once you have created your new Droplet, I am assuming you have your new Droplet's IP Address, and that you have also added your SSH keys so you can access remotely.

OK, now that you have a barebones Droplet, we can get started setting it up. Here is how I setup mine:

Login to Droplet over SSH:

In a new terminal window type the following:

ssh myusername@123.45.67.890

Update & Install Required Packages:

sudo apt-get update
sudo apt-get install nginx -y
sudo apt-get install git -y
sudo apt-get install zsh -y
sudo apt-get install unzip -y
sudo apt-get install --reinstall build-essential -y

At this point you may wish to also check out the Security / Firewall discussions on DigitalOceans How To Install Nginx on Ubuntu 16.04.

Customize Greeting:

I personally do not like seeing a message every time I login, so you can hush that login message via:

touch ~/.hushlogin

Setup Terminal Preferences:

Next I install Zsh:

sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"

Then I add a really sweet zsh-syntax-highlighting plugin that I LOVE:

cd ~
git clone https://github.com/zsh-users/zsh-syntax-highlighting.git
echo "source ${(q-)PWD}/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" >> ${ZDOTDIR:-$HOME}/.zshrc
source ./zsh-syntax-highlighting/zsh-syntax-highlighting.zsh

Then I add a really sweet zsh-autosuggestions plugin that I LOVE:

git clone git://github.com/zsh-users/zsh-autosuggestions $ZSH_CUSTOM/plugins/zsh-autosuggestions

Finally, I edit the ~/.zshrc file ( after making a backup file at ~/.zshrc.backup ):

cp ~/.zshrc ~/.zshrc.backup
cat /dev/null > ~/.zshrc
nano ~/.zshrc

and replace the entire contents of the file with:

export ZSH=$HOME/.oh-my-zsh
ZSH_THEME="robbyrussell"
CASE_SENSITIVE="true"
HYPHEN_INSENSITIVE="true"
DISABLE_AUTO_TITLE="true"
ENABLE_CORRECTION="true"
COMPLETION_WAITING_DOTS="true"
DISABLE_UNTRACKED_FILES_DIRTY="true"

case $TERM in
    xterm*)
        precmd () {print -Pn "\e]0;Digital Ocean Droplet\a"}
        ;;
esac

plugins=(git zsh-autosuggestions)

export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games"

source $ZSH/oh-my-zsh.sh
source $HOME/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh

Next, if you want to make nano a little prettier to use, you can do the following:

curl https://raw.githubusercontent.com/scopatz/nanorc/master/install.sh | sh

Preparing for SSL Usage:

I know I am going to be using SSL on this new Droplet, so I go ahead and get that ready:

sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
sudo apt install certbot python3-certbot-nginx

Then you can go ahead and setup CRON jobs to automagically update any SSL certs you end up installing:

sudo crontab -e

Then add the following to your CRON tab:

30 2 * * 1 /opt/letsencrypt/letsencrypt-auto renew >> /var/log/le-renew.log
35 2 * * 1 /bin/systemctl reload nginx

ALL DONE

Now, whenever I am ready to setup a new website on this Droplet, I can just hop on over to my other documentation for this :)

DigitialOcean Nginx Website Setup ( Virtual Hosts )

Optional Setup

So what if you want to install some other stuff, like PHP or MySQL? I keep track of those installs steps below, but don't always need them:

There is a really good writeup on getting both PHP & MySQL setup on DigitalOceans How To Install Linux, Nginx, MySQL, PHP (LEMP stack) in Ubuntu 16.04 document. But some of the stuff is just running basic commands in terminal:

MySQL

Run the following:

sudo apt-get install mysql-server -y
sudo mysql_secure_installation

Then follow the on screen prompt. You will be asked to create a new password, so you might want to create that ahead of time.

Now, its probably a good idea to not just have the root user on there, so let's create a custom user we'll use for MySQL by running the following in terminal:

mysql -u root -p

When prompted, enter the password you just setup when installing MySQL.

You will probably not have a database yet, so let's go ahead and create one:

  • Change all instances of mydatabase to whatever you want to call your database
  • Change all instances of newuser to whatever you want your new username to be
  • Change all instances of password to whatever you want your new password to be
CREATE DATABASE mydatabase;

Now, let's create our new user and give them access to this database ( and ONLY this database ):

CREATE USER 'newuser'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON mydatabase.* TO 'newuser'@'localhost';
FLUSH PRIVILEGES;
exit;

PHP

Cool, now that you have MySQL setup, maybe you want to use PHP to connect to it. Or maybe you just want PHP and don't need MySQL right now, here is how to get that setup:

sudo apt-get install php-fpm php-mysql -y

Now you will need to tweak one setting in your php.ini file to get things running smoothly:

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

Look for the a commented out line of ;cgi.fix_pathinfo=1, uncomment it, and change it to cgi.fix_pathinfo=0. Save, then exit :)

Now you need to restart PHP so the changes take affect:

sudo systemctl restart php7.0-fpm

Create a Temp Swap Drive:

I recently discovered that running npm install on larger projects seemed to be randomly failing at different points throughout the install process. After a bit of research, I found the issue was memory related, and creating a Swap Drive could be a temp solution. It is important to note that Swap Files on SSD drives are not recommended. DigitalOcean uses SSD. However, for my case it did not make sense for me to upgrade the entire server for a memory issue because of a single command I needed to run one time. So, if you are only going to run a swap file for a minute or so, here are the steps you need to create a Temp Swap Drive:

sudo fallocate -l 1G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

When you are done, run the following:

sudo swapoff /swapfile

The swapfile will automatically be disabled on restart.

Setup website.com

Change all instances of website.com to your actual website's domain.

Create Website Root

sudo mkdir -p /var/www/website.com/html

Setup Nginx Config File

sudo nano /etc/nginx/sites-available/website.com

Then paste in the contents of website.com template file.

Make Symbolic Link to Enable New Website

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

Create SSL Certificate

NOTE: This won't work until DNS points to this newly created server.

cd /opt/letsencrypt
./letsencrypt-auto certonly -a webroot --webroot-path=/var/www/website.com/html -d website.com -d www.website.com

Create a new SSL config file for the new website

sudo nano /etc/nginx/snippets/ssl-website.com.conf

Paste in the following

ssl_certificate /etc/letsencrypt/live/website.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/website.com/privkey.pem;

Update Permissions for New Folders & Files

Do this after uploading all your content.

sudo chmod -R u+rwX,go+rX,go-w /var/www/website.com/html
sudo chown -R $USER:$USER /var/www/website.com/html

Testing New Settings & Restarting Nginx

Now that you have all your files setup, you can quickly test your nginx configuration by running the followin:

sudo nginx -t

You should see something like this, telling you everything is OK:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

If something was wrong, it will tell you which file has an issue, and you should update that file and restest.

Once everything is verified to be correct, you can run the following to apply your new changes:

sudo systemctl restart nginx
# /etc/nginx/snippets/basic.conf
#Specify a charset
charset utf-8;
# Setup Content Encoding
gzip on;
gzip_min_length 1100;
gzip_buffers 4 32k;
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
gzip_vary on;
# Force the latest IE version
add_header "X-UA-Compatible" "IE=Edge";
# Expire rules for static content
# cache.appcache, your document html and data
location ~* \.(?:manifest|appcache|html?|xml|json)$ {
expires -1;
}
# Feed
location ~* \.(?:rss|atom)$ {
expires 1h;
}
# Media: images, icons, video, audio, HTC
location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
expires 1M;
access_log off;
add_header Cache-Control "public";
}
# CSS and Javascript
location ~* \.(?:css|js)$ {
expires 1y;
access_log off;
}
# Cross domain webfont access
location ~* \.(?:ttf|ttc|otf|eot|woff|woff2)$ {
# Cross domain AJAX requests
# http://www.w3.org/TR/cors/#access-control-allow-origin-response-header
# **Security Warning**
# Do not use this without understanding the consequences.
# This will permit access from any other website.
#
add_header "Access-Control-Allow-Origin" "*";
# Instead of using this file, consider using a specific rule such as:
#
# Allow access based on [sub]domain:
# add_header "Access-Control-Allow-Origin" "subdomain.example.com";
expires 1M;
access_log off;
add_header Cache-Control "public";
}
# Prevent clients from accessing hidden files (starting with a dot)
# This is particularly important if you store .htpasswd files in the site hierarchy
# Access to `/.well-known/` is allowed.
# https://www.mnot.net/blog/2010/04/07/well-known
# https://tools.ietf.org/html/rfc5785
location ~* /\.(?!well-known\/) {
deny all;
}
# Prevent clients from accessing to backup/config/source files
location ~* (?:\.(?:bak|conf|dist|fla|in[ci]|log|psd|sh|sql|sw[op])|~)$ {
deny all;
}
location ~ \.(?:css|htc|js|js2|js3|js4)$ {
gzip_vary on;
}
# PHP Support
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
}
location ~ /\.ht {
deny all;
}
# /etc/nginx/snippets/error-pages.conf
error_page 403 /error_403.html;
error_page 404 /error_404.html;
error_page 500 /error_500.html;
error_page 503 /error_503.html;
error_page 504 /error_504.html;
location = /error_403.html {
root /usr/share/nginx/html/error-pages;
internal;
}
location = /error_404.html {
root /usr/share/nginx/html/error-pages;
internal;
}
location = /error_500.html {
root /usr/share/nginx/html/error-pages;
internal;
}
location = /error_503.html {
root /usr/share/nginx/html/error-pages;
internal;
}
location = /error_504.html {
root /usr/share/nginx/html/error-pages;
internal;
}
# /etc/nginx/nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 768;
use epoll;
multi_accept on;
}
http {
# Basic Settings
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
server_tokens off;
server_names_hash_bucket_size 64;
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains: always;";
include /etc/nginx/mime.types;
default_type application/octet-stream;
# SSL Settings
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
# Logging Settings
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# Gzip Settings
gzip on;
gzip_disable "msie6";
# Virtual Host Configs
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
# /etc/nginx/snippets/ssl-params.conf
# from https://cipherli.st/
# and https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1h;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# Disable preloading HSTS for now. You can use the commented out header line that includes
# the "preload" directive if you understand the implications.
#add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
# /etc/nginx/snippets/ssl-website.com.conf
ssl_certificate /etc/letsencrypt/live/website.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/website.com/privkey.pem;
# /etc/nginx/sites-available/website.com
# IMPORTANT:
#
# You cannot use this until you have added SSL certificates to your server
# until then, you should use the template in `website.com`
server {
listen 80;
listen [::]:80;
server_name website.com www.website.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
include snippets/ssl-website.com.conf;
include snippets/ssl-params.conf;
include snippets/basic.conf;
include snippets/error-pages.conf;
root /var/www/website.com/html;
index index.php index.html index.htm index.nginx-debian.html;
server_name website.com www.website.com;
location / {
try_files $uri $uri/ =404;
}
}
# /etc/nginx/sites-available/website.com
# IMPORTANT:
#
# You should use this for a new website untl you are able to access it
# using a browser. Before you can setup SSL on a website, it will need
# to be accessible online at both http://website.com & http://www.website.com
# Until both of these URL's work, you cannot create an SSL certificate.
#
# Once your SSL certificates are installed, you will need to switch this config
# file for the one in `website-ssl.com` so you are using HTTPS with SSL certs.
server {
listen 80;
listen [::]:80;
include snippets/basic.conf;
include snippets/error-pages.conf;
root /var/www/website.com/html;
index index.php index.html index.htm index.nginx-debian.html;
server_name website.com www.website.com;
location / {
try_files $uri $uri/ =404;
}
}
@manifestinteractive
Copy link
Author

ssl-params.conf file assumes you have created the /etc/ssl/certs/dhparam.pem file by running the following command in terminal:

sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048

See https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-16-04 for more details

@manifestinteractive
Copy link
Author

For the error-pages.conf file located in /etc/nginx/snippets/error-pages.conf I use my custom error pages using Disaster Girl ... because she's awesome. Instructions for using these are on the page.

https://github.com/manifestinteractive/disaster-girl-error-pages

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment