Ubuntu Focal LAMP 7.4

Last updated: September 15th 2022

The Perfect Server - Ubuntu LAMP 7.4

This document details how we've set up our Ubuntu LAMP (Linux Apache Mysql and PHP) 7.4 stack. By provisioning a base Ubuntu Focal 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 document continuously updated.

Please note: There may be breaking changes between PHP 7.2/7.3 and 7.4. Click here for more information.

Install fail2ban and enable firewall

apt-get update; apt-get upgrade -y; apt-get install -y fail2ban ufw;
ufw allow 22
ufw allow 80
ufw allow 443

# Skip the following 3 lines if you do not plan on using FTP
ufw allow 21 
ufw allow 50000:50099/tcp 
ufw allow out 20/tcp

# And lastly we activate UFW
ufw --force enable

We choose the port range 50000->50099 in order to allow passive FTP connections.

Add some PPAs to stay current

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

Set up MariaDB repositories

apt-key adv --fetch-keys 'https://mariadb.org/mariadb_release_signing_key.asc'
add-apt-repository 'deb [arch=amd64,arm64,ppc64el] https://mirrors.dotsrc.org/mariadb/repo/10.6/ubuntu focal main'

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 net-tools zip dirmngr apt-transport-https

Set the timezone to UTC

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

Set SSH to KeepAlive

If you want the SSH Daemon to keep your connections alive, you can run the following commands:

sed -i "s/#TCPKeepAlive yes/TCPKeepAlive yes/" /etc/ssh/sshd_config
sed -i "s/#ClientAliveInterval 0/ClientAliveInterval 60/" /etc/ssh/sshd_config
sed -i "s/#ClientAliveCountMax 3/ClientAliveCountMax 3/" /etc/ssh/sshd_config

Install PHP7.4 and common PHP packages

apt-get install -y php7.4-cli php7.4-dev php7.4-pgsql php7.4-sqlite3 php7.4-gd php7.4-curl php7.4-memcached php7.4-imap php7.4-mysql php7.4-mbstring php7.4-xml php7.4-imagick php7.4-zip php7.4-bcmath php7.4-soap php7.4-intl php7.4-readline php7.4-common php7.4-pspell php7.4-tidy php7.4-xmlrpc php7.4-xsl php7.4-opcache php7.4-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' /etc/memcached.conf
systemctl restart memcached

Update PHP CLI configuration

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

Configure sessions directory permissions

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

Install Apache and PHP-FPM

apt-get install -y apache2 apache2-utils php7.4-fpm

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/7.4/fpm/php.ini
sed -i "s/display_errors = .*/display_errors = Off/" /etc/php/7.4/fpm/php.ini
sed -i "s/memory_limit = .*/memory_limit = 512M/" /etc/php/7.4/fpm/php.ini
sed -i "s/upload_max_filesize = .*/upload_max_filesize = 256M/" /etc/php/7.4/fpm/php.ini
sed -i "s/post_max_size = .*/post_max_size = 256M/" /etc/php/7.4/fpm/php.ini
sed -i "s/;date.timezone.*/date.timezone = UTC/" /etc/php/7.4/fpm/php.ini

Tune PHP-FPM pool settings

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

Tweak Apache settings - let's hide what OS and Webserver this server is running

sed -i "s/ServerTokens OS/ServerTokens Prod/" /etc/apache2/conf-available/security.conf
sed -i "s/ServerSignature On/ServerSignature Off/" /etc/apache2/conf-available/security.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

Install latest NodeJS LTS

curl -fsSL https://deb.nodesource.com/setup_17.x | sudo -E bash -
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 install mariadb-server mariadb-backup -y

Secure your MariaDB installation


Install phpMyAdmin

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

Please check this phpMyAdmin issue


and if it is not marked as resolved, you need to change the composer create-project command below to:

composer create-project phpmyadmin/phpmyadmin --repository-url=https://www.phpmyadmin.net/packages.json --no-dev

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

wget -qO - https://www.mongodb.org/static/pgp/server-5.0.asc | sudo apt-key add -
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/5.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-5.0.list
apt update
apt-get install -y libcurl4-openssl-dev pkg-config libssl-dev mongodb-org

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

pecl install mongodb
echo "extension=mongodb.so" > /etc/php/7.4/fpm/conf.d/30-mongodb.ini
echo "extension=mongodb.so" > /etc/php/7.4/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 Apache virtual host config

Edit the file /etc/apache2/sites-available/webdock.conf and set the following

Now disable the default and symlink in your config

rm /etc/apache2/sites-enabled/000-default.conf
ln -s /etc/apache2/sites-available/webdock.conf /etc/apache2/sites-enabled/webdock.conf

Make sure Apache modules are enabled

a2enmod suexec rewrite ssl actions include cgi actions proxy_fcgi alias headers;

Install Letsencrypt Certbot

apt install -y python3-certbot-apache;

Restart PHP-FPM and Apache

systemctl restart php7.4-fpm; systemctl restart apache2;

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
Description=Pure-FTPd FTP server

ExecStart=/usr/sbin/pure-ftpd /etc/pure-ftpd.conf


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

Restart Fail2Ban

systemctl restart fail2ban

For a further discussion on other services which can be protected with Fail2Ban, please see our Fail2Ban configuration guide.

(Optional) Install BotGuard Server Module

BotGuard provides a service to protect your website from malicious bots, crawlers, scrapers, and hacker attacks. It selectively blocks bad bot traffic in real time, while granting unrestricted access to the website to human users, search engines, and affiliate bots. Read more on their website.

Please note: This is a completely optional step and not required for the functioning of your server. We include it here as Webdock offers 1 free domain protected per Server as part of our VPS Server packages.

Start with installing a missing required package and adding their repository key:

apt install apt-transport-https -y
curl -fsSL https://repo.botguard.net/botguard.gpg | apt-key add -

Next, install the module itself.  Read more in their integration guide here.

add-apt-repository "deb https://repo.botguard.net/debian $(lsb_release -cs) main"
apt update; apt install libapache2-mod-botguard -y
systemctl restart apache2

BotGuard is now installed and once you have your domain set up in their dashboard you can edit /etc/apache2/mods-available/botguard.conf in order to set your assigned BotGuard servers.

For more information, please see the BotGuard Integration Guide for Apache

Set up logrotate for Apache logs

Execute the following to give you log rotation for Apache - this gives you 10 days of logs, rotated daily

cat > /etc/logrotate.d/vhost << EOF
/var/www/logs/*.log {
 rotate 10
 /usr/sbin/apache2ctl graceful > /dev/null

Setup unattended security upgrades

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";

You can further tweak unattended upgrades if you wish in /etc/apt/apt.conf.d/50unattended-upgrades. The default in Focal is to only install security upgrades automatically.

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.

Related articles