Skip to content

Instantly share code, notes, and snippets.

@99darwin
Last active April 19, 2019 15:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 99darwin/deaa3d2e83b0b5742734ca76e997c9d0 to your computer and use it in GitHub Desktop.
Save 99darwin/deaa3d2e83b0b5742734ca76e997c9d0 to your computer and use it in GitHub Desktop.
Setting up a WordPress site on Digital Ocean from A-Z

Prerequisites

This guide assumes that you already have some of the basics handled.

  1. A Digital Ocean account
  2. A Droplet running Ubuntu 18.04 deployed (but not set up)
  3. A domain (optional)
  4. Basic understanding of SSH
  5. Basic understanding of nano

NOTE: all items marked in bold are boilerplate and should be replaced with your own values.

1. Server setup

First we must set up and secure the droplet.

1a. Create a sudo user

SSH into your server

ssh root@your_server_ip_goes_here

Create a new user

adduser username

Enter a new password and fill in any additional info, or just hit ENTER.

Grant your new user admin privileges

usermod -aG sudo username

Allow password authentication access for the new sudo user

nano /etc/ssh/sshd_config

Find the line that says PasswordAuthentication no and change it to

PasswordAuthentication yes

Save and quit the text editor by typing ctrl + X followed by Y

Restart the ssh service

service ssh restart

1b. Set up firewall

Check which firewall profiles are available

ufw app list
# Output
Available applications:
  OpenSSH

Allow SSH connections

ufw allow OpenSSH
# Output
Rules updated
Rules updated (v6)

Enable the firewall

ufw enable

Type y and press ENTER

Now you can see that SSH connections are enabled

ufw status
# Output
Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)

1c. Log in with new sudo user

Open a new terminal window/tab and enter

ssh username@your_server_ip_goes_here

Enter the password and run a sudo command to ensure that the user has root privileges.

sudo ufw app list

You may be asked for your password. Use the one set up in previous steps

2. Install a LAMP stack

LAMP stands for Linux Apache MySQL PHP, which are the essential requirements for running a WordPress website.

2a. Install Apache

While still accessing your Droplet via the sudo user we set up earlier, update and install Apache.

sudo apt update
sudo apt install apache2

Press Y when asked if you're sure you'd like to install.

2b. Adjust the firewall

We need to add an application profile for Apache

sudo ufw app list

You'll notice 3 new profiles

# Output
Available applications:
  Apache
  Apache Full
  Apache Secure
  OpenSSH

We want to use Apache Full because it enables traffic to ports 80 (HTTP) and 443 (HTTPS)

sudo ufw allow in "Apache Full"

Now, when you access your server via a browser, you should see the Apache2 Default Page.

http://your_server_ip_goes_here

If you need to find the IP of your server at any time, simply call

curl -4 icanhazip.com

2c. Install MySQL

Now that the server is up, we can configure the database.

sudo apt install mysql-server

Remove defaults and lock down access to your database

sudo mysql_secure_installation

Press Y to install the VALIDATE PASSWORD PLUGIN

Securing the MySQL server deployment.

Connecting to MySQL using a blank password.

VALIDATE PASSWORD PLUGIN can be used to test passwords
and improve security. It checks the strength of password
and allows the users to set only those passwords which are
secure enough. Would you like to setup VALIDATE PASSWORD plugin?

Press y|Y for Yes, any other key for No: y

Press 1 and follow the MEDIUM password strength guidelines

There are three levels of password validation policy:

LOW    Length >= 8
MEDIUM Length >= 8, numeric, mixed case, and special characters
STRONG Length >= 8, numeric, mixed case, special characters and dictionary                  file

Please enter 0 = LOW, 1 = MEDIUM and 2 = STRONG: 1

Enter a new password that meets the criteria and press Y to accept it.

Estimated strength of the password: 100
Do you wish to continue with the password provided?(Press y|Y for Yes, any other key for No) : y

For the rest of the questions, press Y and ENTER at each prompt.

2d. Update authentication method for root user

sudo mysql

Check which authentication method each user uses

SELECT user,authentication_string,plugin,host FROM mysql.user;
# Output
+------------------+-------------------------------------------+-----------------------+-----------+
| user             | authentication_string                     | plugin                | host      |
+------------------+-------------------------------------------+-----------------------+-----------+
| root             |                                           | auth_socket           | localhost |
| mysql.session    | *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE | mysql_native_password | localhost |
| mysql.sys        | *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE | mysql_native_password | localhost |
| debian-sys-maint | *337CC3D55BCC1BA64047C3559593136117F669B4 | mysql_native_password | localhost |
+------------------+-------------------------------------------+-----------------------+-----------+
4 rows in set (0.01 sec)

Configure root to authenticate with a

password
. Be sure to select a strong password.

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '**password**';

Then reload the tables and invoke the changes

FLUSH PRIVILEGES;

Now, check that the authentication method has been updated.

SELECT user,authentication_string,plugin,host FROM mysql.user;
+------------------+-------------------------------------------+-----------------------+-----------+
| user             | authentication_string                     | plugin                | host      |
+------------------+-------------------------------------------+-----------------------+-----------+
| root             | *F60E736B9BAE549E45C246225D58E17D16BF1D22 | mysql_native_password | localhost |
| mysql.session    | *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE | mysql_native_password | localhost |
| mysql.sys        | *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE | mysql_native_password | localhost |
| debian-sys-maint | *337CC3D55BCC1BA64047C3559593136117F669B4 | mysql_native_password | localhost |
+------------------+-------------------------------------------+-----------------------+-----------+
4 rows in set (0.00 sec)

Voila! Everything looks correct, root now authenticates using a password. You can now exit the MySQL Shell.

exit

2e. Install PHP

Install PHP dependencies.

sudo apt install php libapache2-mod-php php-mysql

Next, we need to make sure that Apache prefers PHP files over HTML or others.

sudo nano /etc/apache2/mods-enabled/dir.conf

Once in the configuration file, move index.php from the index it is in by default, to the 0th index (first position). It should look like this when you're done.

<IfModule mod_dir.c>
        DirectoryIndex index.php index.html index.cgi index.pl index.xhtml inde$
</IfModule>

Save and quit the text editor with ctrl + X and Y then ENTER

Restart Apache

sudo systemctl restart apache2

Make sure Apache is running as intended

sudo systemctl status apache2

If all is well, it should look like this

● apache2.service - The Apache HTTP Server
   Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset:
  Drop-In: /lib/systemd/system/apache2.service.d
           └─apache2-systemd.conf
   Active: active (running) since Fri 2019-04-19 01:59:12 UTC; 26s ago
  Process: 15126 ExecStop=/usr/sbin/apachectl stop (code=exited, status=0/SUCCES
  Process: 15133 ExecStart=/usr/sbin/apachectl start (code=exited, status=0/SUCC
 Main PID: 15149 (apache2)
    Tasks: 6 (limit: 1152)
   CGroup: /system.slice/apache2.service
           ├─15149 /usr/sbin/apache2 -k start
           ├─15154 /usr/sbin/apache2 -k start
           ├─15155 /usr/sbin/apache2 -k start
           ├─15156 /usr/sbin/apache2 -k start
           ├─15157 /usr/sbin/apache2 -k start
           └─15158 /usr/sbin/apache2 -k start

Exit this output by typing Q

3. Set up virtual hosts

The virtual host will be used to configure the domain. Remember to replace example.com with your own domain name.

Create a directory for the website

sudo mkdir -p /var/www/example.com

Assign ownership of the directory

sudo chown -R $USER:$USER /var/www/example.com

Ensure that permissions of your web roots are correct

sudo chmod -R 755 /var/www/example.com

Create a new configuration file

sudo nano /etc/apache2/sites-available/example.com.conf

Paste in the following, substituting any example values for your own.

    ServerAdmin admin@example.com
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example.com/html
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

Save and close the file when you're done.

Enable the file

sudo a2ensite <b>example.com</b>.conf

Disable the default file

sudo a2dissite 000-default.conf

Test config for errors

sudo apahce2ctl configtest

You may see a warning in the output, you can ignore that for now as long as you see Syntax OK

# Output
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1. Set the 'ServerName' directive globally to suppress this message
Syntax OK

Restart apache to apply the changes

sudo systemctl restart apache2

4. Secure the website with SSL

For the purposes of this tutorial we will be using certbot

4a. Install Certbot

First add certbot

sudo add-apt-repository ppa:certbot/certbot

Press ENTER to accept

Install certbot's Apache package

sudo apt install python-certbot-apache

4b. Obtain the SSL certificate

Run certbot

sudo certbot --apache -d example.com -d www.example.com

Enter your email address if/when prompted.

Agree to the ToS by pressing A.

Answer Y or N depending on whether or not you ='d like your email shared with the EFF.

You will then be prompted with the following

# Output

Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2

Select option 2.

If successful, you will receive the following output

# Output

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/example.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/example.com/privkey.pem
   Your cert will expire on 2019-07-18. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot again
   with the "certonly" option. To non-interactively renew *all* of
   your certificates, run "certbot renew"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

4c. Verify Certbot auto-renewal

Ensure that your SSL certificate will auto-renew to avoid any future technical debt.

sudo certbot renew --dry-run

5. Configure and install WordPress

Finally, the moment you've been waiting for. We're just a few moments away from actually setting your WordPress website loose on the world. But first, a few preparations must be made.

5a. Create MySQL database & user for WordPress

Log into MySQL as root

mysql -u root -p

Enter the password you created for this user in the previous steps.

Create a separate database for WordPress.

CREATE DATABASE wordpress DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;

Create a new user for the database. Make sure to set

password
to a secure password.

GRANT ALL ON wordpress.* TO 'wordpressuser'@'localhost' IDENTIFIED BY 'password';

Flush the privileges

FLUSH PRIVILEGES;

Exit MySQL EXIT;

5b. Add PHP extensions

We will install the most popular PHP extensions used with WordPress

sudo apt update
sudo apt install php-curl php-gd php-mbstring php-xml php-xmlrpc php-soap php-intl php-zip

Restart Apache

sudo systemctl restart apache2

5c. Allow .htaccess overrides and rewrites

Open the Apache config file for your site

sudo nano /etc/apache2/sites-available/example.com.conf

Add the following within the VirtualHost block.

example.com/>
    AllowOverride All

Enable the rewrite module

sudo a2enmod rewrite

Test the configuration

sudo apache2ctl configtest

Restart apache to apply changes

sudo systemctl restart apache2

5d. Download WordPress

Enter the tmp directory and download the latest version of WordPress

cd /tmp
curl -O https://wordpress.org/latest.tar.gz

Extract the file

tar xzvf latest.tar.gz

Create an .htaccess file

touch /tmp/wordpress/.htacess

Copy the sample config file to the filename that WordPress reads

cp /tmp/wordpress/wp-config-sample.php /tmp/wordpress/wp-config.php

Create an upgrade directory to avoid permissions issues

mkdir /tmp/wordpress/wp-content/upgrade

Copy the contents of the directory into the document root

sudo cp -a /tmp/wordpress/. /var/www/example.com

5e. Configure the WordPress directory

Update ownership of the directory

sudo chown -R www-data:www-data /var/www/example.com

Set correct permissions on the WordPress directories and files

sudo find /var/www/example.com/ -type d -exec chmod 750 {} \;
sudo find /var/www/example.com/ -type f -exec chmod 640 {} \;

5e. Set up WordPress configuration file

Grab secure values from the WordPress secret keygen

curl -s https://api.wordpress.org/secret-key/1.1/salt/

The output will look similar to this. Use your own values in the coming steps and DO NOT COPY THESE.

# Output
define('AUTH_KEY',         '1jl/vqfs<XhdXoAPz9 DO NOT COPY THESE VALUES c_j{iwqD^<+c9.k<J@4H');
define('SECURE_AUTH_KEY',  'E2N-h2]Dcvp+aS/p7X DO NOT COPY THESE VALUES {Ka(f;rv?Pxf})CgLi-3');
define('LOGGED_IN_KEY',    'W(50,{W^,OPB%PB<JF DO NOT COPY THESE VALUES 2;y&,2m%3]R6DUth[;88');
define('NONCE_KEY',        'll,4UC)7ua+8<!4VM+ DO NOT COPY THESE VALUES #`DXF+[$atzM7 o^-C7g');
define('AUTH_SALT',        'koMrurzOA+|L_lG}kf DO NOT COPY THESE VALUES  07VC*Lj*lD&?3w!BT#-');
define('SECURE_AUTH_SALT', 'p32*p,]z%LZ+pAu:VY DO NOT COPY THESE VALUES C-?y+K0DK_+F|0h{!_xY');
define('LOGGED_IN_SALT',   'i^/G2W7!-1H2OQ+t$3 DO NOT COPY THESE VALUES t6**bRVFSD[Hi])-qS`|');
define('NONCE_SALT',       'Q6]U:K?j4L%Z]}h^q7 DO NOT COPY THESE VALUES 1% ^qUswWgn+6&xqHN&%');

Open the WordPress config file

sudo nano /var/www/example.com/wp-config.php

Find the section that looks like this under Authentication Unique Keys and Salts

. . .

define('AUTH_KEY',         'put your unique phrase here');
define('SECURE_AUTH_KEY',  'put your unique phrase here');
define('LOGGED_IN_KEY',    'put your unique phrase here');
define('NONCE_KEY',        'put your unique phrase here');
define('AUTH_SALT',        'put your unique phrase here');
define('SECURE_AUTH_SALT', 'put your unique phrase here');
define('LOGGED_IN_SALT',   'put your unique phrase here');
define('NONCE_SALT',       'put your unique phrase here');

. . .

Delete and paste in the values you copied from the step above.

Next, find the section towards the top that looks like this and replace with your own values. If you've followed the tutorial up to this point, you can copy the code below. The only change you'll need to make is to the DB_PASSWORD value.

. . .

define('DB_NAME', 'wordpress');

/** MySQL database username */
define('DB_USER', 'wordpressuser');

/** MySQL database password */
define('DB_PASSWORD', 'password');

. . .

define('FS_METHOD', 'direct');

Save and close the file when you're done.

5f. Complete the installation through the web interface

From your browser, navigate to your website

https://your_website

Follow the WordPress onboarding flow, and you're all set!`

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