Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save ntopulos/d60d6d32342dd1ec69756b4da1d4f278 to your computer and use it in GitHub Desktop.
Save ntopulos/d60d6d32342dd1ec69756b4da1d4f278 to your computer and use it in GitHub Desktop.

Production LAMP server setup on Ubuntu 16.04

Introduction

Summary of the steps to get a Ubuntu 16.04 production server running with LAMP, multiple virtual hosts and Let's Encrypt SSL.

Initial setup

  1. Follow the Initial Server Setup with Ubuntu 16.04 tutorial from Digitalocean.

  2. Change the time zone to match your location:

     sudo dpkg-reconfigure tzdata
    
  3. Update the machine:

     apt-get update
     apt-get upgrade
    

LAMP installation

  1. Install LAMP:

     apt-get install lamp-server^
    
  2. Allow Apache through the firewall:

     ufw allow http
     ufw allow https
    

    You should now be able to reach your web server.

Apache configuration

Install multiple PHP versions

Multiple PHP versions on Ubuntu 16.04

Apache miscellaneous

After modifying a sites-available configuration file, a reload is sufficient:

service apache2 reload

To remove a host:

a2dissite example.local.conf

To add a module, for instance mod_rewrite:

a2enmod rewrite
service apache2 restart

To easily distinguish development and production environments, we set a variable inside /etc/apache2/apache2.conf:

# Environment
SetEnv APP_ENV "development"

MySQL configuration

Permanently change SQL mode in MySQL

MySQL 5.7 introduces some changes to the default configuration that can break some old script queries. For instance, the ONLY_FULL_GROUP_BY. If you need to continue developing an old project containing such dependencies, here is how to adapt the MySQL configuration.

First, connect to mysql and run this query:

select @@sql_mode;
+-------------------------------------------------------------------------------------------------------------------------------------------+
| @@sql_mode                                                                                                                                |
+-------------------------------------------------------------------------------------------------------------------------------------------+
| ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+-------------------------------------------------------------------------------------------------------------------------------------------+

Copy the output string and remove ONLY_FULL_GROUP_BY from it. We will then paste the result to a mysql configuration file.

But first, we must find out which configuration file our MySQL uses:

which mysqld
/usr/sbin/mysqld

Then, we use this path to execute the lookup:

/usr/sbin/mysqld --verbose --help | grep -A 1 "Default options"

Default options are read from the following files in the given order:
/etc/my.cnf /etc/mysql/my.cnf ~/.my.cnf

We open the first existing file from this list, and add a [mysqld] section with our customised string from the previous step:

[mysqld]
sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"

Restart MySQL:

sudo service mysql restart

Security

fail2ban

How To Protect SSH with Fail2Ban on Ubuntu 14.04

Let's Encrypt

To install Let's Encrypt certificate(s), each concerned domain (.conf ServerName) must be defined in a separate configuration file.

Define all the domains/subdomains, and activate the default-ssl.conf, use the eff.org certbot. It will generate, install, register the SSL certificate(s) and edit your Apache virtual hosts accordingly. The bot also gives the option to redirect http to https.

When the certificates are installed and https works, it is time to test the renewal:

letsencrypt renew --email [email protected] --agree-tos --dry-run

If it works, something like this can be added to your cronjobs crontab -e:

36 4,16 * * *  letsencrypt renew --email [email protected] --agree-tos --no-self-upgrade > /dev/null 2>&1

The cronjob should run twice a day at random minutes.

To get a log of what is happening:

36 4,16 * * *  letsencrypt renew --email [email protected] --agree-tos --no-self-upgrade >> /var/log/letsencrypt/letsencrypt.renew.log 2>&1

On nearly all systems the above cron will fail because of a PATH issue: it seems that crontab uses a different PATH than a regular user, leading to execution failures.
To avoid that, get your PATH:

echo $PATH

Copy it, and paste at the beginning of your cron file:

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment