Howto set up WordPress on Ubuntu LAMP-server [Ubuntu Server 14.04 LTS]


In this blog-post we will look at setting up WordPress on our LAMP-server running on Ubuntu Server 14.04 LTS.


After installing LAMP in Ubuntu Server 14.04 LTS we start by configuring MySQL. We want to make sure to use UTF-8 character-set everywhere possible. Let’s start to find out what we got by default when it comes to collation and character sets in MySQL. Login as root to MySQL from a terminal or ssh-shell…


Now type in the following SQL and hit return:

mysql> show variables where variable_name like 'ch%' or variable_name like 'col%';

this will give you a response similar to the table below:

| Variable_name            | Value                      |
| character_set_client     | utf8                       |
| character_set_connection | utf8                       |
| character_set_database   | latin1                     |
| character_set_filesystem | binary                     |
| character_set_results    | utf8                       |
| character_set_server     | latin1                     |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
| collation_connection     | utf8_general_ci            |
| collation_database       | latin1_swedish_ci          |
| collation_server         | latin1_swedish_ci          |
11 rows in set (0.00 sec)

We see here that we have some records with latin1 and also collation_connection is utf8_general_ci rather than utf8_unicode_ci. The argument for using utf8_unicode_ci instead of utf8_general_ci can be read here. Maybe you even want to go with utf8mb4  as default…

init_connect='SET collation_connection = utf8mb4_unicode_ci; SET NAMES utf8mb4;'

…However, I’m perfectly fine with using only the original utf8 (utf8mb3) as default, since I do not need more than the first 65,536 codepoints (which uses 1 to 3 bytes per character). I’m not planning to blog in full Cantonese any time soon 🙂  If you are in need for full coverage of CJVK (Chinese, Japanese, Vietnam, Korean) you should go with utf8mb4 that uses 1 to 4 bytes per character. You can read more about MySQL and uft8mb4 here.

Let’s do the configuration:

In /etc/mysql we have the config-file my.cnf

root@ubuntu01:~# cd /etc/mysql/
root@ubuntu01:/etc/mysql# ls -al
total 24
drwxr-xr-x  3 root root 4096 Jun 22 19:42 .
drwxr-xr-x 94 root root 4096 Jun 23 16:14 ..
drwxr-xr-x  2 root root 4096 Jun 22 19:42 conf.d
-rw-------  1 root root  333 Jun 18 15:04 debian.cnf
-rwxr-xr-x  1 root root 1220 Jan 21 22:31 debian-start
-rw-r--r--  1 root root 3704 Jun 20 15:19 my.cnf

Put the following entries in the config-file my.cnf under the [mysqld] section:

init_connect='SET collation_connection = utf8_unicode_ci; SET NAMES utf8;'

Restart MySQL-daemon:

 root@ubuntu01:/etc/mysql# service mysql restart

Now, running the show variables statement again shows this:

| Variable_name            | Value                      |
| character_set_client     | utf8                       |
| character_set_connection | utf8                       |
| character_set_database   | utf8                       |
| character_set_filesystem | binary                     |
| character_set_results    | utf8                       |
| character_set_server     | utf8                       |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
| collation_connection     | utf8_unicode_ci            |
| collation_database       | utf8_unicode_ci            |
| collation_server         | utf8_unicode_ci            |
11 rows in set (0.00 sec)

Looks okay now. We can test that the default values works as expected by creating a dummy database…

mysql> create database junk_db;
Query OK, 1 row affected (0.00 sec)

…and look with what parameters it was created:

mysql> show create database junk_db;

| junk_db  | CREATE DATABASE `junk_db` /*!40100 DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci */ |

1 row in set (0.00 sec)

Nice! Both character set and collation were the expected ones. Now create a dummy table in this db…

mysql> use junk_db;
Database changed

mysql> create table junk_table (name varchar(255));
Query OK, 0 rows affected (0.00 sec)

…and look how it was created:

mysql> show create table junk_table;

| junk_table | CREATE TABLE `junk_table` ( `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci    |

1 row in set (0.00 sec)

Same thing here. Looks just as it should. Please note that the default engine is InnoDB which is the one I want by default. Now drop this database…

mysql> drop database junk_db;
Query OK, 0 rows affected (0.00 sec)

…and create the WordPress database. I prefix it with wp_ and then using the domain name just for clarity:

mysql> create database wp_creang;
Query OK, 1 row affected (0.00 sec)

Show all databases in MySQL:

mysql> show databases;
| Database           |
| information_schema |
| mysql              |
| performance_schema |
| wp_creang          |
4 rows in set (0.01 sec)

Verify that the database was created with utf8 and utf8_unicode_ci…

mysql> show create database wp_creang;

| wp_creang | CREATE DATABASE `wp_creang` /*!40100 DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci */ |

1 row in set (0.00 sec)

Create a user that we later will use with wordpress to connect to this database, again I’m giving it a suitable name for clarity. Password_here should of course be substituted with your chosen password.

mysql> create user wpuser_creang@localhost identified by 'password_here';
Query OK, 0 rows affected (0.00 sec)

Give the user full permission to access and manipulate this database:

mysql> grant all privileges on wp_creang.* to wpuser_creang@localhost;
Query OK, 0 rows affected (0.00 sec)

Now, just to verify everything looks okay, switch to the internal MySQL database:

mysql> use mysql
Database changed
 mysql> select host,user,password from user;
| localhost | wpuser_creang    | *E6B......... |
6 rows in set (0.00 sec)
 mysql> select host,db,user from db;
| host      | db        | user          |
| localhost | wp_creang | wpuser_creang |
1 row in set (0.00 sec)

Everything looks okay here, so let’s make the change take effect:

mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)

Now having the db prepared let’s download and install WordPress:

WordPress installation

Make a download folder under /usr/src or in any other preferred location

root@ubuntu01:/etc/mysql# cd /usr/src/
root@ubuntu01:/usr/src# mkdir download
root@ubuntu01:/usr/src# cd download/

Download the latest WordPress tarball:

root@ubuntu01:/usr/src/download# wget

Unpack, This will create a directory called wordpress in your download directory.

root@ubuntu01:/usr/src/download# tar xvzf latest.tar.gz

Install php5-gd and libssh2-php

root@ubuntu01:/usr/src/download# apt-get update
root@ubuntu01:/usr/src/download# apt-get install php5-gd libssh2-php

Copy the sample config

root@ubuntu01:/usr/src/download# cd wordpress/
root@ubuntu01:/usr/src/download/wordpress# cp wp-config-sample.php wp-config.php

And put this in your newly created wp-config.php, I use VIM to edit the file…

root@ubuntu01:/usr/src/download/wordpress# vi wp-config.php
/** The name of the database for WordPress */
define('DB_NAME', 'wp_creang');

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

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

/** MySQL hostname */
define('DB_HOST', 'localhost');

/** Database Charset to use in creating database tables. */
define('DB_CHARSET', 'utf8');

/** The Database Collate type. Don't change this if in doubt. */
define('DB_COLLATE', 'utf8_unicode_ci');

define( 'WP_POST_REVISIONS', 5 );

The last entry makes sure only five revisions of posts are kept in the database. The default is 30 but I do not want that many in order to keep the db small and tidy. Put in whatever value you want here.

Now when the configuration is done create a folder that will work as a home directory for this site under Apache webserver. I chose the domain name as folder name for clarity. Under Ubuntu Server the apache directory is located at /var/www

root@ubuntu01:/usr/src/download/wordpress# cd /var/www/
root@ubuntu01:/var/www# mkdir creang

Listing the www-folder now shows this:

root@ubuntu01:/var/www# ls -l
drwxr-xr-x 2 root root 4096 Jun 20 16:11 creang
drwxr-xr-x 2 root root 4096 Jun 18 15:04 html

Now, copy or move the wordpress folder content into this folder. I’m using rsync to copy it over:

root@ubuntu01:/var/www/creang# cd /usr/src/download/
root@ubuntu01:/usr/src/download# rsync -avP wordpress/ /var/www/creang/

Create an uploads-folder under the wp-content folder.

root@ubuntu01:/usr/src/download# cd /var/www/creang/wp-content/
root@ubuntu01:/var/www/creang/wp-content# mkdir uploads

Change the owner of the files recursively to your user and group. I’m using nobody and www-data here.

root@ubuntu01:/var/www/creang/wp-content# cd ..
root@ubuntu01:/var/www/creang# chown -R nobody:www-data *

Apache config:

Let’s start by checking what version of apache we have:

root@ubuntu01:/var/www/creang# apache2 -v
Server version: Apache/2.4.7 (Ubuntu)
Server built:   Mar 10 2015 13:05:59

Ok, so we have apache 2.4. We will set this site up as one of several name-based web sites on a single IP-address. For the 2.4-version of Apache the examples for this can be found here.

Now, just making sure apache listens to port 80, check the contents of ports.conf:

root@ubuntu01:/var/www/creang/wp-content# cd /etc/apache2/
root@ubuntu01:/etc/apache2# more ports.conf
# If you just change the port or add more ports here, you will likely also
# have to change the VirtualHost statement in
# /etc/apache2/sites-enabled/000-default.conf

Listen 80

<IfModule ssl_module>
        Listen 443

<IfModule mod_gnutls.c>
        Listen 443

We can also see if apache is running and what address it is bound to by this command:

root@ubuntu01:/etc/apache2# netstat -nap
Active Internet connections (servers and established)
Proto Local Address Foreign Address State   PID/Program name
tcp*       LISTEN  8270/apache2

Okay, so everything looks as it should. Apache listens on port 80 already. Now, start by editing the apache2.conf

root@ubuntu01:/etc/apache2# vi apache2.conf

Set servername to something suitable, like localhost:

# Global configuration
ServerName localhost

Now, comment out the Directory /var/www, we don’t need that directory enabled any longer.

#<Directory /var/www>
#       Options Indexes FollowSymLinks
#       AllowOverride None
#       Require all granted

Disable the default configuration file /etc/apache2/sites-enabled/000-default.conf

root@ubuntu01:/etc/apache2/sites-enabled# a2dissite 000-default.conf
Site 000-default disabled.

The a2dissite-command does the removal of the symlink in folder sites-enabled. We double check that the job is done.

root@ubuntu01:/etc/apache2/sites-enabled# ls -al
drwxr-xr-x 2 root root 4096 Jun 20 17:11 .
drwxr-xr-x 8 root root 4096 Jun 20 16:51 ..

Indeed empty. Now copy the default config file as a skel for our new config file. I use the name vhosts-default.conf

root@ubuntu01:/etc/apache2/sites-available# cp 000-default.conf vhosts-default.conf

Now, having both the .com and the .se domain with the same name I want them all to point to the same site redirecting to regardless if you type, or in your browser they will all redirect to This will require a little DNS-configuration and some Apache-config as well. In DNS, I set up both www-subdomains as CNAME-records pointing to the naked domain name. Like this.
@    A
www  CNAME
@    A
www  CNAME

Two A-records are set up to point to the public IP-address of the server. They will be updated automatically via ddclient should my ip-address change, read more about how to configure that here.

Now, with these records in place we now configure our vhosts-default.conf like this:

<VirtualHost *:80>
        RedirectMatch 301 (.*)$1
<VirtualHost *:80>
        DocumentRoot /var/www/creang
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
        <Directory /var/www/creang>
                Options Indexes FollowSymLinks
                AllowOverride All
                Require all granted

The first VirtualHost section takes care of the redirect. Since this is a permanent redirect I went with 301, good for SEO-purposes. The second section points out the document root and sets some permissions where AllowOverride All is important for enabling the use of .htaccess-files, which we will use from WordPress in order to enable mod_rewrite and the use of clean urls.

Create a .htaccess file under /var/www/creang

root@ubuntu01:/etc/apache2# cd /var/www/creang/
root@ubuntu01:/var/www/creang# touch .htaccess
root@ubuntu01:/var/www/creang# chown nobody:www-data .htaccess
root@ubuntu01:/var/www/creang# chmod 664 .htaccess

Enable the rewrite module:

root@ubuntu01:/var/www/creang# a2enmod rewrite

Enable our site (adding the symlink to sites-enabled)

root@ubuntu01:/etc/apache2/sites-available# a2ensite vhosts-default.conf

A quick check shows the symlink was added:

root@ubuntu01:/etc/apache2/sites-enabled# ls -al
drwxr-xr-x 2 root root 4096 Jun 20 20:11 .
drwxr-xr-x 8 root root 4096 Jun 29 16:29 ..
lrwxrwxrwx 1 root root   38 Jun 20 18:07 vhosts-default.conf -> ../sites-available/vhosts-default.conf

Now we need to restart our apache server in order for the new wordpress site to come alive. Be aware that if you have configured your machine with a public IP-address then maybe you should set a temporary firewall rule in place first, so that only you (your ip-address) are allowed to access the server during setup of wordpress. Not so fun if anyone hijacks your wordpress installation.

Maybe something similar to below in order to allow only clients from a specific IP-range…and don’t forget to temporary remove the any-client-accept-rule and verify that the new rule takes full effect before continuing. Delete this rule later when you go live.

iptables -I INPUT 4 -p tcp --dport 80 -m iprange --src-range -j ACCEPT

Restart or reload Apache service

 root@ubuntu01:/etc/apache2# service apache2 restart

Point your browser to your url. You should now see the welcome screen.


Type in your info and just click Install WordPress and you are done! 🙂


Well…Almost done!, we want to use clean urls, login to you site with your newly created user…


After login, go to Settings->Permalinks


And choose your preferred way of displaying urls. Mine is using custom structure with both category and postname giving me urls such as this one:


After “Save Changes” you will now have something similar to this in your .htaccess file:

root@ubuntu01:/var/www/creang# more .htaccess

# BEGIN WordPress
 <IfModule mod_rewrite.c>
 RewriteEngine On
 RewriteBase /
 RewriteRule ^index\.php$ - [L]
 RewriteCond %{REQUEST_FILENAME} !-f
 RewriteCond %{REQUEST_FILENAME} !-d
 RewriteRule . /index.php [L]
# END WordPress

Out of curiosity we can take a peek at the wordpress db and now see how the tables in the wp database were created. Since WordPress 4.2 utf8mb4 is used whenever possible, read more about it here

mysql> use wp_creang;

Database changed
mysql> show tables;
| Tables_in_wp_creang   |
| wp_commentmeta        |
| wp_comments           |
| wp_links              |
| wp_options            |
| wp_postmeta           |
| wp_posts              |
| wp_term_relationships |
| wp_term_taxonomy      |
| wp_terms              |
| wp_usermeta           |
| wp_users              |
11 rows in set (0.00 sec)
mysql> show create table wp_comments;


Indeed utf8mb4 is used here.

That’s all for now. In the next blog-post we will look at setting up the WordPress-plugin Akismet to protect comments and contact forms from spam. Edit! Now available here.


Leave a Reply

Your email address will not be published. Required fields are marked *