From Organic Design

Learning the LAMP stack

My first project is to learn the LAMP stack, I'm writing the LAMP article as I learn more about it.

Local machine

First I downloaded Linux Mint 18 (Cinnamon) from their download page and installed Rufus on my local Windows machine (an application for Windows to format and create a bootable USB drive) to put the Mint 18 ISO on my 8GB USB. Goodbye Windows it was!

Once Mint was installed I clicked on Menu at the bottom and opened software manager then searched for and installed Nginx (which I'll be learning to use first before Apache). I also searched for MariaDB in the software manager and installed this on Mint.


Nad has set up a domain and small server for me at The first thing will be to install a LAMP stack on this server by learning the basic aspects of the Install a new server procedure.

Lesson #1: Setting up the essentials for my server

On Nov 24th 2016, Nad guided me through a lesson over skype which started by creating an RSA key to get into my server with. This creates two files, and id_rsa, the first one is like a padlock and I can give that out to anyone, the second file is the key that unlocks the padlock and that's never shown to anyone. Nad installed my public key (padlock) on my server, so now I can login to it since I'm the only one who has the private key to match that public key.

I need to be in root when making edits to my server I can do this by typing

sudo bash

I can now access my server using the following command from the shell on my computer that has the private key on it:


I then used apt-get (which is program behind the desktop's Software Manager application) and installed the necessary packages for the LAMP stack on my server (this is a very simplified version of the Install a new server procedure):

apt-get update
apt-get upgrade
apt-get install sudo host screen ntp p7zip-full bzip2 unzip git curl librsvg2-bin imagemagick
apt-get install mariadb-server nginx php5-fpm
apt-get install php5-cli php5-mysqlnd php5-gd php5-mcrypt php5-xsl php5-intl php5-xmlrpc php5-curl php-apc

I was then able to see that Nginx (the web-server) was running and successfully installed by going to in a browser which looked like this:


The PURPOSE of Nginx is to map requested domains to directories.

I could make edits to this test page by looking in the /var/www directory which is where Nginx puts all the web pages, and finding that the test page was in the file /var/www/html/index.nginx-debian.html. So I used the nano shell text editor to edit the file and then refreshed the browser page to see that my edits had changed the message.

nano /var/www/html/index.nginx-debian.html

Note: Use CTRL+X and then type Y, Enter to save changes and exit (here's a quick reference for the keys).

I also needed to remove some packages that I accidentally added from the big Install a new server procedure that weren't needed on my server and would slow it down too much. I didn't realise my server couldn't handle email so I removed these packages with the apt-get remove command.

apt-get remove exim4-daemon-heavy dovecot-common dovecot-imapd spamassassin spamc maildirsync

Quick note is if im making edits to the Nginx config file I need to restart Nginx by typing in the following command in shell

service nginx restart

Linode Server set up

On Dec 22nd 2016 I came back to setting up a new server by myself. I first started by going to (They specialize in Linux only) they call their servers "Linodes". I logged in using Arans account (username: aranad) and selected the cheapest linode which is $.015 / hr ($10 / mo) and chose a location, mine was based in Frankfurt in ze Fatherland. I then clicked add a linode then the next step was running an OS on the server so I clicked on Dashboard and selected Deploy an Image, I just used default on all the settings, Which the OS it would use was Debian 8 then set a password and clicked Deploy then clicked Boot on the next screen.

I could then see my Server status as running and could ssh into the Server by going sshroot@(server IP goes here) I installed the necessary packages for the LAMP Stack on my server(above) and cloned my git repo into root@debian:/var/www by doing git clone

Lesson #5: Servers, services, IP addresses and ports

After another skype with Aran he gave me a recap on what Nginx is and what its used for, see the LAMP#Apache.

You can think of an IP address as a street address, it points to a specific computer on the Internet the same way as a street address points to a specific house. There are two versions of IP address these days IPv4 and IPv6. We'll only worry about IPv4 for now which are always in the format of four numbers (each from 0 to 254) separated by dots. The special address of always refers to the computer you're using and is called the localhost.

A server is just a computer, but it gets known as a "server" when it has two important properties:

  1. it's always connected to the net at the same IP address, and
  2. it's running a type of programs called services. (e.g. nginx is a service)

A service is a normal program but with two important properties:

  1. it always runs as long as the computer is running,
  2. it "listens" to requests on a specific port

Port: But what happens if a house is divided into many different apartments? then you would append a letter onto the house number of the street address, e.g. 123E means apartment E at street number 123. In the same way port number specifies what service number you want to connect to in the computer at that IP address, e.g. means you want to speak to the service listening on port 500 on the computer at IP address Ports are needed because a server with only one IP address may have many services running on it and you need to be able to specify which one you want to talk to. A browser expects someone to be listening on port 80 when it connects to a server at an IP address.

If I type in the shell command

netstat -4tnlp

It will show me a list of all the services that are listening for requests. E.g. Skype was Listening to requests on port number 60287 on my computer today. The 4t filters the list to only IPv4 TCP connections, the n means show the number of the port instead of the name, the l means only show things that are listening and the p means to show what the program is that's listening.

Lesson #6: PHP/HTML Recap on things

1st Rule of tonight's lesson/recap - No flogging dead horses

2nd Rule of tonight's lesson/recap - NO FLOGGING DEAD HORSES!!

3rd Rule - Work local only and commit and push only when things are successful

4th Rule - I need to see the information about what's wrong. I had to enable this by going (cd /etc/php/7.0/fpm) inside this directory was php.ini, so I nano php.ini and changed the display_errors = Off to display_errors = On so I could see the information given to me with the error to fix. When making changes in this I need to enter the following command:

service php7.0-fpm restart

A program is made of two things, data and code, the code is directions about what to do with the data, the data is generally 3 types of things, numbers, strings and objects.

PHP like all programming languages is in the form of a list of commands. I can only put one command per line, every command has a ; at the end of it.

Strings is the writing inside the quotes, e.g. "organic design", so quotes always have to be in pairs.

If you want to make a string that has quotes as part of it, you can use a single quote instead, or put a backslash before each quote, e.g:

"I can make a quote like this: \" or just use ' instead"

All HTML must be inside quotes when doing PHP! PHP doesn't understand HTML, it may as well be Chinese or Greek, so it has to be inside quotes so PHP knows it's just general stuff that it's not supposed to understand.

A dot is used to add strings together example

"<p>Your IP address is <strong>" . $_SERVER['REMOTE_ADDR'] . "</strong></p>"

Two quoted strings next to each other doesn't really make sense because it's the same as one string, e.g.

"Foo" . "Bar" . "Baz"

is just the same as this:


Only the opening tag-name can have attributes, and the closing tag never has anything in it! The opening tag-name must match the closing tag-name eg. in PHP

"<span style='color:red;'>" . date("h:i:sa") . "</span>"

A good way to approach this kind of task is to figure out the HTML first and put it in a PHP comment, then while working on figuring out the PHP you can always be reminded of the final result you're trying to achieve. E.g. for the IP address task above it might be like this:

// The following PHP outputs html of the following format
// <p>Your IP address is <strong></strong></p>
echo "<p>Your IP address is <strong>" . $_SERVER['REMOTE_ADDR'] . "</strong></p>";

Lesson #7 Setting up my first LAMP Application

Today I learned to install my first LAMP app which was a wordpress site at

First I needed to edit my Nginx config file and add a new server block which in this case was:

# Blog
server {
	listen 80;
	root /var/www/;
	include /var/www/;

A server block defines one single site and needs to have 3 commands; listen, server_name, and a root. The listen is the port that the site will be on (usually 80). server_name is the domain of the new site and root is where the files for the new site are on the server. The highlighted parts above show the key information used to configure my new wordpress.

My config file is in my git repo, so I edited it on my local machine and then committed and pushed it which automatically updates the repo on my server.

I then went into the server, changed to root and restarted nginx.

sudo bash
service nginx restart

To download something from a web address to the server from the shell we first make sure we're in the location we want it to be and then use the wget command. In my case I went to and copied the download link of the latest version of wordpress from their download page, then used this URL in the wget command.

cd /var/www

Usually installations for Linux are .tar.gz files (sometimes called .tgz instead), they're just like a zip and the following command is used to unzip them in the shell.

tar -zxf <FILE.TAR.GZ>

This command will unpack the tgz to a folder in the current directory. In this case the folder it was all zipped up into was called "wordpress" which just happens to be where I wanted my new site, but if it was something different I would have had to change the name with the mv command.

Now that all the wordpress PHP files were in the right place, I refreshed and the wordpress installation page came up. I followed the instructions to set up the database and install it.

The database was of type MySQL and the server is at localhost which means the database is running on the same server as the web-server. I had to create the database manually because the installation wasn't able to do it itself, I had to log in to MySQL from the shell and then create a new empty database of the same name I specified in the installation which was "wordpress".

mysql -u root -p
create database wordpress;

At the end of the installation after I had answered all its questions, it said that it needed me to create its configuration file because it didn't have permission to create the file itself. It told me what file I should create and gave me the stuff I should paste into it. The file name was config.php and I used nano to create it and paste the stuff in.

nano /var/www/

I then went back to again, and it was all running :-)

Keeping LAMP app configuration settings organised

Every LAMP app has one file that's used to keep the various configuration settings in. After you install the app and answer all its installation questions it gives you the initial content for this file which includes the database name and password etc. It asks you to create this file and paste the initial content into it since it doesn't have permission to do it itself. MediaWiki's file is called LocalSettings.php, Wordpress's is called config.php.

You may later want to add more different settings for example for adding extensions or other configuration such as setting the site logo. These settings should all be at the bottom of the file so its very easy to see what you've added.

An even better solution to organising these settings is to keep them in a separate file in your repo so you know its safe and backed up and you can always go back to previous versions if you mess something up.

To do this for my MediaWiki I first created a file called CyWikiSettings.php and added it to the repo, and then put my settings that I had added to the end of LocalSettings.php into the instead. Note that because the settings are all PHP code we must put the PHP delimiter at the start of the file. Since this file is in the repo, I added it on my local machine and then committed and pushed it as usual.

$wgLogo = "";
$wgDefaultSkin = "MonoBook";

Then I shelled into the server and removed the settings I had added from LocalSettings.php using nano and added the following line at the end instead which tells PHP to insert everything from my new repo settings file there.

include( "/var/www/" );

Now I can just add new configuration to the CyWikiSettings.php locally in my repo to change the wiki settings. I can keep easier track of the changes I make and not wreck any of the default settings already in LocalSettings.php.

It doesn't matter if there's already a setting of the same thing (e.g. $wgLogo) in LocalSettings.php because it's whatever it was set to last that counts. For example LocalSettings.php sets the skin ($wgDefaultSkin) to "vector", CyWikiSettings.php sets it to "MonoBook". Since the include of CyWikiSettings.php is at the bottom of the file my settings will be the final ones that are set. For example I could do this instead and it would make no difference, because "MonoBook" is the final thing it's set to.

$wgDefaultSkin = "Foo";
$wgDefaultSkin = "Bar";
$wgDefaultSkin = "Baz";
$wgDefaultSkin = "MonoBook";

Lesson #8 Learning about the database server

After this mornings skype lesson I learned about the database server after nad explaining to me.

Instead of using MySQL I'll be using MariaDB as the database server which is OD's preferred one, but it's identical to MySQL to use and even uses the "mysql" command so you can use either without having to change any settings in apps that use the database.

To log into MariaDB from the shell use the command below.

mysql -u root -p

I can see my databases and tables by entering the following commands below. I must use a ; at the end of each command in MariaDB just like with PHP commands.

show databases;      /* shows all the databases */
use DATABASE_NAME;   /* to access into a database */
show tables;         /* shows the list of tables once accessed into a database */

Each table in a database is like a bank statement where there are certain columns across the top like date, description, debit, credit, balance. Different tables have different columns depending on what they're for. Every row in the table has specific information but has all the same columns, e.g. you can't have one row in your bank statement that has different columns than other rows otherwise it wouldn't be a proper table and wouldn't make any sense.

All a database really is, is just a kind of storage that's specifically designed for dealing with tables.

To see the details of a table's structure - i.e. what columns it has and what type of data can go in each one etc, you use the following:

describe TABLE_NAME

For example, describing the "user" table in my MediaWiki gave the following result:

| Field                    | Type             | Null | Key | Default  | Extra          |
| user_id                  | int(10) unsigned | NO   | PRI | NULL     | auto_increment |
| user_name                | varbinary(255)   | NO   | UNI |          |                |
| user_real_name           | varbinary(255)   | NO   |     |          |                |
| user_password            | tinyblob         | NO   |     | NULL     |                |
| user_newpassword         | tinyblob         | NO   |     | NULL     |                |
| user_newpass_time        | binary(14)       | YES  |     | NULL     |                |
| user_email               | tinyblob         | NO   | MUL | NULL     |                |
| user_touched             | binary(14)       | NO   |     |          |                |
| user_token               | binary(32)       | NO   |     |          |                |
| user_email_authenticated | binary(14)       | YES  |     | NULL     |                |
| user_email_token         | binary(32)       | YES  | MUL | NULL     |                |
| user_email_token_expires | binary(14)       | YES  |     | NULL     |                |
| user_registration        | binary(14)       | YES  |     | NULL     |                |
| user_editcount           | int(11)          | YES  |     | NULL     |                |
| user_password_expires    | varbinary(14)    | YES  |     | NULL     |                |

This shows that the "user" table has 15 columns in it.

I can then select some of the information out of the table (i.e. see what some of the rows are) like this:

select user_id, user_name, user_email from user;

This will show me ALL of the rows in the table, but only showing the three columns that I had chosen to see - seeing all 15 columns would be too much information and be very cluttered, but you can do that by using * instead of a list of specific columns. The command I used shows me just the ID, name and email of all the users in the table, which resulted in the following:

| user_id | user_name | user_email               |
|       1 | Cyrusty   |            |
|       2 | Nad       | |
|       3 | Henry     |         |
|       4 | Mike      |        |

If there were thousands of users it would have been better to only select a specific row, for example if I wanted to see the information for user "Nad" I could add a where clause to the select:

select user_id, user_name, user_email from user where user_name="Nad";

Now the result only shows the one row that has that user_name:

| user_id | user_name | user_email               |
|       2 | Nad       | |

If I wanted to change the email address to "" I could do it like this:

update user set user_email="" where user_name="Nad";

Note that I've used the same where clause as I had in the select above so I know that it will apply to the correct row. There is no undo function so you must test your WHERE clause with a SELECT first!!! If it selects the correct row that you want to change and no other rows, then you know it's safe to use that same where part for an update to change things.

Some other things I learned about selecting are like, limit and order. Like is used instead of "=" to specify columns that are a close match rather than only ones that are an exact match. You use a "%" character to specify any group of zero or more characters. For example here we select all the users who registered in January of 2012 by stating that the user_registration column must start with "201201".

select user_id,user_name,user_registration from user where user_registration like "201201%";

Here we use limit and order by to find the first 10 users to register.

select user_name,user_registration from user order by user_registration limit 10;

Or the last 10 user to register:

select user_name,user_registration from user order by user_registration desc limit 10;

Or combining all together, the first 5 people to register in January 2012:

select user_name,user_registration from user where user_registration like "201201%" order by user_registration desc limit 10;

Here we sort the users by name (alphabetically) and show the first and then the last 20,

select user_name from user order by user_name limit 20;
select user_name from user order by user_name desc limit 20;