Setting up your Linode
If you are not familiar with Linode, they are a provider of VPS (Virtual Private Servers) which is basically a semi-private version of shared hosting. You generally don’t have to share the box with hundreds of other people and even if you do you are guaranteed a certain share of the resources (CPU / RAM / etc). Not to mention the server hardware itself is extremely beast as far as power. You are in your own little private eco-system meaning you and only you have access to your machine (or slice of a machine rather). You will not experience the same horrible load times and transfer rates that you would get at companies like Go Daddy and Dreamhost (shudder). The only down side is you do not have a CPanel or Plesk panel (though you can install one if you wish, but I do not use them and will not go into that here) so you have to install and manage everything yourself. It’s not as hard as you might think if you can get used to searching Google and reading a lot of tutorials.
Command line can be a tricky beast for beginners, this blog aims to help ease some of that pain by sharing the procedures that I usually take when setting up my web server. Please be advised that with every new version of Linux the steps can vary a bit due to packages being upgraded or removed from the repositories.
This guide focuses on configuring the latest and greatest (at the time of this article) version of Ubuntu on a freshly provisioned Linode server. The exact system specs do not matter that much, you can use the lowest 1G plan if you like, the steps will be the same. I’m using the cheapest plan that costs $10 a month. You can check out Linode’s prices and decide for yourself.
Provision your Linode
This is the easiest step thanks to Linode’s user friendly UI. Simply add a new Linode, select your desired size from the options presented. You can always increase the size at a later date so feel free to select the smallest size you are comfortable with. On the dashboard for the newly created Linode you will need to find the link “Deploy a Linux Distribution”. Select the latest version of Ubuntu which is at the time of this article “Ubuntu 16.04 LTS”. Type a root password in the input box and hit the Deploy button. Wait for the tasks in the job queue to all show success and then click the “boot” button to fire your machine up.
The first stage is finished, congratulations! Now for the real fun stuff!
Connect to your server
Open up a terminal so you can connect to your newly created Linode server. If you are on Linux or Mac this should be pretty self explanatory, but on Windows you have to install a third party software called Putty. In putty you will have a GUI to type in the IP address and user. You can get your IP address from the Linode Manager simply click “Linodes” on the top menu and it should list your Linodes with the IP address visible. It is also visible on the “Remote Access” tab of your Linode’s Dashboard.
1 |
ssh root@<ip_address_here> |
You will then be prompted to provide the root password that you specified when you provisioned the Linode. This will log you into the server and drop you at the command line.
Basic Ubuntu Setup
The following stuff is some pretty basic Ubuntu configuration you should make before doing anything else.
Set the hostname
This can be anything, generally I just specify a short version of the full domain. (i.e. mydomain.com would become cooldomain)
1 2 |
echo "mydomain" > /etc/hostname hostname -F /etc/hostname |
You can verify it was set correctly by typing:
1 |
hostname |
Set the fully qualified domain name
Now you can set the FQDM by making sure the following is in the /etc/hosts file.
1 2 3 4 5 6 7 8 |
127.0.0.1 localhost 127.0.1.1 ubuntu.members.linode.com ubuntu <ip_address> <full_domain> <hostname> # The following lines are desirable for IPv6 capable hosts ::1 localhost ip6-localhost ip6-loopback ff02::1 ip6-allnodes ff02::2 ip6-allrouters |
Set the time
1 |
dpkg-reconfigure tzdata |
You can verify that it’s correct by typing:
1 |
date |
System updates
Now is a good time to make sure you have all the latest system updates
1 2 |
apt-get update apt-get upgrade |
Adjust swappiness
I generally like to modify swappiness so it only uses swap if it absolutely has to. You can do this pretty simply like this
1 2 3 |
sudo swapoff -a echo 0 | sudo tee /proc/sys/vm/swappiness sudo swapon -a |
Now we need to add a line on sysctl.conf to make sure it keeps the setting after a reboot. So edit /etc/sysctl.conf and add this line to the bottom
1 |
vm.swappiness=0 |
Security Precautions
Here’s some basic steps you can take to improve the security of your server a bit.
Create new user
You should not be connecting as root on a regular basis, so we will create a new user that you can use from now on.
1 |
adduser <username> |
Then you are going to want to put that user into the sudo group so you can execute commands as root
1 |
usermod -a -G sudo <username> |
Now we need to test your new account to make sure it’s working because the next step is to disable logging in as root.
1 2 |
exit ssh <username>@<ip_address_here> |
Disable root login
If you can log in successfully then we are good to go for the next stage, lets disable root login.
1 |
sudo nano /etc/ssh/sshd_config |
Look for the line that says “PermitRootLogin” and change it to “no”
NOTE You can also change the SSH port in the sshd_config file if you desire. Just change “Port” to whatever you like. The default is 22.
Save the file and restart sshd
1 |
sudo service ssh restart |
If you changed your port you will have to login as follows from now on:
1 |
ssh <username>@<domain> -p <port> |
Install UFW
Enabling a firewall is a pretty crucial part of any secure server. You can use either iptables or ufw. I have recently started using ufw because it’s really easy and no fuss.
1 |
sudo apt-get install ufw |
This will install the firewall now we need to configure it.
Open some ports
Now lets open some common ports that we want to use. http, https, ssh, ftp, mail, etc
1 2 3 4 5 6 7 8 |
sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw allow 587/tcp sudo ufw allow 22/tcp sudo ufw allow 21/tcp sudo ufw allow 25/tcp sudo ufw allow 110/tcp sudo ufw allow 143/tcp |
Lets enable it now
1 |
sudo ufw enable |
Pretty simple eh?
Install Fail2Ban
Fail2Ban is a security tool to prevent dictionary attacks. It works by monitoring important services (like SSH) and blocking IP addresses which appear to be malicious (i.e. they are failing too many login attempts because they are guessing passwords).
1 |
sudo apt-get install fail2ban |
Now we need some basic configuration.
1 |
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local |
Restart the service for the changes to take effect.
1 |
sudo service fail2ban restart |
Reboot server on out-of-memory condition
This is a really neat feature to turn on. Basically if your server runs out of memory it will throw an exception and reboot, which will cause a few minutes of downtime but that is waaaaay better than sitting there swapping for hours and basically being non-functional anyways. Add the following to the end of the file “/etc/sysctl.conf“:
1 2 |
vm.panic_on_oom=1 kernel.panic=10 |
Setting vm.panic_on_oom to 1 tells the server to throw a kernel panic when it runs out of memory. Setting kernel.panic to 10 tells it to reboot 10 seconds after panicking.
Extras
Install FTP server
FTP server is a useful thing to have for numerous reasons, one being some software like wordpress uses it to update itself or install plugins.
1 |
sudo apt-get install vsftpd |
Now edit the config:
1 |
sudo nano /etc/vsftpd.conf |
Make sure these settings are uncommented and or created
1 2 3 |
anonymous_enable=NO local_enable=YES write_enable=YES |
Save the file and restart vsftp:
1 |
sudo service vsftpd restart |
Install some useful software
These aren’t required but this is a running list of some stuff I generally install on new servers
1 |
apt-get install build-essential screen htop unrar-free unzip git-core zip zlibc rsync dnsutils mcrypt libtool libyaml-dev tcl8.5 libreadline-gplv2-dev libssl-dev libpcre3-dev libbz2-dev cmake libjson0-dev make gcc libboost1.55 libexpat1 libexpat1-dev libyajl-dev libyajl2 git cmake libgcrypt11-dev libcurl4-openssl-dev automake autoconf pkg-config libcurl4-openssl-dev intltool libxml2-dev libgtk2.0-dev libnotify-dev libglib2.0-dev libevent-dev checkinstall software-properties-common python-software-properties |
Forward domain e-mail to Gmail
Setting up an e-mail server is a very complicated and often frustrating experience. For that reason I do not use it myself. Instead I simply want email sent to my various domains to be forwarded to my personal Gmail address. This is what I will cover below. We start by installing postfix
1 |
sudo apt-get install postfix |
Just leave all the settings default
Now we need to do a little tweaking to the config file.
1 |
sudo nano /etc/postfix/main.cf |
Add the following lines at the end (replace
1 2 |
virtual_alias_domains = <domain> virtual_alias_maps = hash:/etc/postfix/virtual |
You will also need to add any domains you wish to forward emails from onto the mydestination line like so
1 |
mydestination = <domain1>, <domain2>, <domain3>, localhost.com, localhost |
Save the file and open up the virtual config:
1 |
sudo nano /etc/postfix/virtual |
Now you can configure your forwarding rules. The most basic is a catch-all but you can also input specific email addresses and where you want them forwarded. Here is a catch all example:
1 |
@<domain> <personal gmail address> |
For a specific address you just add the preceeding mailbox
1 |
admin@<domain> <personal gmail address> |
Save the file when you are finished and run postmap, this will load the new forwarding rules.
1 |
sudo postmap /etc/postfix/virtual |
Go ahead and restart postfix for good measure.
1 |
sudo service postfix restart |
It’s important to note that if you are forwarding to a gmail account any test e-mails you try sending to the domains from the same address the server is forwarding to will not reach your e-mail box. This is something on gmails end. To properly test forwarding you will need to send an email to your server from a different gmail account or from a non-gmail account.
Forward mail to system accounts to /dev/null
You may want any mail sent to specific system users to be sent to the void (aka deleted). You can’t directly send mail to /dev/null with the virtual file but you can create a system alias and have it sent there.
1 |
sudo nano /etc/aliases |
Add this line to the file
1 |
devnull: /dev/null |
Now edit your virtual database to add the forward rule
1 |
sudo nano /etc/postfix/virtual |
Add a line for each box you want sent to /dev/null
1 |
root@<domain> devnull |
Run postmap again and you are good to go
1 |
sudo postmap /etc/postfix/virtual |
Installing Nginx and PHP7-FPM
So we have a basic server configured now we need to install the most important aspect, the web server itself. I greatly prefer Nginx to Apache because of it’s much lower memory footprint / requirements. In the past I have used spawn-fcgi to run php as fastcgi to serve pages. I’ve since abandoned it for php7-fpm which is much simpler to get running and a lot less manual labor.
Install the Nginx Web Server
First things first lets install the web server.
1 |
sudo apt-get install nginx-full |
In Ubuntu 16.04 Nginx will automatically start upon installation. You can test it now to see if it’s running by inputting the domain or ipaddress of the server into your web browser. If for some reason you don’t have a domain or don’t know your ip address you can quickly get it by typing this on the command line:
1 |
ip addr show eth0 | grep inet | awk '{ print $2; }' | sed 's/\/.*$//' |
Another option is to curl one of the various ip address websites like so:
1 |
curl http://icanhazip.com |
You should see a Welcome to Nginx web page in your browser if everything went according to plan.
Install any databases you want to use
The most common databases would be either MySQL or PostgreSQL, install both of them if you like or only the ones you want to use
1 2 |
sudo apt-get install postgresql postgresql-client php7.0-pgsql libpq-dev sudo apt-get install mysql-server mysql-client libmysqlclient-dev php7.0-mysql libmysqld-dev |
Now complete the process by securing your installation
1 |
sudo mysql_secure_installation |
You’ll be asked to enter the mysql root password that you specified during installation.
After this process is finished MySQL should be ready to go.
Install PHP7
Now we need to install PHP to complete our web stack. Since Nginx does not have native PHP support we will need to run PHP as fastcgi via php7-fpm, which stands for “fastcgi process manager”. We will tell Nginx to pass PHP requests to this software for processing.
1 |
sudo apt-get install php7.0-fpm php7.0-mysql php7.0-zip memcached php7.0-gd php-pear php-apcu php7.0-cli php7.0-common php7.0-curl php7.0-mcrypt php7.0-cgi |
Now you need to configure the php processor.
1 |
sudo nano /etc/php/7.0/fpm/php.ini |
We are looking for a line “cgi.fix_pathinfo” this line should be commented out with a semi-colon and set to 1. We need to uncomment this and edit it to 0 like so:
1 |
cgi.fix_pathinfo=0 |
Save and close the file then restart php7.0-fpm
1 |
sudo service php7.0-fpm restart |
Memcached
Lets tweak the pool setting on memcached to make it more efficient.
1 |
sudo nano /etc/memcached.conf |
Now you want to find the line that begins with -m and modify it from 64 to 128
1 |
-m 128 |
Save the file and close it.
Enable Gzip Compression
Basic Gzip compression should be on by default but we want to uncomment a bunch of lines in the config to fully enable it.
1 |
sudo nano /etc/nginx/nginx.conf |
You are looking for a big block of settings that starts with “gzip on;” we want to uncomment all the lines to match below:
1 2 3 4 5 6 7 8 |
gzip on; gzip_disable "msie6"; gzip_vary on; gzip_proxied any; gzip_comp_level 6; gzip_buffers 16 8k; gzip_http_version 1.1; gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; |
Save the file and that is finished.
Now restart nginx
1 |
sudo service nginx restart |
Folder Structure and Nginx Virtual Hosts
Website folder structure
Now that we have all the packages installed we need to set up our folder structure that will house all of our domains. This is a pretty subjective topic and feel free to modify it to your liking. I’ll just supply an example folder setup that you can use if you so desire.
1 2 3 4 5 6 7 8 9 |
/home /myuser /domains /domain1.com /public /dev /private /logs /backup |
Under each domain folder I place four difference folders, this is the intended purpose of the different folders
- public
The web root for the production domain, all web accessible production files will go here. - dev
The web root for the development version of the website. You can setup a sub domain such as dev.to point here. - private
You should put any items that you do not want to be web accessible here. You can still access them through PHP to utilize in your web applications. Files here could be shared between production and dev, or in the case of ExtJS the main app code can be housed here. - logs
The nginx error and access logs should be placed here. - backup
If you want to run any sort of backups for a specific domain you can place the backup archives here.
First make the domains folder in your home directory
1 2 |
cd ~ mkdir domains |
Then you can create the folder structure for each domain with a one liner pretty easily like this
1 |
mkdir -p domains/<domain>/{public,private,dev,logs,backup} |
Then you should make the logs folder writable
1 |
sudo chmod 777 ~/domains/<domain>/logs |
Nginx Virtual Hosts
Each domain that you want Ngnix to host will need it’s own virtual host file. These are all located at “/etc/nginx/sites-available/“. When you installed Nginx it automatically created a “default” virtual host. You can either delete this virtual host or use it for your primary domain. There are a few changes you’ll need to make to it first.
1 |
sudo nano /etc/nginx/sites-available/default |
Here is the current default vhost file in it’s entirety.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# Default server configuration # 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.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 the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { # include snippets/fastcgi-php.conf; # # # With php7.0-cgi alone: # fastcgi_pass 127.0.0.1:9000; # # With php7.0-fpm: # fastcgi_pass unix:/run/php/php7.0-fpm.sock; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} } # Virtual Host configuration for example.com # # You can move that to a different file under sites-available/ and symlink that # to sites-enabled/ to enable it. # #server { # listen 80; # listen [::]:80; # # server_name example.com; # # root /var/www/example.com; # index index.html; # # location / { # try_files $uri $uri/ =404; # } #} |
You will need to change the “root” line to point to the domain’s path for the site you wish to serve
1 |
root /home/<user>/domains/<domain>/public; |
There is a line that lists the various index file types you’ll need to add index.php like so
1 |
index index.php index.html index.htm; |
You need to modify the server_name line to match your desired domain name
1 |
server_name <domain>; |
Below the server_name line add these two lines to setup the logging to our proper folders
1 2 |
access_log /home/<user>/domains/<domain>/logs/access.log; error_log /home/<user>/domains/<domain>/logs/error.log; |
If you are going to be using wordpress pretty urls on the domain you will need to add this block below the log lines
1 2 3 4 5 6 |
location / { # this sends all non-existing file or directory requests to index.php if (!-e $request_filename) { rewrite ^(.+)$ /index.php?q=$1 last; } } |
The final block you will need to add below the previous one is what sends the php traffic to php-fpm. Some form of this should already exist by default but commented out. Simply uncomment the required lines. Make sure to only uncomment one of the fastcgi_pass lines. (The one for FPM) as shown below.
1 2 3 4 5 6 7 8 9 10 |
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # location ~ \.php$ { include snippets/fastcgi-php.conf; # With php7.0-cgi alone: # fastcgi_pass 127.0.0.1:9000; # With php7.0-fpm: fastcgi_pass unix:/run/php/php7.0-fpm.sock; } |
Now for each new domain you wish to add you’ll need to add a new file to the sites-available folder. Here’s a full sample virtual host configuration.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
server { listen 80; server_name www.domain.com; return 301 $scheme://domain.com$request_uri; } server { listen 80; listen [::]:80 default_server ipv6only=on; #root /usr/share/nginx/html; root /home/<user>/domains/domain.com/public; index index.php index.html index.htm; # Make site accessible from http://localhost/ server_name domain.com; access_log /home/<user>/domains/domain.com/logs/access.log; error_log /home/<user>/domains/domain.com/logs/error.log; #try_files $uri $uri; location / { # this sends all non-existing file or directory requests to index.php if (!-e $request_filename) { rewrite ^(.+)$ /index.php?q=$1 last; } } # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # location ~ \.php$ { include snippets/fastcgi-php.conf; # With php7.0-cgi alone: # fastcgi_pass 127.0.0.1:9000; # With php7.0-fpm: fastcgi_pass unix:/run/php/php7.0-fpm.sock; } # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # location ~ /\.ht { deny all; } } |
NOTE I made the primary website www. in this example and the top server block redirects all traffic to the regular domain to www. This is not required, you can remove this if you wish and change the server_name in the primary block to remove the www.
After you save this file you will need to link it to the sites-enabled directory and restart nginx
1 2 |
sudo ln -s /etc/nginx/sites-available/<domain> /etc/nginx/sites-enabled/<domain> sudo service nginx restart |
The website should now be live. If you want to test it to make sure php is working you can create a new file in the domains public folder and output the results of phpinfo().
1 |
nano ~/domains/<domain>/public/info.php |
Now paste in the following code and save the file:
1 |
<?php phpinfo() ?> |
Now if you visit your domain at http://<domain>/info.php you should see the phpinfo page. Thats it for now. have fun!
Leave a Reply