Ubuntu LEMP 5.6

Last updated: December 4th 2019

The Perfect Server - Ubuntu LEMP 5.6

This document details how we've set up our Ubuntu LEMP (Linux (E)Nginx Mysql and PHP) 5.6 stack for legacy applications. By provisioning a base Ubuntu Bionic image, and following these steps exactly, you would be able to duplicate our server image.

This is a living document. Based on on-going experience and community feedback we tweak our stacks and keep this doc continuously updated.

Install fail2ban and enable firewall

apt-get update; apt-get upgrade -y; apt-get install -y fail2ban ufw;
ufw allow 21
ufw allow 50000:50099/tcp
ufw allow out 20/tcp
ufw allow 22
ufw allow 80
ufw allow 443
ufw --force enable

Add some PPAs to stay current

apt-get install -y software-properties-common
apt-add-repository ppa:nginx/development -y
apt-add-repository ppa:ondrej/apache2 -y
apt-add-repository ppa:ondrej/nginx-mainline -y
apt-add-repository ppa:ondrej/php -y

Set up MariaDB repositories

apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xF1656F24C74CD1D8
add-apt-repository 'deb [arch=amd64] http://mariadb.mirror.nucleus.be/repo/10.4/ubuntu bionic main' -y

Install base packages

apt-get update; apt-get install -y build-essential curl nano wget lftp unzip bzip2 arj nomarch lzop htop openssl gcc git binutils libmcrypt4 libpcre3-dev make python3 python3-pip supervisor unattended-upgrades whois zsh imagemagick uuid-runtime

Set the timezone to UTC

ln -sf /usr/share/zoneinfo/UTC /etc/localtime

Install PHP5.6 and common PHP packages

apt-get install -y php5.6-cli php5.6-dev php5.6-pgsql php5.6-sqlite3 php5.6-gd php5.6-curl php5.6-memcached php5.6-imap php5.6-mysql php5.6-mbstring php5.6-xml php5.6-imagick php5.6-zip php5.6-bcmath php5.6-soap php5.6-intl php5.6-readline php5.6-mcrypt php5.6-common php5.6-pspell php5.6-tidy php5.6-xmlrpc php5.6-xsl php5.6-opcache php-apcu

Install Composer

curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer

Install and configure Memcached

apt-get install -y memcached
sed -i 's/-l 0.0.0.0/-l 127.0.0.1/' /etc/memcached.conf
systemctl restart memcached

Update PHP CLI configuration

sed -i "s/error_reporting = .*/error_reporting = E_ALL/" /etc/php/5.6/cli/php.ini
sed -i "s/display_errors = .*/display_errors = On/" /etc/php/5.6/cli/php.ini
sed -i "s/memory_limit = .*/memory_limit = 512M/" /etc/php/5.6/cli/php.ini
sed -i "s/;date.timezone.*/date.timezone = UTC/" /etc/php/5.6/cli/php.ini

Configure sessions directory permissions

chmod 733 /var/lib/php/sessions
chmod +t /var/lib/php/sessions

Install Nginx and PHP-FPM

apt-get install -y nginx php5.6-fpm

Generate dhparam file for stronger Nginx SSL security

openssl dhparam -out /etc/nginx/dhparams.pem 2048

Tweak PHP-FPM settings

Please note: We are suppressing PHP error output here by setting these options to production values

sed -i "s/error_reporting = .*/error_reporting = E_ALL \& ~E_NOTICE \& ~E_STRICT \& ~E_DEPRECATED/" /etc/php/5.6/fpm/php.ini
sed -i "s/display_errors = .*/display_errors = Off/" /etc/php/5.6/fpm/php.ini
sed -i "s/memory_limit = .*/memory_limit = 512M/" /etc/php/5.6/fpm/php.ini
sed -i "s/upload_max_filesize = .*/upload_max_filesize = 256M/" /etc/php/5.6/fpm/php.ini
sed -i "s/post_max_size = .*/post_max_size = 256M/" /etc/php/5.6/fpm/php.ini
sed -i "s/;date.timezone.*/date.timezone = UTC/" /etc/php/5.6/fpm/php.ini

Tune PHP-FPM pool settings

sed -i "s/;listen\.mode.*/listen.mode = 0666/" /etc/php/5.6/fpm/pool.d/www.conf
sed -i "s/;request_terminate_timeout.*/request_terminate_timeout = 60/" /etc/php/5.6/fpm/pool.d/www.conf
sed -i "s/pm\.max_children.*/pm.max_children = 70/" /etc/php/5.6/fpm/pool.d/www.conf
sed -i "s/pm\.start_servers.*/pm.start_servers = 20/" /etc/php/5.6/fpm/pool.d/www.conf
sed -i "s/pm\.min_spare_servers.*/pm.min_spare_servers = 20/" /etc/php/5.6/fpm/pool.d/www.conf
sed -i "s/pm\.max_spare_servers.*/pm.max_spare_servers = 35/" /etc/php/5.6/fpm/pool.d/www.conf
sed -i "s/;pm\.max_requests.*/pm.max_requests = 500/" /etc/php/5.6/fpm/pool.d/www.conf

Tweak Nginx settings

sed -i "s/worker_processes.*/worker_processes auto;/" /etc/nginx/nginx.conf
sed -i "s/# multi_accept.*/multi_accept on;/" /etc/nginx/nginx.conf
sed -i "s/# server_names_hash_bucket_size.*/server_names_hash_bucket_size 128;/" /etc/nginx/nginx.conf
sed -i "s/# server_tokens off/server_tokens off/" /etc/nginx/nginx.conf

If you want XDebug - install it and follow the on-screen instructions

This is not installed per default on Webdock stacks - included here for reference.

pecl install xdebug

Configure Gzip for Nginx

Execute the following command to configure Gzip

cat > /etc/nginx/conf.d/gzip.conf << EOF
gzip_comp_level 5;
gzip_min_length 256;
gzip_proxied any;
gzip_vary on;

gzip_types
application/atom+xml
application/javascript
application/json
application/rss+xml
application/vnd.ms-fontobject
application/x-web-app-manifest+json
application/xhtml+xml
application/xml
font/otf
font/ttf
image/svg+xml
image/x-icon
text/css
text/plain;
EOF

Install latest NodeJS LTS

curl --silent --location https://deb.nodesource.com/setup_12.x | bash -; apt-get update; apt-get install -y nodejs;

Install MariaDB (MySQL) and set a strong root password

Remember to make a note of your password, you are going to need it in a minute

apt-get install -y mariadb-server;

Secure your MariaDB installation

mysql_secure_installation

Point PHP CLI to PHP v5.6

rm /etc/alternatives/php; ln -s /usr/bin/php5.6 /etc/alternatives/php;

Confirm version by running

php -v

Install PHPMyAdmin

We will install PHPMyAdmin using Composer as Ubuntu packages are no longer being maintained.

cd /var/www
composer create-project phpmyadmin/phpmyadmin
cp /var/www/phpmyadmin/config.sample.inc.php /var/www/phpmyadmin/config.inc.php
mysql -u root -pYOUR_ROOT_PASSWORD < /var/www/phpmyadmin/sql/create_tables.sql
sed -i "s/\$cfg\['blowfish_secret'\] = '';.*/\$cfg\['blowfish_secret'\] = '$(uuidgen)';/" /var/www/phpmyadmin/config.inc.php
mkdir /var/www/phpmyadmin/tmp; chown www-data:www-data /var/www/phpmyadmin/tmp;

Symlink PHPMyAdmin, create logs dir and set permissions and ownership on /var/www

ln -s /var/www/phpmyadmin/ /var/www/html/phpmyadmin;  mkdir /var/www/logs;  chown www-data:www-data /var/www/html; chown www-data:www-data /var/www/logs; chown www-data:www-data /var/www; chmod -R g+rw /var/www;

Install Mongodb

MongoDB is installed but disabled by default in Webdock stacks. Click here to learn how to enable MongoDB

Please note: This is the legacy MongoDB driver for PHP v5.6

apt-get install -y libcurl4-openssl-dev pkg-config libssl-dev mongodb
pecl install mongo

add extension=mongo.so to PHP-fpm and PHP-cli configuration:

echo "extension=mongo.so" > /etc/php/5.6/fpm/conf.d/30-mongodb.ini
echo "extension=mongo.so" > /etc/php/5.6/cli/conf.d/30-mongodb.ini

Install Redis

apt install redis-server -y;

Set systemd for supervised and restart Redis

sed -i "s/supervised.*/supervised systemd/" /etc/redis/redis.conf; systemctl restart redis.service;

You may want to further secure your Redis installation

Create Nginx virtual host config

Edit the file /etc/nginx/sites-available/webdock and set the following

Now disable the default and symlink in your config

rm /etc/nginx/sites-enabled/default
ln -s /etc/nginx/sites-available/webdock /etc/nginx/sites-enabled/webdock

Install Letsencrypt Certbot

add-apt-repository ppa:certbot/certbot -y; apt-get update; apt-get install -y python-certbot-nginx;

Restart PHP-FPM and Nginx

systemctl restart php5.6-fpm; systemctl restart nginx;

Setup and configure FTP

FTP is enabled by default in Webdock stacks. If you do not use FTP or feel it's a security risk, you should disable it. Click here to learn how to disable FTP.

We are now building and installing Pure-FTPd directly from source instead of using APT as the official Ubuntu packages are very much out of date, and doing it this way simplifies config somewhat

We start with generating our TLS certificate

openssl dhparam -out /etc/ssl/private/pure-ftpd-dhparams.pem 2048
openssl req -x509 -days 36500 -nodes -newkey rsa:2048 -sha256 -keyout \
  /etc/ssl/private/pure-ftpd.pem \
  -out /etc/ssl/private/pure-ftpd.pem
chmod 600 /etc/ssl/private/*.pem

Download, build and install Pure-FTPd - check what the latest version is at pureftpd.org and update here if appropriate:

wget https://download.pureftpd.org/pub/pure-ftpd/releases/pure-ftpd-1.0.49.tar.gz -O /tmp/pure-ftpd-1.0.49.tar.gz
cd /tmp; tar -xf pure-ftpd-1.0.49.tar.gz; cd pure-ftpd-1.0.49;
./configure --prefix=/usr --with-everything --with-tls --with-certfile=/etc/ssl/private/pure-ftpd.pem; make install;
cd /tmp; rm -rf ./pure-*;

Set our config and setup PureDB for authentication. Here we are disallowing anonymous login, setting MinUID to 33 so that users in the www-data group can log in, enabling TLS and non-TLS connections and lastly setting our chosen passive port range:

sed -i "s/BrokenClientsCompatibility .*/BrokenClientsCompatibility yes/" /etc/pure-ftpd.conf
sed -i "s/NoAnonymous .*/NoAnonymous yes/" /etc/pure-ftpd.conf
sed -i "s/# PureDB  /PureDB/" /etc/pure-ftpd.conf
sed -i "s/MinUID .*/MinUID 33/" /etc/pure-ftpd.conf
sed -i "s/# TLS /TLS /" /etc/pure-ftpd.conf
sed -i "s/# PassivePortRange .*/PassivePortRange 50000 50099/" /etc/pure-ftpd.conf
touch /etc/pureftpd.passwd; /usr/bin/pure-pw mkdb;

By editing the config file and setting TLS to a value of "2" you can force TLS connections. However, as we are self-signing our certificate here, you may encounter problems connecting with your FTP client and may need to forcibly ignore certificate errors.

Create the systemd service file so Pure-FTPd starts on boot

cat > /etc/systemd/system/pure-ftpd.service << EOF
# pure-ftpd binary startup for Webdock servers
# To reload systemd daemon after changes to this file:
# systemctl --system daemon-reload
[Unit]
Description=Pure-FTPd FTP server
After=network-online.target

[Service]
Type=forking
ExecStart=/usr/sbin/pure-ftpd /etc/pure-ftpd.conf
PIDFile=/var/run/pure-ftpd.pid
Restart=always
RestartSec=1

[Install]
WantedBy=multi-user.target
EOF

Enable the daemon

systemctl --system daemon-reload; systemctl enable pure-ftpd; systemctl start pure-ftpd;

Complete the configuration

systemctl restart pure-ftpd

Activate Fail2Ban for Pure-FTPd

cat > /etc/fail2ban/jail.d/pure-ftpd.conf << EOF
[pure-ftpd]
enabled=true
maxretry=5
EOF

Restart Fail2Ban

 systemctl restart fail2ban

Set up logrotate for our Nginx logs

Edit /etc/logrotate.d/vhost and add the following - this gives you 10 days of logs, rotated daily

/var/www/logs/*.log {
 rotate 10
 daily
 compress
 delaycompress
 sharedscripts
 
 postrotate
 systemctl reload nginx > /dev/null
 endscript
}

Setup unattended security upgrades

cat > /etc/apt/apt.conf.d/50unattended-upgrades << EOF
Unattended-Upgrade::Allowed-Origins {
"Ubuntu bionic-security";
};
Unattended-Upgrade::Package-Blacklist {
//
};
EOF
cat > /etc/apt/apt.conf.d/10periodic << EOF
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";
EOF

Restart your server

Make sure all services come up properly. You can see what services are listening on which ports on your server by running

netstat -tapn

Congratulations. All services should now come up and you have a production-ready webserver stack running. You can further tune your webserver and PHP-FPM settings based on your use-case (e.g. whether you have a high or low traffic site, mostly serve static or dynamic content etc.). Try Googling "optimize php-fpm" or "optimize apache" for example and pick settings that fit your requirements and chosen Webdock profile. If you find some optimized defaults you would like us to include in our stacks, please let us know and we will take a look.