So I'm teaching a friend how to set up Linux, Apache, MySQL, and PHP. Here's my version of teaching you how to do it.


• Download and install Ubuntu (Server or Desktop.) See instructions here. Install with the default options, or just run the Desktop version from USB.


• Remember that if you have trouble running a command, you might need to type sudo before it to run it as root (the administrator account on Linux machines. Most installing/repairing/uninstalling/configuring work requires root privileges.)

• To edit files, use nano (easiest), vim (hardcore), or gedit (if you're running desktop not server). In Ubuntu Desktop, you access programs using the "Dash Home" (aka Ubuntu Menu at the top left) and typing a command or search keyword. For this guide we'll be working out of the terminal, so type "terminal" and open the terminal program.

• From terminal, to edit a file like /etc/apache2/httpd.conf you'll type vim /etc/apache2/httpd.conf. Other useful commands include cd for changing directories, ls for listing files in a directory, and find * -name "*a*" for finding everything under the current directory with "a" in the name.

• Also remember that the text to the left of your cursor (user@computer:folder$) shows you the current folder you're in (such as ~ which is a shortcut for your home folder, /home/yourusername). Everything under Linux is stored as a file under the root directory (/) and so there are no C: or D: prefixes like in Windows. If you see a preceding slash (/home/user) that means "the user folder, under the home folder, under the root folder." This is an important distinction because "home/user" could mean any home folder anywhere, depending on context. If your current folder is /var/www then you'd be talking about /var/www/home/user which might not even exist. So pay attention to slashes and context.

• Use of the arrow keys, and home/end/pgup/pgdn, will help a lot. You can press the up arrow to re-type previously entered commands, press Ctrl+R to do a reverse-search of previous commands, and open the ~/.bash_history file to see logs of your previous sessions.

• Use the Tab key to auto-complete things on the command line. To type "vim /etc/apache2/httpd.conf you can actually type "vim /etc/apahtt" (if there is no auto-completion, then you need to type more to be more specific.)

• Rule #1 of Linux is RTFM -- this means either read the manual (by typing man in the terminal, for example "man vim" or "man find" or "man life", OR just Google it. Copy the error or key words from the error into Google and hunt away! Finally, don't be afraid to ask-- but not before reading the man page and Google (or if you're in a class, ask your neighbor or the instructor.) Open source software like Linux is all-volunteer, so help volunteers by asking intelligent questions -- questions that you still have after attempting to find the answer yourself. For further reading, check out "How to Ask Questions the Smart Way" by ESR:

• To copy-paste from the Ubuntu Desktop terminal, highlight something with your mouse and press Ctrl+Shift+C (copy) or Ctrl+Shift+V (paste). You can also right-click and choose the copy or paste options.


• Make sure you're connected to the internet. Use the Wifi icon in the top right to connect to networks, or in terminal type ifconfig and ping to check what might be wrong.

• Let's start by typing sudo -i so that we become root and don't have to type sudo after half this stuff. If you've got a password, it'll ask you to type it.

• A brand-new Ubuntu install might not have its software repositories updated. Type apt-get update to fetch info about the latest software available.

• Run this command to install Vim, Apache, Git, MySQL, and PHP with my favorite plugins. apt-get install vim apache2 git-core mysql-server php5 libapache2-mod-php5 php5-gd php5-curl php5-mysql php5-xsl php5-cli

• Pay attention to any errors during this process; problems here can result in a piece of software not working later on in this guide and retracing your steps sucks.

• MySQL should ask you for a root password. Make up something extremely long and random, and store it in a safe place for future reference. (In this case, a sticky note is actually more secure than a text file.) You'll need to type this in when we get to the MySQL section and any other time you need to administer the MySQL server.

• This is my backup script (crontab) -- to make a crontab, type crontab -e

# Below is for regular backups. 10 19 * * * /usr/bin/mysqldump --opt -uUSER DBNAME > /backups/DBNAME.sql 14 19 * * * /usr/bin/mysqldump --opt -uUSER mysql > /backups/mysql.sql 16 19 * * * tar -zcf /backups/etc-backup.tgz /etc 18 19 * * * tar -zcf /backups/www-backup.tgz /var/www

For the root user, create a /root/.my.cnf file (as root) with the following content: [client] password=PASSWORD

Note that USER, PASSWORD, and DBNAME should be replaced appropriately. Formerly the crontab script included a -pPASSWORD option (without a space) but this is less secure and reliable than the .my.cnf method.

If your backups aren't created properly (0 filesize) it may be because the backup user's password isn't getting found. You can add --defaults-extra-file=/root/.my.cnf to the mysqldump command in your crontab.

Make sure you create /backups and that it is writeable by root (or whoever the cron job runs as.) Test the command before putting it in your crontab.

You'll see that I'm doing a mysqldump of the database named 'mysql' -- this is intentional as it is the database that holds configuration information including database users which you'll probably want during a restoration.

Also, I'm doing a backup of /etc which is where most configuration files are stored. Half of a website is how it's configured, so you'll definitely want that during a restoration.

This should take care of most Apache/MySQL websites as long as all websites are stored in /var/www -- if not, modify the script as needed.

• Finally, it would be a good idea to encrypt these files or at least make sure they're chmod 700 -- readable only by the folder owner and nobody else -- because they will invariably contain passwords in the config files and mysql tables. Your mileage may vary and it's your responsibility to make sure you're adequately secured.

Don't forget to copy your backups offsite. I typically add this line to a remote crontab -e:

15 21 * * 1 scp -r /backups/latest/example

You'll need to copy ~/.ssh/ from the remote machine to the /root/.ssh/authorized_keys file on though, to let scp run without a password. (Test scp manually so you can accept's key first.)

• Save, exit, and check back tomorrow to make sure the contents of /backups looks good (in nano, save is Ctrl+O and exit is Ctrl+X. In vim, you insert text by typing i, exit from insert mode with Escape, save is :w and exit is :q)

Apache: • Edit /etc/apache2/httpd.conf and insert this line at the bottom (it's ok if the file is blank): ServerName Also check /etc/apache2/ports.conf to make sure it contains the line "NameVirtualHost *:80"

• Edit /etc/apache2/sites-available/default and add ServerName localhost below the first line (the ~3rd line of the file), because otherwise it'll handle all requests even though we're about to create a new site.

• Copy /etc/apache2/sites-available/default to a new config file, named the same as your domain (with a .conf suffix), into the same folder. ( For example, type cd /etc/apache2/sites-available and then cp default mydomain.conf -- type ls to see the contents of the folder and verify the copy command worked.)

• Edit your new config file. Remove the ServerName localhost line and replace it with the following lines (again, right below the line, replacing mydomain with your domain):

ServerName ServerAlias

• In the same file, edit the DocumentRoot to be the folder you'll store your website in. For example, /var/www, /var/www/mydomain, or /home/yourusername/public_html. For demonstration purposes, you'll want it to be different than the default site so you can tell the difference.

• Edit the ErrorLog line and CustomLog line to be the location or filename you want your logfiles at. You can just change ${APACHE_LOG_DIR}/error.log and ${APACHE_LOG_DIR}/access.log to different filenames, like ${APACHE_LOG_DIR}/error-mydomain.log and so on, or you can put the files in your /home/yourusername/ folder. Up to you. If you leave them as-is, the logs for this site will go to the same place as logs for the default site, which can sometimes get confusing once you have multiple sites.

• Save your config file and exit.

• Type a2ensite mydomain.conf to enable your site, where "mydomain" is the name of your config file in the sites-available folder.

• Type service apache2 restart to restart your webserver. If there are errors, it's likely that there's a typo in your config file. Typing a2dissite mydomain will disable so you can see if it's actually your site config file that's causing the problem.

• Pay attention to the output of the restart command; errors here indicate a problem that might need to get resolved before everything works.

• Create an index.html file in your DocumentRoot folder and edit it to say something like "hello world!" -- you'll need to create the folder first, if it doesn't exist. (For example: mkdir /var/www/mydomain && nano /var/www/mydomain/index.html

• If you're doing this on your local machine, ignore the DNS step below and instead edit /etc/hosts -- add to this file, save and exit, to override DNS on this machine. For extra fun, you can do the same with and impress your friends or create new enemies. Undo it though, just in case. Any IP address starting in 127 means "this computer" so you don't need to figure out your IP address just to access your own computer.

• Make sure your domain name has its DNS pointing to your IP. Usually your registrar (for example or a certain evil "Daddy of Go" registrar) will have a DNS hosting / management option. Ensure and are both pointing to the IP of your machine. If you have a firewall that gives your machine an IP starting in 10, 172, or 192, you'll have to forward port 80 in your router to your machine, and find out your public IP using a site like

• You should now be able to access from a web browser (Firefox in Desktop or lynx in Server) and see your "hello world" message. If you see the Apache default "It works!" message, or an error, double-check your configuration files and make sure you've enabled/restarted things properly.


• When you installed MySQL above, it should've asked you to pick a password. If you forgot that password, see resetting MySQL password.

• Login to the MySQL console by typing mysql -u root -p (type your root password at the prompt.)

• In the MySQL console, let's create databases and users. MySQL commands must end with a semicolon, unlike console commands that end when you type enter. Type create database exampledb; GRANT ALL ON exampledb.* TO exampleuser@'localhost' IDENTIFIED BY 'examplepassword'; (replace example with your own details. Note that GRANT ALL can be a security risk if the username/password gets leaked or if a SQL injection attack is successful and the account does things it's not supposed to. Grant the minimum necessary privileges for security.)

• You can now either create tables and insert data on the command line, or you can restore from a dump if you have one (i.e. from a backup.)

• To create tables and insert data on the command line, first type use exampledb; and then type CREATE TABLE example_table (id INT, column1 VARCHAR(35)); INSERT INTO example_table (id, column1) VALUES (1,'examplevalue');

• To see what your database looks like now, you can type one of the following commands: show databases; show tables; show columns from example_table; select * from example_table; (remember to use exampledb before trying to do anything with tables.)

• Type quit to close MySQL.

• You don't need to do this now, but if you wanted to restore from a mysqldump, type quit and then on the command line type mysql -u root -p exampledb < dumpfilename.sql (replacing exampledb and dumpfilename as appropriate)


• Create a new PHP file in your apache site's DocumentRoot, for example /var/www/mydomain/index.php

• Paste this into your file: <?php phpinfo();

• Remember, most PHP files should start with <?php with nothing beforehand, not even a line or space. The file can end with a closing ?> however it doesn't strictly need to. It depends on the coding style and structure used but my advice is to start all files with <?php and not end files with ?> unless you need to.

• Save this file and open in a web browser. Notice that if you just go to it will show your index.html file instead. Also notice that the phpinfo() command is showing you information about your php installation, including some basics about Apache, MySQL, and the system. If you just see the code you just edited, or can't see anything / an error, something about PHP probably isn't working right, or you've mistyped something.

• Remove your index.html file (rm /var/www/mydomain/index.html) and edit the index.php file. Change the file to look like this instead: <?php echo "You are using the ".$_SERVER['HTTP_USER_AGENT']." web browser.";

• Refresh the webpage in your web browser. Cool! Hard to read though, so let's add HTML tags around the USER AGENT to make it stand out: <?php echo "You are using the <strong>".$_SERVER['HTTP_USER_AGENT']."</strong> web browser.";

• Let's store some info in a variable and do some logic: <?php $agent = $_SERVER['HTTP_USER_AGENT']; if(strpos($agent, 'Firefox') !== FALSE) { echo "<h1>Yay Firefox!</h1>"; } echo "You are using the <strong>".$agent."</strong> web browser.";

• Displaying a form and processing form input: <?php // only do processing if the form's been submitted if(isset($_POST['submitbutton'])) { // Sanitize the name input and store it in the $name variable $name = filter_var($_POST['name'], FILTER_SANITIZE_STRING); // Same for the age input, except sanitizing it as an integer $age = filter_var($_POST['age'], FILTER_SANITIZE_NUMBER_INT); // Subtract the current year from $age and store in $birthyear for use below $birthyear = date('Y') - $age; } ?><html> <body> <?php // If we've got a $birthyear to show, echo it to the page. if(isset($birthyear)) { // Note how we glue strings together in PHP; with a . echo "<h2>Cool, ".$name."! Looks like you were born around year ".$birthyear."!</h2>"; } else { // If we don't have a birthdate to show, we'll just show the form. // We're closing the PHP tag below so we can type raw HTML, but we reopen it later. Neat trick. ?> <!-- The action is set by PHP code that returns the current file path (PHP_SELF) and filters it with the htmlentities command for security. --> <form method="post" action="<?php echo htmlentities($_SERVER['PHP_SELF']); ?>"> <input type="text" name="name" id="name" value="Your Name" /> <input type="text" name="age" id="age" value="Your Age" /> <input type="submit" name="submitbutton" id="submitbutton" value="Go!" /> </form> <?php // Gotta close the "else" bracket } ?> </body> </html>

It's not much, but you've now got the basics of LAMP!

Now on to more advanced stuff -- using PHP to communicated with MySQL:

• Make a file called install.php in your DocumentRoot and paste in (or retype, to get the hang of coding) the following contents: (be sure to scroll right, some text may be cut off)

• Edit the variables at the top of the file to match the database and user/password you created earlier. Or, make up new ones and create a new database and user to match. Save and open the install.php script in your web browser. If your database stuff matches, it'll say "Install successful!"

• All the installer did was create a table and insert one row of data into it. This could've been done in the mysql command line or by importing a mysql dump, too. Each PHP "program" has its own way of setting up its database. This is a really basic installer script.

• Make a file called class.php in your DocumentRoot and paste in (or retype, to get the hang of coding) the following contents: (be sure to scroll right, some text may be cut off)

• Edit the variables at the top of the file to match the ones you put in the install.php file.

• Note how, in the code, we've got the top section that processes data and the bottom section that displays it. We could theoretically put it all together, but it gets impossible to read or debug once you get any kind of complexity.

• This isn't the best code in the world, but it's basic and shows you important caveats like sanitizing your inputs, catching errors, and decent detail on what's going on behind the scenes. In a real program, you'd want to clean stuff up more with config files and methods.

• Next, I recommend learning CodeIgniter. This is because rather than coming up with a whole file structure and helper methods yourself, it's wise to use what's already been built for the purpose. CodeIgniter is a framework built for making PHP-based database-driven web applications, and has some great tutorials available.