Installing the LAMP stack on an Amazon EC2 instance

By Philip Knerr

Introduction

Many web applications are based on the LAMP stack. If you need to host such a web application, you’ll need to install the LAMP stack first. This article shows you how.

The “LAMP” stack includes the following components:

  • The Linux operating system.
  • The Apache HTTP server.
  • The MySQL database.
  • The PHP language and platform.

The context and caveats in the Foreword apply to this article.

This information is current as of September 1, 2017.

Requirements

This article assumes you have already instantiated an Amazon EC2 instance according to the instructions in the Basic configuration for general purpose Amazon EC2 instances article. Notably, this article assumes that this EC2 instance is based on the Amazon Linux operating system.

Note that this means that the “L” portion of the LAMP stack should already be installed. Thus, the instructions below focus on the “AMP” portion.

This article further assumes that a data volume was created and mounted at /mnt/data1. If the data volume was mounted at a different mount point, or a separate data volume was not created, paths beginning with /mnt/data1 should be updated accordingly.

Installing Apache

First, check for outdated versions of the Apache HTTP server and remove them if present. Execute the following command:

sudo yum remove httpd*

Then, install an up-to-date version of the Apache HTTP server by executing the following command:

sudo yum install -y httpd24

Restarting Apache automatically upon system restart

Cause Apache to start automatically when the system boots by executing the following command:

sudo chkconfig httpd on

Then, verify that the web server will in fact start automatically. Execute the following command:

chkconfig --list httpd

If the web server was successfully configured to start automatically, this command will report on for runlevels 2 through 5. For example, the following output indicates success:

httpd           0:off   1:off   2:on    3:on    4:on    5:on    6:off

Configuring Apache

Create the following directories for web server configuration on the data volume:

  • /mnt/data1/httpd
  • /mnt/data1/httpd/sites-available
  • /mnt/data1/httpd/sites-enabled

This can be achieved by executing the following commands:

sudo mkdir /mnt/data1/httpd
sudo mkdir /mnt/data1/httpd/sites-available
sudo mkdir /mnt/data1/httpd/sites-enabled

Move existing directories for web server configuration to the data volume. Execute the following commands:

cd /etc/httpd
sudo mv /etc/httpd/conf /mnt/data1/httpd
sudo mv /etc/httpd/conf.d /mnt/data1/httpd

Create symbolic links from the standard locations for the HTTP configuration directories to the actual locations. Execute the following commands (while still in the /etc/httpd directory):

sudo ln -s /mnt/data1/httpd/conf .
sudo ln -s /mnt/data1/httpd/conf.d .

Then, create the following directory for virtual hosts on the data volume:

  • /mnt/data1/vhosts

This can be achieved by executing the following command:

sudo mkdir /mnt/data1/vhosts

Then, edit the Apache configuration file in its new location on the data volume. It was moved to /mnt/data1/httpd/conf/httpd.conf above. For example, to edit this file using the nano text editor, execute the following command:

sudo nano /mnt/data1/httpd/conf/httpd.conf

In this file, search for a line similar to the following:

ServerAdmin root@localhost

In this line, change root@localhost to the email address of the system administrator for the EC2 instance. This email address might be displayed if some error conditions occur.

Then, add index.php as a DirectoryIndex file. When an HTTP request specifies the URL of a directory, and the directory contains a DirectoryIndex file, the DirectoryIndex file is served. To make this change, search for the following code segment:

<IfModule dir_module>
    DirectoryIndex index.html
</IfModule>

In this code segment, add index.php before index.html, so that the code segment reads as follows:

<IfModule dir_module>
    DirectoryIndex index.php index.html
</IfModule>

Note that index.php will now take precedence over index.html if both files are present in the same directory. If you prefer index.html to take precedence, just add index.php to the right of index.html instead. This change can also be omitted entirely if you are sure that none of the web applications to be hosted by this server rely on DirectoryIndex files.

Next, change the LogLevel to info. Doing so records more information about web server activity (relative to the default level of warn). To make this change, search for the following directive:

LogLevel warn

Change warn to info, so that the code segment reads as follows:

LogLevel info

Finally, if you plan to add at least one virtual host, add the following line to the bottom of the file:

Include /mnt/data1/httpd/sites-enabled/*

Installing MySQL

To install both the server and the client for the MySQL database, execute the following command:

sudo yum install -y mysql56 mysql56-server

Restarting MySQL automatically upon system restart

Cause the MySQL server to start automatically when the system boots by executing the following command:

sudo chkconfig mysqld on

Then, verify that the MySQL server will in fact start automatically. Execute the following command:

chkconfig --list mysqld

If the MySQL server was successfully configured to start automatically, this command will report on for runlevels 2 through 5. For example, the following output indicates success:

mysqld          0:off   1:off   2:on    3:on    4:on    5:on    6:off

Securing the MySQL database

First, start the MySQL database by executing the following command:

sudo service mysqld start

A message will be displayed such as the following:

Initializing MySQL database:  Installing MySQL system tables...
170901 17:04:34 [Note] Ignoring --secure-file-priv value as server is running with --bootstrap.
170901 17:04:34 [Note] /usr/libexec/mysql55/mysqld (mysqld 5.5.56) starting as process 21905 ...
OK
Filling help tables...
170901 17:04:34 [Note] Ignoring --secure-file-priv value as server is running with --bootstrap.
170901 17:04:34 [Note] /usr/libexec/mysql55/mysqld (mysqld 5.5.56) starting as process 21912 ...
OK

To start mysqld at boot time you have to copy
support-files/mysql.server to the right place for your system

PLEASE REMEMBER TO SET A PASSWORD FOR THE MySQL root USER !
To do so, start the server, then issue the following commands:    

/usr/libexec/mysql55/mysqladmin -u root password 'new-password'
/usr/libexec/mysql55/mysqladmin -u root -h ip-172-31-37-136 password 'new-password'

Alternatively you can run:
/usr/libexec/mysql55/mysql_secure_installation

which will also give you the option of removing the test
databases and anonymous user created by default.  This is
strongly recommended for production servers.

See the manual for more instructions.

You can start the MySQL daemon with:
cd /usr ; /usr/libexec/mysql55/mysqld_safe &

You can test the MySQL daemon with mysql-test-run.pl
cd /usr/mysql-test ; perl mysql-test-run.pl

Please report any problems at http://bugs.mysql.com/

                                                           [  OK  ]
Starting mysqld:                                           [  OK  ]

As this message suggests, secure the MySQL installation. Execute the following command:

sudo /usr/bin/mysql_secure_installation

N.B. This command is intentionally different than the command shown in the output above.

This command starts an interactive command line script. It will ask you several questions:

  • In response to, Enter current password for root (enter for none), simply press Enter. (There is no root password yet.)
  • In response to, Set root password? [Y/n], enter Y. (You definitely want to set a root password.)
  • In response to, New password, enter any reasonably secure password you would like. This password has absolute access to all MySQL databases, so it should be different than the password used by any specific web application.
  • In response to, Re-enter new password, enter the same password again.
  • In response to, Remove anonymous users? [Y/n], enter Y. Anonymous users are insecure and usually unnecessary. Web applications will typically have one or more accounts with which to access the database. Likewise, each developer or other person who needs direct SQL access should have their own account.
  • In response to, Disallow root login remotely? [Y/n], entering Y is the more secure option. However, if you have a specific and compelling reason why the root user (the superuser) must be able to log in remotely, enter n instead. For example, this might be necessary in order to use a graphical tool installed on your local system to administer the MySQL server on your EC2 instance.
  • In response to, Remove test database and access to it? [Y/n], enter Y. The test database is usually unnecessary. If you actually need a test database, you can add one later and set permissions appropriately.
  • In response to, Reload privilege tables now? [Y/n], enter Y.

N.B. This script only works if the MySQL database is running and its data files are in the standard location. Therefore, the script needs to be run prior to the steps below.

Moving the MySQL data directory to the data volume

Stop the database by executing the following command:

sudo service mysqld stop

Once the database has stopped, move the MySQL data directory to the data volume by executing the following commands:

cd /var/lib
sudo mv mysql /mnt/data1

Create a symbolic link from the standard location for the MySQL data directory to the actual location on the data volume. Execute the following command (while still in the /var/lib directory):

sudo ln -s /mnt/data1/mysql

Restart the database by executing the following command:

sudo service mysqld start

Output similar to the following should be displayed:

Starting mysqld:                                           [  OK  ]

Installing PHP

First, check for outdated versions of the PHP platform and language and remove them if present. Execute the following command:

sudo yum remove php*

Then, install an up-to-date version of PHP 7 by executing the following command:

sudo yum install -y php71

Additionally, the PHP platform needs to be able to communicate with the MySQL database. To grant it this ability, execute the following command:

sudo yum install -y php71-mysqlnd

If you installed a different version of PHP, the package name in the command above should be changed to begin with the name of the package used to install PHP.

Any other PHP modules required by your web applications should also be installed by executing commands similar to the above. For example, frequently used modules may be installed by executing the following commands:

sudo yum install -y php71-gd
sudo yum install -y php71-mbstring
sudo yum install -y php71-mcrypt

N.B. If you have compelling reasons to use PHP 5, you can install the php56 package instead. For example, you may need to host a web application which is not yet compatible with PHP 7. That said, seriously consider using PHP 7 if at all possible. It offers many improvements over PHP 5, including improved performance and new functionality. (Also, for reasons too complicated to go into here, there is no such thing as PHP 6.)

Configuring individual applications

Each application hosted by the EC2 instance will typically exist within a separate virtual host. Each virtual host should generally have its own subdirectory, web server configuration file, and set of web server log files.

The instructions in this section should be repeated once for each virtual host to be created on the EC2 instance.

Configuring a virtual host

Change to the virtual hosts directory by executing the following command:

cd /mnt/data1/vhosts

Create a subdirectory for the virtual host within this directory by executing this command, where <virtualHostName> is the name of the virtual host:

sudo mkdir <virtualHostName>

The virtual host may be named after the canonical DNS hostname of the virtual host. It may be named after the application. The exact naming convention used is less important than the fact that a consistent naming convention is used at all.

For example, if the virtual host should be named myapp, execute the following command:

sudo mkdir myapp

Change to this subdirectory by executing the following command:

cd <virtualHostName>

For example, if the virtual host is named myapp, execute the following command:

cd myapp

Grant ownership of this subdirectory to the user and group who should manage the application. Execute the following command, where <userName> and <groupName> are the desired user name and group name:

sudo chown <userName>:<groupName> .

For example, if the application should be managed by the ec2-user user and group, execute the following command:

sudo chown ec2-user:ec2-user .

Within this subdirectory, create a subdirectory named log for web server log files specific to this virtual host. (Note that the Rails framework has its own logs which are separate from the web server logs. PHP applications may write to the web server logs or, in some frameworks, may write to yet another set of logs.) Execute the following command:

sudo mkdir log

Grant ownership of the log subdirectory to the user (but not the group) who should manage the application. Execute the following command:

sudo chown <userName> log

For example, if the application should be managed by the ec2-user user, execute the following command:

sudo chown ec2-user log

Grant ownership of the log subdirectory to the apache group under which the web server executes. Execute the following command:

sudo chgrp apache log

Grant the apache group write access to this directory by executing the following command:

sudo chmod g+w log

Create an application directory to contain the application. If the application is a traditional website, this directory is commonly named webroot. Alternatively, in many modern frameworks, this directory is typically named after the application. Execute the following command, where <applicationDirectoryName> is the desired name of the directory:

sudo mkdir <applicationDirectoryName>

For example, if the directory should be named webroot, execute the following command:

sudo mkdir webroot

Grant ownership of this subdirectory to the user and group who should manage the application. Execute the following command:

sudo chown <userName>:<groupName> <applicationDirectoryName>

For example, if the application is in the webroot directory and should be managed by the ec2-user user and group, execute the following command:

sudo chown ec2-user:ec2-user webroot

In the /mnt/data1/vhosts/sites-available directory, create a web server configuration file for this virtual host. This configuration file should have the same name as the virtual host, e.g., <virtualHostName>. To create this file, you can edit the nonexistent file with your preferred text editor, and it will be created when the file is saved. For example, to create this file using the nano text editor, execute the following command:

sudo nano /mnt/data1/httpd/sites-available/<virtualHostName>

For example, if the virtual host name is myapp, execute the following command:

sudo nano /mnt/data1/httpd/sites-available/myapp

The following configuration file may be used as a starting point:

<VirtualHost *:80>
    ServerName <fqdn>

    DocumentRoot /mnt/data1/vhosts/<virtualHostName>/<applicationDirectoryName>
    <Directory />
        Options FollowSymLinks
        AllowOverride None
    </Directory>
    <Directory /mnt/data1/vhosts/<virtualHostName>/<applicationDirectoryName>>
        AllowOverride None

        # MultiViews must be turned off.
        Options -MultiViews

        # Comment this out if you're on Apache < 2.4:
        Require all granted
    </Directory>

    ErrorLog /mnt/data1/vhosts/<virtualHostName>/log/error.log

    # Possible values include: debug, info, notice, warn, error, crit,
    # alert, emerg.
    LogLevel info

    CustomLog /mnt/data1/vhosts/<virtualHostName>/log/access.log combined
</VirtualHost>

In this configuration file, <virtualHostName> and <applicationDirectoryName> have the same meanings as above. <fqdn> is the fully-qualified domain name by which the virtual host should be accessed. For example, this could be myapp.example.com. Also, you can execute the following command to determine which version of the Apache web server is installed:

httpd -v

This configuration file will likely need to be edited to meet the specific needs of your application. Web applications frequently use environment variables which are defined in the web server configuration. This is especially true of applications leveraging the Laravel and Rails frameworks. Also, this example would need to be adapted to support Secure HTTP.

Then, change to the subdirectory for enabling sites in the web server configuration. Execute the following command:

cd /mnt/data1/httpd/sites-enabled

In this subdirectory, create a symbolic link which points to the actual configuration file which was just created. The name of the symbolic link should be a number, followed by a hyphen, followed by the name of the configuration file. The numbers should have a consistent number of digits (three is usually adequate). This strategy causes virtual hosts to be started in numeric order according to these numbers upon starting or restarting the Apache HTTP server. Execute the following command, where <position> is the number specifying the order in which the virtual host should be started:

sudo ln -s ../sites-available/<virtualHostName> <position>-<virtualHostName>

For example, if <position> should be 100, the command would be:

sudo ln -s ../sites-available/myapp 100-myapp

It is usually better to skip numbers so that you can later add new virtual hosts between existing virtual hosts. For example, you could assign <position> numbers as 100, 110, and so on. You could also group related virtual hosts together in a common numeric range.

Then, update your DNS zone file to point the fully-qualified domain name, e.g., <fqdn>, to the Elastic IP address. This can be done by adding an A record. Alternatively, it can be done by adding a CNAME record pointing to an existing A record for the server. This needs to be done even while testing, because the web server directs incoming requests to virtual hosts according to the specified hostname. The exact steps required to update your DNS zone file vary between DNS providers. Keep in mind that there will often be a delay before DNS updates take effect. This normally depends on the time-to-live (TTL) parameter, but in some cases may take even longer.

N.B. It is completely fine for multiple DNS hostnames to point to the same IP address. For example, if you have five different applications, with each running in a separate virtual host with its own DNS hostname, all five hostnames would be pointed to the same IP address. In this scenario, the web server will correctly route each hostname to the correct application.

Adding files to a virtual host

The directories and files which are part of, or required by, the application executing in a virtual host should generally be added to the application directory created above. Any directory or file therein which must be writeable by the web server should by owned by the apache group and should be writeable by this group. Conversely, if the web server has no need to write to a directory or file therein, it is safer for the directory or file to be owned by a different user and group, such as the user and group who should manage the application.

Applying these changes

The foregoing changes only take effect when Apache is started.

Therefore, to cause these changes to be applied, you can start Apache by executing the following command:

sudo service httpd start

If Apache is already running, you should instead restart Apache by executing the following command:

sudo service httpd restart

If in doubt as to whether Apache is already running, it’s safe to restart Apache. (Restarting Apache simply stops and then starts Apache. If Apache is not running, stopping Apache will fail, but this will not prevent attempting to start Apache.)

Testing PHP

To confirm that PHP is working correctly, you can create a file named test.php in the application directory. For example, you could execute the following command (as the user who manages the application):

nano /mnt/data1/vhosts/<virtualHostName>/<applicationDirectoryName>/test.php

Add the following content to this file:

<html>
    <head>
        <title>PHP Test Page</title>
    </head>
    <body>
        <h1>PHP Test Page</h1>

        <p>
            The answer is:  <?php echo 19 + 23; ?>
        </p>
    </body>
</html>

Now, try to view this file in a web browser by visiting the URL, http://<fqdn>/test.php. If PHP is working correctly, the browser should display: “The answer is: 42”. The addition is a PHP expression. Correct evaluation of the expression confirms that PHP is working (because PHP is what evaluated the expression).

The following results indicate an incorrect configuration:

  • The page does not load at all. (This may mean Apache is not working. It may also mean that a firewall, such as that defined in an EC2 security group, is blocking access to the HTTP port on the server.)
  • The Apache test page is displayed. (This may mean that the virtual host is not correctly pointing to your application. The good news is that Apache is at least accepting requests.)
  • The PHP test page is rendered, but no answer is displayed after “The answer is:” (This may mean that Apache is failing to pass PHP files to the PHP interpreter. You can see the unevaluated expression by viewing source for the page. Again, the good news is that Apache is at least accepting requests.)

Conclusion

Your Amazon EC2 instance now offers the LAMP stack. Now, install some amazing applications! Better yet, write some amazing applications!

–Phil Knerr