Setting a LAMP server on Digital Ocean + SSL
Tags: lamp, lampstack, vps
Published: 2023-06-28 14:47:22

Disclaimer: Although **I had done this procedure several** times through the years before, I have never documented it until now. I am writing this article with **no pretension to make this an innovative tutorial**, everything that will be said below, all commands and instructions are already available online in lots of other articles, and documentation, etc. The host providers themself use to make available these instructions very clear. My main goal in writing this is to register for **my own future reference**, as we often forgot commands and end up spending lots of time "googling" to remember. 

With some hope, this can somehow help someone else too!

# Steps to deploy Lamp in a VPS on Droplet on Digital Ocean


- [Initial Setup](#initial-setup) 
 - [Create Droplet and set SSH Keys](#create-droplet-and-set-SSH -keys)
 - [Setting firewall](#setting-firewall)
 - [Enabling External Access](#enabling-external-access)
- [Apache](#apache)
 - [Installing](#installing)
 - [Adjusting Firewall](#adjusting-firewall)
 - [Checking Web Server](#checking-the-web-server)
 - [Handling Apache Basic Commands](#handling-apache-basic-commands)
 - [Virtual Hosts](#virtual-hosts)
 - [Apache Files and directories overview](#apache-files-and-irectories-overview)
- [MySQL](#mysql)
- [PHP](#php)
- [SSL](#ssl)


## Initial Setup 

We must have a [Digital Ocean](https://m.do.co/c/ffe66a9df6bd) account. If you still don't sign up through this link and get $200, 60-day credit.

After login in your account, create a project and then create a Droplet.

### Create Droplet and set SSH Keys

#### Create a Droplet

To create your Droplet you can choose among lots of configurations, prices, sizes, memory, processors, etc. Select the most suitable one for your project. How to make this decision **is completely out of the scope of this article, because well, it depends**.


#### Create SSH keys


#### Login to the console

Create a new user

`adduser username`

Create password
_
password_

It's necessary to grant privileges to the new user:

`usermod -aG sudo username`

Now, when logged in as a regular user, we can type `sudo` before commands to run them with **superuser privileges**.

### Setting firewall

`ufw app list`

The expected output:
```
Available applications:
 ** OpenSSH**
```

`ufw allow OpenSSH`

Enable firewall

`ufw enable`

`ufw status`

The expected output:
```
Output
Status: active

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

### Enabling External Access 

With SSH keys

`rsync --archive --chown=username:username ~/.ssh /home/username`

To access:

`ssh -i keypub username@ip`

The keypub is the file with the SSH keys.

## Apache

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gkcmmuf3ul5999sduolb.png)


### Installing [Apache](https://www.apache.org/)

`sudo apt update`

`sudo apt install apache2`

### Adjusting firewall

`sudo ufw app list`

The expected output:

```
Available applications:
  Apache
  Apache Full
  Apache Secure
  OpenSSH
```

As indicated by the output, there are three profiles available for Apache:

-  Apache: This profile opens only port 80; 

-  Apache Full: This profile opens both port 80 and port 443 (TLS/SSL encrypted traffic);

-  Apache Secure: This profile opens only port 443 (TLS/SSL encrypted traffic);

`sudo ufw allow 'Apache'`

`sudo ufw status`

The expected output:

```
Output
Status: active

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


### Checking the Web Server

`sudo systemctl status apache2`

The expected output:

```
apache2.service - The Apache HTTP Server
     Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: enabled)
     Active: active (running) since Thu 2020-04-23 22:36:30 UTC; 20h ago
       Docs: https://httpd.apache.org/docs/2.4/
   Main PID: 29435 (apache2)
      Tasks: 55 (limit: 1137)
     Memory: 8.0M
     CGroup: /system.slice/apache2.service
             ├─29435 /usr/sbin/apache2 -k start
             ├─29437 /usr/sbin/apache2 -k start
             └─29438 /usr/sbin/apache2 -k start
```

Access the default Apache landing page to **confirm that the software is running properly through your IP address**. We should see something like this:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zlcbs9obpygtsg923863.png)

If you do not know your server’s IP address, try typing this at your server’s command prompt:

` hostname -I `

You will get back a few addresses separated by spaces. 



### Handling Apache Basic Commands

Some basic management commands to handle Apache using systemctl are necessary.

To stop your web server:

`sudo systemctl stop apache2`

To start the web server when it is stopped:

`sudo systemctl start apache2`

To stop and then start the service again:

`sudo systemctl restart apache2`

Reload without dropping connections:

`sudo systemctl reload apache2`

By default, Apache is configured to start automatically when the server boots. If this is not what you want, disable this behavior by typing:

`sudo systemctl disable apache2`

To re-enable the service to start up at boot:

`sudo systemctl enable apache2`

### Virtual Hosts

While working with an Apache web server, we can use virtual hosts (see server blocks in Nginx) to encapsulate configuration details and **host more than one domain from a single server**. 

Apache on Ubuntu has one server block enabled by default that is configured to serve documents from the /var/www/html directory. We will create a directory structure within /var/www for a domain-name site, leaving /var/www/html in place as the **default directory** to be served if a client request doesn’t match any other sites.

Make sure to replace the domain-name this with your own domain name.

Creating dir:

`sudo mkdir /var/www/domain-name.com`

Assign user to dir:

`sudo chown -R $USER:$USER /var/www/domain-name`

Set permissions:

`sudo chmod -R 755 /var/www/domain-name`

Create index (optional):

`sudo nano /var/www/domain-name/index.html`

The index.html content:

```
<html>
    <head>
        <title>Welcome to domain-name!</title>
    </head>
    <body>
        <h1>The domain-name virtual host is working!</h1>
    </body>
</html>
```

#### Create the virtual host

For Apache to serve this content, it’s necessary to create a virtual host file with the correct directives. Instead of modifying the default configuration file located at /etc/apache2/sites-available/000-default.conf directly, let’s make a new one at /etc/apache2/sites-available/your_domain.conf:

`sudo nano /etc/apache2/sites-available/domain-name.conf`

The file content:

```
<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    ServerName domain-name.com
    ServerAlias www.domain-name.com
    DocumentRoot /var/www/domain-name
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
```

`sudo a2ensite domain-name.conf`

`sudo a2dissite 000-default.conf`

`sudo apache2ctl configtest`

`sudo systemctl restart apache2`


### Apache Files and Directories Overview

It's interesting to be familiarized with a few important Apache directories and files.

#### Content

- **/var/www/html:** The actual web content, can be changed by altering Apache configuration files.

#### Server Configuration

- **/etc/apache2:** The Apache configuration directory;
- **/etc/apache2/apache2.conf:** The main Apache configuration file. This can be modified to make changes to the Apache global configuration. This file is responsible for loading many of the other files in the configuration directory.

- **/etc/apache2/ports.conf:** This file specifies the ports that Apache will listen on. 
- **/etc/apache2/sites-available/:** The directory where per-site virtual hosts can be stored. Apache will not use the configuration files found in this directory unless they are linked to the sites-enabled directory. Typically, all server block configuration is done in this directory and then enabled by **linking to the other directory with the a2ensite command**.
- **/etc/apache2/sites-enabled/:** The directory where enabled per-site virtual hosts are stored. Typically, these are created by linking to configuration files found in the sites-available directory with the a2ensite. 
 

- **/etc/apache2/conf-available/, /etc/apache2/conf-enabled/:** These directories have the same relationship as the sites-available and sites-enabled directories but are used to store configuration fragments that do not belong in a virtual host. Files in the conf-available directory can be **enabled with the a2enconf command and disabled with the a2disconf** command.

- 
**/etc/apache2/mods-available/, /etc/apache2/mods-enabled/:** These directories contain the available and enabled modules, respectively. Files ending in .load contain fragments to load specific modules, while files ending in .conf contain the configuration for those modules. Modules can be enabled and disabled using the a2enmod and a2dismod commands.

#### Server Logs

- 
**/var/log/apache2/access.log:** By default, every request to your web server is recorded in this log file unless Apache is configured to do otherwise.

- 
**/var/log/apache2/error.log:** By default, all errors are recorded in this file. The LogLevel directive in the Apache configuration specifies how much detail the error logs will contain.

## MySQL

Now we have a web server up and running, we need to install the **database system**. MySQL is a popular option used within PHP environments.
Use apt to acquire and install this software:

`sudo apt install mysql-server`

Access the mysql:

`sudo mysql`

This will connect to the **MySQL server** as the administrative database user root, which is inferred by the use of sudo when running this command. 

Expected output:

```
Output
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 10
Server version: 8.0.28-0ubuntu4 (Ubuntu)

Copyright (c) 2000, 2022, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 
```

Exit mysql:

`mysql> exit`


## PHP

With Apache installed to serve our content and MySQL installed to store and manage our data, we need **PHP that will process code to display dynamic content to the final user**. 

In addition to the **php package**, we will need **php-mysql**, a PHP module that allows PHP to communicate with MySQL-based databases. We will also need **libapache2-mod-php** to enable Apache to handle PHP files. Core PHP packages will automatically be installed as dependencies.

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

Check version:

`php -v`

Expected similar output:

```
Output
PHP 8.1.2 (cli) (built: Mar  4 2022 18:13:46) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.2, Copyright (c) Zend Technologies
    with Zend OPcache v8.1.2, Copyright (c), by Zend Technologies
```

🚀Yay! Our LAMP server is fully installed and cofigured. To add more domains, performe the virtual hosts stpes over and over again (of course we have to think about traffic, hardware capacity, but it is completeley out of the scope of this article). For our purposes we can stop here and everything is working already. For safety, I will add **SSL for securing our Apache** in the next step and although is higly recommendable, it's optional.


# 🔐 SSL

That's a plus section. For securing Secure Apache on Ubuntu we will be using Let's Encrypt, a Certificate Authority (CA) that facilitates obtaining and installing free TLS/SSL certificates.

## Installing Certbot

To obtain an SSL certificate with Let’s Encrypt, we need to install the Certbot software on our server and a plugin that integrates Certbot with Apache:

`sudo apt install certbot python3-certbot-apache`

### Checking your Apache Virtual Host Configuration

To automatically obtain and configure SSL for your web server, Certbot needs to find the correct virtual host within your Apache configuration files, which we already [did previously](#virtual-hosts).

Checking just in case:

`sudo apache2ctl configtest`

The expected output should be:

```
Output
Syntax OK
```


### Allowing HTTPS Through the Firewall

[Previously we enabled the UFW firewall](#adjusting-firewall), so we'll need to adjust the settings to **allow HTTPS traffic**. 

Verifying what kind of traffic is currently allowed on your server:

`sudo ufw status`

Our output should look something like this, meaning that only HTTP traffic on port 80 is currently allowed:

```
Output
Status: active

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

To let in HTTPS traffic, we have to allow the “Apache Full” profile and delete the redundant “Apache” profile:

`sudo ufw allow 'Apache Full'`

`sudo ufw delete allow 'Apache'`

Our status will now look like this:

`sudo ufw status`

Expected output:

```
Output
Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere                  
Apache Full                ALLOW       Anywhere                  
OpenSSH (v6)               ALLOW       Anywhere (v6)             
Apache Full (v6)           ALLOW       Anywhere (v6)  
```
      
### Obtaining an SSL Certificate

Certbot provides a variety of ways to obtain SSL certificates through plugins, let's use Apache:

`sudo certbot --apache`

A series of questions will be prompted to configure our SSL certificate. 

- Valid e-mail address which will be used for renewal notifications and security notices:

```
Output
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator apache, Installer apache
Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel): you@your_domain
```

Hit ENTER to proceed to the next step. 

- Confirm if you agree to Let’s Encrypt terms of service:

```
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v02.api.letsencrypt.org/directory
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(A)gree/(C)ancel: A
```

- We’ll be asked if you would like to share your email with the Electronic Frontier Foundation to receive news and other information:

```
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: N
```

- Inform Certbot of which domains we’d like to activate HTTPS for. The listed domain names are automatically obtained from your Apache virtual host configuration:

```
Which names would you like to activate HTTPS for?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: your_domain
2: www.your_domain
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel): 
```

We’ll see output like this:

```
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for domain-name
http-01 challenge for www.domain-name
Enabled Apache rewrite module
Waiting for verification...
Cleaning up challenges
Created an SSL vhost at /etc/apache2/sites-available/domain-name-ssl.conf
Enabled Apache socache_shmcb module
Enabled Apache ssl module
Deploying Certificate to VirtualHost /etc/apache2/sites-available/domain-name-le-ssl.conf
Enabling available site: /etc/apache2/sites-available/domain-name-le-ssl.conf
Deploying Certificate to VirtualHost /etc/apache2/sites-available/domain-name-le-ssl.conf

```

- 
Next, select whether or not you want HTTP traffic redirected to HTTPS:

```
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
```

After this, **Certbot’s configuration is finished**, and we will be presented with the final remarks about your new certificate.

### Verifying Certbot Auto-Renewal

[Let’s Encrypt’s](https://letsencrypt.org) certificates are only **valid for ninety days** to ensure that misused certificates or stolen keys will expire sooner rather than later.

We do not need to worry about this renewal process since the certbot package we installed previously takes care of this. It has a script that runs twice a day and will automatically renew any certificate that’s within thirty days of expiration.

To check the status of this service:

`sudo systemctl status certbot.timer`

The expected output should be similar to this:

```
Output
● certbot.timer - Run certbot twice daily
     Loaded: loaded (/lib/systemd/system/certbot.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Tue 2020-04-28 17:57:48 UTC; 17h ago
    Trigger: Wed 2020-04-29 23:50:31 UTC; 12h left
   Triggers: ● certbot.service

Apr 28 17:57:48 fine-turtle systemd[1]: Started Run certbot twice daily.
```

To test the renewal process manually:

`sudo certbot renew --dry-run`

If you see no errors, you’re all set. When necessary, Certbot will renew your certificates and reload Apache to pick up the changes. If the automated renewal process ever fails, Let’s Encrypt will send a message to the email you specified, warning you when your certificate is about to expire.

For further questions about using Certbot, please refer to [their documentation](https://letsencrypt.org/).


Some References:
- [MySQL on Ubuntu](https://www.digitalocean.com/community/tutorials/how-to-install-mysql-on-ubuntu-20-04)
- [Apache on Ubuntu](https://www.digitalocean.com/community/tutorial_collections/how-to-install-apache)
- [Apache Software Foundation](https://www.apache.org/)
- [Lamp on Ubuntu 22-04](https://www.digitalocean.com/community/tutorials/how-to-install-linux-apache-mysql-php-lamp-stack-on-ubuntu-22-04)
- [Secure Apache with Let's Encrypt](https://www.digitalocean.com/community/tutorials/how-to-secure-apache-with-let-s-encrypt-on-ubuntu-20-04)
- [Let's Encrypt](https://letsencrypt.org/)


The process for setup LAMP in an EC2 is quite similar, which I will write about soon.