Ubuntu web host

by Gisle Hannemyr

This chapter shows how to set up a LAMP stack on Ubuntu, suitable for a website or multiple vhosts, all with a suitable environment for running the Drupal or Grav.

Table of contents

Introduction

This chapter assumes that you've already set up an Ubuntu instance on either a physical server, or a virtual cloud server (e.g. a DigitalOcean droplet). Inital set up of an instance is described in separate chapters:

shows how to set up a LAMP stack on Ubuntu 16.04 LTS, 18.04 LTS and 20.04 LTS, suitable for a website or multiple vhosts, all with a suitable environment for running the Drupal WCMS or the Grav WCMS.

[Mumble]

CLI access to the web server

To learn about how to gain access to the web server's command line interface (CLI). see the section in “Unix notes” about using a terminal emulator.

Check for upgrades

After logging in to the CLI it is recommended that you check that the release you are running is up to date.

You do this by first updating the package list, look through the list of packages that can be upgraded, and then upgrade all packages. You do this with the following sequence of commands:

$ sudo apt update
[…]
$ sudo apt list --upgradable
[…]
$ sudo apt upgrade
[…]

To make your upgrade effective, you need to reboot:

$ sudo shutdown -r now

You may have to redo this from time to time to keep your configuration current.

noteThe command apt update updates the list of available packages and their versions, but it does not install or upgrade any packages. After updating the lists, the package manager knows about available updates for your configuration. The command apt list --upgradable will list these packages. The command apt upgrade will perform all pending non-distruptive upgrades. However, it will not perform upgrades that requires installing, replacing and/or removing other packages. Instead, it will say that those upgrades are being “kept back”. To force those upgrades, you can use the command apt full-upgrade (previous: apt-get dist-upgrade). This command will perform all pending upgrades and their dependencies. I.e. it will install, replace and remove packages as required to force the upgrade. This can be quite dangerous, as such changes may break backwards compatility and change your system's behaviour. In worst case, doing a full upgrade could leave you with a broken system.

Removing packages

To remove a package that is installed globally, that you no longer neeed, and purge its configuration files, you may use these commands:

$ sudo apt remove package
$ sudo apt --purge remove package
$ sudo apt autoremove  package
$ sudo apt --purge autoremove package

The first pair of commands will remove the package and purge its configuration files if it was directly installed with apt. The second pair of commands will do the same if it was installed as an dependency, and is no longer needed.

Source: AskUbuntu.

Install and enable Apache

Make sure you always install Apache before PHP.

$ sudo apt update
$ sudo apt install apache2
…
$ apache2 -v
Server version: Apache/2.4.54 (Ubuntu)
Server built:   2022-07-21T19:38:00

At the end of the installation process, Ubuntu starts Apache. The web server should already be up and running.

We can check with the systemd init system to make sure the service is running by typing:

$ service apache2 status
apache2.service - The Apache HTTP Server
     Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: e…)
     Active: active (running) since Fri 2020-05-01 13:47:40 UTC; 2min 49s ago
       Docs: https://httpd.apache.org/docs/2.4/
   Main PID: 1773 (apache2)
      Tasks: 55 (limit: 2345)
     Memory: 5.2M
     CGroup: /system.slice/apache2.service
             ├─1773 /usr/sbin/apache2 -k start
             ├─1775 /usr/sbin/apache2 -k start
             └─1776 /usr/sbin/apache2 -k start
…

Check that the configuration is OK. Just after installing Apacahe2 you may get the following::

$ sudo apache2ctl configtest
 AH00558: apache2: Could not reliably determine the server's fully qualified domain \
 name, using 127.0.1.1. Set the 'ServerName' directive globally to suppress this \
 message

To fix it put the following line in “ServerName localhost” in /etc/apache2/apache2.conf, near the end, like this:

# Include generic snippets of statements
IncludeOptional conf-enabled/*.conf

ServerName localhost

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

Rerun the configtest. You should get this:

$ sudo apache2ctl configtest
Syntax OK

To see the version of Apache the server is running, use one the following commands. The second one also provides compile info.

$ apache2ctl -v
Server version: Apache/2.4.41 (Ubuntu)
Server built: 2020-04-13T17:19:17
$ sudo apache2ctl -V
Server version: Apache/2.4.41 (Ubuntu)
Server built:   2020-04-13T17:19:17
Server's Module Magic Number: 20120211:88
Server loaded:  APR 1.6.5, APR-UTIL 1.6.1
Compiled using: APR 1.6.5, APR-UTIL 1.6.1
Architecture:   64-bit
Server MPM:     event
  threaded:     yes (fixed thread count)
    forked:     yes (variable process count)
Server compiled with....
 -D APR_HAS_SENDFILE
 -D APR_HAS_MMAP
 -D APR_HAVE_IPV6 (IPv4-mapped addresses enabled)
 -D APR_USE_SYSVSEM_SERIALIZE
 -D APR_USE_PTHREAD_SERIALIZE
 -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT
 -D APR_HAS_OTHER_CHILD
 -D AP_HAVE_RELIABLE_PIPED_LOGS
 -D DYNAMIC_MODULE_LIMIT=256
 -D HTTPD_ROOT="/etc/apache2"
 -D SUEXEC_BIN="/usr/lib/apache2/suexec"
 -D DEFAULT_PIDLOG="/var/run/apache2.pid"
 -D DEFAULT_SCOREBOARD="logs/apache_runtime_status"
 -D DEFAULT_ERRORLOG="logs/error_log"
 -D AP_TYPES_CONFIG_FILE="mime.types"
 -D SERVER_CONFIG_FILE="apache2.conf"

To test your web server, type its domain name in a browser's address field.

If you haven't cobfigured the DNS yet, first get its public IP-address from the CLI. Examples:

$ curl ifconfig.me
192.0.2.0.12 …
$ hostname -I
192.0.2.0.12 …

You can convert the IP-address to a URL (e.g. http://192.0.2.0.12) and use it in the address field of your web browser.

If your web server is working, you should see the Apache2 Default Page and the text: "It works!"

Manage Apache

Prior to introduction of systemctl in Ubuntu 16, service was a wrapper for a number of scripts to start, stop and examine various services. Now service is a wrapper for systemctl as well. For good measure, there is also apache2ctl command that interacts with the Apache server using the CLI. It works for some (but not all) these contexts. In some contexts, it is the only command that works, in other contexts of works differently.

Below, all these are shown, but the systemctl version will only work on Ubuntu 16 and later.

To display the status of your web server, as perceived by the service manager, you can type:

$ systemctl status apache2
$ service apache2 status

Alternatvely, you can check status using apache2ctl:

$ apache2ctl status
$ apache2ctl fullstatus

To stop your web server, you can type:

$ sudo systemctl stop apache2
$ sudo service apache2 stop
$ sudo apache2ctl stop

To start the web server when it is stopped, type:

$ sudo systemctl start apache2
$ sudo service apache2 start
$ sudo apache2ctl start

To stop and then start the service again, type:

$ sudo systemctl restart apache2
$ sudo service apache2 restart
$ sudo apache2ctl restart

If you are simply making configuration changes, Apache can often reload without dropping connections. To do this, you can use this command for a graceful reload:

$ sudo systemctl reload apache2
$ sudo service apache2 reload
$ sudo apache2ctl graceful

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

$ sudo systemctl disable apache2
$ sudo service apache2 disable

To re-enable the service to start up at boot, you can use this command:

$ sudo systemctl enable apache2
$ sudo service apache2 enable

Check configuration.

$ sudo apache2ctl configtest

Enable site example.com.

$ sudo a2ensite example.com

Disable site example.com.

$ sudo a2dissite example.com

Enable Apache mod_rewrite

You can inspect /etc/apache2/mods-enabled/ to see what modules are enabled.

In order to have clean URLs with Drupal, it is necessary to enable an extension to the Apache web-server known as mod_rewrite. You do this with the following pair of shell commands:

$ sudo a2enmod rewrite
$ sudo systemctl restart apache2

You will also need to add the following directive for the directories where you want .htaccess to override the defaults:

AllowOverride All

See, for example, the section about phpMyAdmin access control.

Set up a virtual host

To be able to create directories on the web server without having to use sudo, it is recommended to change the ownership of the direcory /var/www. Example for a webmaster with userame "bob":

$ sudo chown bob.www-data /var/www

To set up a vhost with the domain name “example.com”, you need to change DNS for this domain to point to your web server. Set up DNS for aliases as well (e.g. “www.example.com”).

Then, on your web server, create a webroot for the domain.

$ mkdir -p /var/www/example.com/web

If necessary, change ownership and group for this directory to allow the development team to have write access to it.

If this is the first vhost on this web server, navigate to /etc/apache2/sites-available and copy the file named 000-default.conf to example.com.conf.

The copy will look like this (with the comments removed):

<VirtualHost *:80>
	ServerAdmin webmaster@localhost
	DocumentRoot /var/www/html
	ErrorLog ${APACHE_LOG_DIR}/error.log
	CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

Change the “ServerAdmin” directive to an email-address that the real site administrator can receive emails through.

After this, you need to add two directives. The first, called “ServerName”, establishes the base domain that should match for this vhost definition. This will most likely be your domain. The second, called “ServerAlias”, defines further names that should match as if they were the base name. This is useful for matching host aliases you defined, like “www”.

The only other thing we need to change for a basic vhost file is the location of the document root for this domain by setting the DocumentRoot directive to the correct path.

<VirtualHost *:80>
	ServerAdmin admin@example.com
	ServerName example.com
	ServerAlias www.example.com
	DocumentRoot /var/www/example.com/web
	ErrorLog ${APACHE_LOG_DIR}/error.log
	CustomLog ${APACHE_LOG_DIR}/access.log combined
 </VirtualHost>

So far, we have a basic vhost file that can be used for a basic website.

For Drupal, we also need to add a directory block and allow overrides. To not block any IP addresses use the directive “Require all granted”. (See Apache access control.)

Then, we need to protect some sensitive files in the Drupal or Grav root that may exist from being viewed by a web client. For instance, the file CHANGELOG.txt or CHANGELOG.md that is installed along with Drupal or Grav will reveal what version of the WCMS the site is running. An attacker will want to look at this file just to see if the version is vulnerable to certain exploits. A backup file is often saved when a file is edited. Some text editors (e.g. emacs make backup copies automatically, appending a ~ (tilde) to the end of the file name. Unless these files are protected, an attacker may probe for settings.php~ and learn your database credentials.

The modified vhost configuration file for Drupal should now look like this:

<VirtualHost *:80>
	ServerAdmin admin@example.com
	ServerName example.com
	ServerAlias www.example.com
	DocumentRoot /var/www/example.com/web
	ErrorLog ${APACHE_LOG_DIR}/error.log
	CustomLog ${APACHE_LOG_DIR}/access.log combined
        <Directory /var/www/example.com/html>
                Options FollowSymLinks
                AllowOverride All
                Require all granted
        </Directory>
        <Files ~ "CHANGELOG\.txt">
                Require all denied
        </Files>
        <Files ~ "~">
                Require all denied
        </Files>
 </VirtualHost>

Enable the vhost configuration file:

$ sudo a2ensite example.com.conf

Check that there are no errors in the newly enabled vhost configuration file:

$ sudo apache2ctl configtest

If there were no errors, you may reload the web server to make the new vhost visible on the web:

$ sudo systemctl reload apache2

Now that you have your first vhost configuration file established, you can create more by copying that file and adjusting it as needed.

Set up HSTS for a virtual host

HTTP Strict Transport Security (HSTS) is a security feature that lets a website tell browsers that it should only be communicated with using HTTPS, instead of using HTTP.

Note that if a certificate fails for a domain or sub-domain, the website will no longer be viewable.

To set up, first make sure that mod_headers is enabled. If it is not symlinked in /etc/apache2/mods-enabled, do:

$ sudo a2enmod headers

Then put the following three lines in the le-sll.conf-file for the virtual host:

<VirtualHost *:443>
  Header always set Strict-Transport-Security \
    max-age="63072000; includeSubdomains; preload"
</VirtualHost>

Finally, test and reaload Apache:

$ sudo apache2ctl configtest
$ sudo systemctl reload apache2

Install MySQL

Now that the web server up and running, it is time to install MySQL. It is a free database management system. Basically, it will organize and provide access to databases where our site can store information.

$ sudo apt update
$ sudo apt install mysql-server

During the installation, you may be asked to select and confirm a password for the MySQL root user. This is an administrative account in MySQL that has increased privileges. Make sure you pick a strong one.

After installing you should always use the following CLI command:

$ sudo mysql_secure_installation

noteWith Ububtu 22.04 LTS, an error will occur when you run the mysql_secure_installation script. See setup instructions on DigitalOcean for the steps to get around this.

This will let you set up a password validator, change the root passeword, also give you the option of removing the test database and the anonymous database user (created by default).

After setting up, check that your are able to login to the MySQL monitor as “root”:

$ sudo mysql -e "SELECT CURRENT_USER"
+----------------+
| CURRENT_USER   |
+----------------+
| root@localhost |
+----------------+

noteIf you are denied access a get the following error (ERROR 1698 (28000): Access denied for user 'root'@'localhost'), you either typed the wrong password, or you forgot “sudo”.

The configuration file is usully located at /etc/my.cnf or /etc/mysql/my.cnf.

To run Drupal, it is recommented that you alter the MySQL transaction isolation level:

mysql> SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
Source DO: Setting the MySQL transaction isolation level.

There is no need to restart MySQL after having installed it, but remember that for any changes made to the configuration to have effect you need to restart MySQL.

Some commands for the mysql service:

$ service mysql status
$ sudo /etc/init.d/mysql restart
$ sudo systemctl restart mysql

Install PHP

PHP is the component of our setup that will process code to display dynamic content. It can run scripts, connect to the database to get information, and hand the processed content over to the web server to display.

Follow instructions from DigitalOcean.

Ubuntu 22.04 LTS:

$ sudo apt install php
$ sudo apt install libapache2-mod-php
$ sudo apt install php-mysql
$ sudo apt install php-xml
$ sudo apt install php-gd
$ sudo apt install php-mbstring
$ php -v
PHP 8.1.7-1ubuntu3.1 (cli) (built: Nov  2 2022 13:39:03) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.7, Copyright (c) Zend Technologies
    with Zend OPcache v8.1.7-1ubuntu3.1, Copyright (c), by Zend Technologies

Ubuntu 20.04 LTS:

$ sudo apt install php
$ php -v
PHP 7.4.3 (cli) (built: Mar 26 2020 20:24:23) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.3, Copyright (c), by Zend Technologies

As for PHP extensions needed by Drupal core, they are defined in the core's composer.json-file –see for example the file for Drupal 8.8.x. Look at the “require” section and the keys starting with “ext-”. If Druapl is installed with composer they will be installed as well.

PHP APCu

Since PHP version 5.5, a data caching module named “APCu” has been available. Unlike its predecessor (named “APC”) it is designed to be used together with “OpCache” (opcode cache).

The following sequence of commands installs and enables it:

$ sudo apt install php-apcu
$ sudo phpenmod apcu
$ sudo systemctl restart apache2

PHP curl

You may get the following warning:

"Composer is operating significantly slower than normal because you do not have the PHP curl extension enabled."

To fix it install PHP curl:

$ sudo apt install php-curl

No restart is necessary.

PHP library for uploadprogress

The Drupal status page will display this notice:

“Upload progress   Not enabled
Your server is capable of displaying file upload progress, but does not have the required libraries. It is recommended to install the PECL uploadprogress library.”

As of June 2021, there is still an active core issue @ Drupal.org discussing the merits of the above warning. My take on this that there is no trivial way to install this library, and it is not really needed. Don't waste time trying to install this library (at least not until clear documentation about how to install it exists).

Managing PHP modules

The configuration files for all installed PHP modules are available under the /etc/php/PHP_VERSION/mods-available directory. You can see the number of files with extension .ini. The php-common package provides followings commands to manage PHP modules: phpenmod, phpdismod, phpquery.

You can define version using -v to enable/disable module for that version only.

To locate the PHP initialisation file, either examine the output from phpinfo() in a browser. This tells you the version loaded by the web server. In the CLI, you can use:

$ php -i | fgrep 'Loaded Configuration'
Loaded Configuration File => /etc/php/8.2/cli/php.ini

The name of the directory after the PHP version number indicates the SAPI (Server API) . Several types of SAPI exist. Those available are in the directory /etc/php/PHP_VERSION. Those that comes with Ububtu 18.04 LTS and 20.04 LTS by default are: apache2, cli. You can use the CLI-option -s to enable/disable a module for that SAPI only.

To disable the module mbstring:

$ sudo phpdismod mbstring

To enable the module mbstring for PHP version 8.2 and SAPI apache2:

$ sudo phpenmod -v 8.2 -s apache2 mbstring

You need to restart apache2 to make enabling or disabling effective:

$ sudo systemctl restart apache2

To view status of the module mbstring for PHP 8.2 and the SAPI apache2:

$ phpquery -v 8.2 -s apache2 -m mbstring
mbstring (Enabled for apache2 by the maintainer script)

Note that both version and SAPI are required arguments to phpquery.

Set up Apache to prefer PHP files

By default Apache will look first for a file named index.html in a requested directory. To tell our web server to prefer index.php, modify /etc/apache2/mods-available/dir.conf as follows:

<IfModule mod_dir.c>
    DirectoryIndex index.php index.html index.cgi index.pl index.xhtml index.htm
</IfModule>

Restart apache2 to make the change effective:

$ sudo systemctl restart apache2

Changing the PHP version

The following instructions are for changing from PHP 8.1 to PHP 8.2 on Ubuntu version 22.04 LTS + Apache2:

$ sudo a2dismod php8.1
$ sudo a2enmod php8.2
$ sudo service apache2 restart

See alsoNote that only one PHP version can be set up for Apache2 on a single vhost. The recipe above is based on: ioecapsule.com: How To Switch Between PHP Version In Ubuntu. The best practice for being able to switch between different PHP version is to set up a Docker based development environment. See: DO: Docker-based solutions overview.

Using ppa:ondrej/php

Before you start, make sure ensure all packages are updated:

$ sudo apt update
[…]
$ sudo apt upgrade
[…]
$ sudo shutdown -r now
To determine the current version of the default PHP package, do:

$ ls -l /etc/php
total 12
drwxr-xr-x 5 root root 4096 Jun 18  2020 7.4
drwxr-xr-x 6 root root 4096 Aug 28  2022 8.1
drwxr-xr-x 6 root root 4096 Jan  5  2023 8.2

note Ubuntu 22.04 LTS ships with PHP versions 7.4, 8.1, 8.2 (with version 8.1 being the default). There is no longer a need to use a PPA to install one of these.

If version you want is not yet available, install the ondrej PPA. This archive, maintaned by Ondřej Surý, provides access to all recent versions of PHP.

$ sudo add-apt-repository ppa:ondrej/php
[…]
$ sudo apt update
[…]

Installing additional PHP modules

After changing version, some PHP-modules may be missing from the default configuration. Check if any of the following are missing and install them if they are.

$ sudo apt install php8.1-xml
…
$ sudo apt install php8.1-gd
…
$ sudo apt install php8.1-mysql
…
$ sudo apt install php8.1-mysqli
…
$ sudo apt install php8.1-mbstring
…
$ sudo service apache2 restart

You may even have to enable them for Apache2:

$ sudo phpenmod -v 8.1 -s apache2 xml
$ sudo phpenmod -v 8.1 -s apache2 gd
$ sudo phpenmod -v 8.1 -s apache2 mysql
$ sudo phpenmod -v 8.1 -s apache2 mysqli
$ sudo phpenmod -v 8.1 -s apache2 mbstring

Install and Configure PHP-FPM

We now have PHP 8 as the default PHP package for the CLI. To make it work with Apache2, we need install and Configure PHP-FPM

The PHP FastCGI Process Manager (FPM) package enhances web server performance. It accelerates page generation, reduces memory consumption, and increases web server capacity. A special Apache module for FastCGI is required to use FPM.

Install the php-fpm package:

$ sudo apt install php-fpm

Install the associated Apache module.

$ sudo apt install libapache2-mod-fcgid

Confirm the php8.1-fpm service is running.

$ sudo systemctl status php8.1-fpm
 php8.1-fpm.service - The PHP 8.1 FastCGI Process Manager
     Loaded: loaded (/lib/systemd/system/php8.1-fpm.service; enabled; …
     Active: active (running) since Sun 2022-08-28 15:32:37 UTC; 1min 30s ago
       Docs: man:php-fpm8.1(8)
   Main PID: 739 (php-fpm8.1)
[…]

Enable all the Apache modules required by FPM.

$ sudo a2enmod actions fcgid alias proxy_fcgi

If you have configured a virtual host for your domain, add an FPM handler to the site's .conf file. Otherwise, add the handler to the default 000-default.conf file. If the site is using TLS, remember to do this to the prt 433 .conf as well.

<VirtualHost *:80>
[…]
	<FilesMatch \.php$>:
	        SetHandler proxy:unix:/var/run/php/php8.1-fpm.sock|fcgi://localhost"
	</FilesMatch>
[…]
</VirtualHost>

noteYou need to add an FPM handler for every virtual host before restaring Apache. If you for get this step Apache will no longer work for the site.

Finally, restart the Apache service:

$ sudo systemctl restart apache2

Check that all websites are operational.

Source Linode: How to Install PHP 8 for Apache and NGINX on Ubuntu.

See also DigitalOcean: How To Install PHP 8.1 and Set Up a Local Development Environment on Ubuntu 22.04.

Install phpMyAdmin

See if there is a version already installed:

The PHP program phpMyAdmin is an open source tool that lets a database administrator interact with a MySQL or MariaDB database using of a standard web browser. It supports a wide range of SQL operations, including data base management, inspecting and altering tables, fields, relations, indexes, users, permissions, etc. It also lets you directly execute any SQL statement.

$ dpkg -l phpmyadmin
…
ii  phpmyadmin     4:4.9.5+dfsg1-2 all          MySQL web administration tool

To install phpMyAdmin, do the following:

$ sudo apt update
$ sudo apt install phpmyadmin

When the install script requests a server for phpmyadmin, select apache2.

noteWhen the prompt appears, apache2 is highlighted, but not selected. If you do not hit <Space> to select Apache2, the installer will not move the necessary files during installation. Hit <Space>, <Tab>, and then <Enter> to select Apache2.

Select yes when asked whether to use dbconfig-common to set up the phpmyadmin database (its dbuser is also phpmyadmin). Make sure you pick a secure password. The credentials for this user is in config-db.php in /etc/phpmyadmin (the phpMyAdmin configuration directory). The code is in /usr/share/phpmyadmin.

You should also make sure that the following extensions to Apache are enabled if available:

They live in the directory /etc/php/X.Y/mods_available, where X.Y is the PHP version number (7.0 for Ubuntu 16.04 LTS, 7.2 for Ubuntu 18.04 LTS, 7.4 for Ubuntu 20.04 LTS), 8.1 for Ubuntu 22.04 LTS).

To see if it is enabled for the web server, inspect the output of phpinfo() that is linked from the Drupal status report, or use phpquery from the CLI.

Install those that are are available, but not enabled.

See Managing PHP modules above to learn how to use phpquery aend phpenmod.

noteSince phpMyAdmin provides direct access to the database, along with powerful commands to manipulate its content, it may also be a security risk. An out of date phpMyAdmin installation may contain well-known security vulnerabilities. As with all management software that can be accessed from the web, it is important to keep the installation current with all security releases. You should not install phpMyAdmin on a production site, and you should restrict access to the IP-addresses belonging to PCes in your organization on a staging site.

You also need to add the configuration file for phpMyAdmin to your Apache configuration. Edit /etc/apache2/apache2.conf and add the following line to the end of the file:

Include /etc/phpmyadmin/apache.conf

You need to restart apache2 to make enabling or disabling effective:

After restarting Apache, you should be able to access the phpMyAdmin by visiting the URL /phpmyadmin.

It is not good idea to use this URL. Since it is the default, it is heavily targeted by bots and hackers.

This URL is set up in /etc/phpmyadmin/apache.conf, where there by default there is a line that looks like this:

Alias /phpmyadmin /usr/share/phpmyadmin

This line sets up an alias that says that phpMyAdmin is found by typing our site's domain name (or IP address), followed by /phpmyadmin. to change it to a more obscure alias, remove or comment out the existing alias assignment and add your own. For example:

# Alias /phpmyadmin /usr/share/phpmyadmin
Alias /nothingtoseehere /usr/share/phpMyAdmin

The alias should be easy to remember, but not easy to guess. It shouldn't indicate the purpose of the URL location. In the example, above /nothingtoseehere is used.

Save the edited file, and restart Apache.

For defence in depth, you may also want to implement access control to the phpMyAdmin-directory. In order to do this, you need to enable overrides: Edit the cofiguration file, and add the directive in bold within the <Directory /usr/share/phpmyadmin>-section of the configuration file:

<Directory /usr/share/phpmyadmin>
  Options FollowSymLinks
  DirectoryIndex index.php
  AllowOverride All
Afterwards, you'll need to restart Apache for your changes to be recognized:
$ sudo systemctl restart apache2

In the directory /usr/share/phpmyadmin, create a file named .htaccess:

$ touch .htaccess

The .htaccess-file should contain one or more lines the one below. It require anyone that access the directory to have at least one of IPs listed. The IP-address used is just an example. You must use the actual IP-addresses of the PCs that you and other developers use to access the web server.

Require ip 192.0.2.0.12

To learn the public IP-adress of the PC your browser runs on, visit WhatIsMyIPAddress.com.

The default configuration (dbconfig-common) gives phpMyAdmin access to the database server running on localhost. You may add additional database servers to the server section of config.inc.php to access remote database servers using phpMyAdmin. E.g.:

$i++;
$cfg['Servers'][$i]['host'] = 'example.com';
$i++;
$cfg['Servers'][$i]['host'] = '192.0.2.0.12';

You can now access the web interface of phpMyAdminby by visiting your server's domain name or public IP address followed by /phpmyadmin (e.g. https://example.org/phpmyadmin). You will see a log in prompt (unless you are using a PC whose IP is blocked by .htaccess).

To autheticate users and to grant access to the database, phpMyAdmin allows four different authentication methods:

  1. cookie – Prompts for MySQL credentials using its own authentication scheme (default if using dbconfig-common).
  2. http – Prompts for MySQL credentials using HTTP basic authentication.
  3. signon – Uses an external (SSO) application for authentication via a prepared PHP script.
  4. config – MySQL username and password stored in clear text in the configuration file.

The config method should only be used if the if the server running phpMyAdmin is placed behind a firewall in a secure environment, or some other secure authentication is used to limit access. Otherwise, it is not only dangerous because the MySQL username and password stored in clear text, but also because it does not password-protect phpMyAdmin or the database. Anyone who can access the correct URL is logged directly in and can manipulate the database.

noteFor newer distributions, the default security nodel requires sudo for dbuser root to log in (i.e. phpMyAdmin will not be able to log in as root). Workarounds are discussed on StackOverflow, but it is safer to keep the default security model.

tipOn Ubuntu 16.04, phpMyAdmin outputs a lot of “Deprecation Notices”. The cure is described in this answer on AskUbuntu. On Ubuntu 18.04, phpMyAdmin complains about an error in sql.lib.php. The cure is described in this answer on StackOverflow. Both patches are trivial, and should not have any side-effects. If the library is automatically upgraded (without the bug fixed), the library must be patched again.

For information about using phpMyadmin, see the chapter about tools for developers.

See alsoFor more information about phpMyAdmin, including official documentation, user maintained wiki pages and third party tutorials, see phpMyAdmin.net. This site also functions as a portal to various support channels, such as the help forum and mailing lists. Click on the “Support” tab to see an overview of support channels.

Login cookie validity

On Ubuntu. the default login cookie validity for phpMyAdmin is annoyingly short 1440 seconds (24 minutes).

According to several support forums, this should be fixed by having something like this in /etc/phpmyadmin/config.inc.php:

$sessionValidity = 3600 * 4; // 4 hours
$cfg['LoginCookieValidity'] = $sessionValidity;
ini_set('session.gc_maxlifetime', $sessionValidity);

Did this on 2020-08-15. Don't know if it helps.

Some support forum links:

noteThere used to be a way to set this for the current session through the GUI. This feature was removed in version 4.8.0.

Install miscellaneous package managers

The package mangers listed below are not essential, and you may delay installing them until you need a library they manange.

Check whether npm, a package manager for JavaScript (in particular Node.js) libraries is already installed:

$ type npm
-bash: type: npm: not found

If it is not installed, then install it (otherwise, skip this step):

To install npm, use apt, the Ubuntu package manager:

$ sudo apt install npm

The Ubunti package manager provides a slightly dated version, and for some reason, reinstallations often fails: It looks like it installs, but the command is not found. An altermative is to first install nvm, and then use that to install the latest version of node:

$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash
# Fire up a new terminal window to get it on the $PATH.
$ nvm install node

This will install it under your home directory.

Source linuxize.com: How to Install Node.js and npm on Ubuntu 20.04.

Check version, and update. The commands are:

$ npm -v
7.17.1
$ sudo npm update
$ sudo npm install -g npm@latest
$ sudo npm install -g npm

The commands npx and node is also installed:

$ npx -v
6.14.4
$ node -v
v10.19.0

In addition to npm, there is n, which users of npm is encouraged to install. It also let you update node. To install latest stable version of npm.

$ sudo npm cache clean -f
$ sudo npm install -g n
$ sudo n stable

Yet another package manager is bower. Check if it is already installed:

$ type bower
-bash: type: bower: not found

If it is not installed, then install it (otherwise, skip this step):

To install bower, use npm:

$ sudo npm install -g bower
$ bower -v
1.8.0
$ bower update

Pip is a software tool that helps you install various Python packages on your system.

Before you start, make sure that apt is up to date and upgrade if it is not:

$ sudo apt update
…
$ sudo apt upgrade

To install it, you must have universe repository enabled. Otherwise the installation commands will throw the following error: “E: Unable to locate package python-pip”. To examine what repositoroes are enabled you can search the file: /etc/apt/sources.list. Example:

$ grep ^deb.*universe$ /etc/apt/sources.list
deb http://mirrors.digitalocean.com/ubuntu/ focal universe
deb http://mirrors.digitalocean.com/ubuntu/ focal-updates universe
deb http://security.ubuntu.com/ubuntu focal-security universe

It it is missing, add it with following command:

$ sudo add-apt-repository universe

To install pip or pip3 do the following:

$ sudo apt install python-pip  # Python 2 (Ububtu 18.04 LTS only)
$ sudo apt install python3-pip # Python 3

Note that pip for Python 2 is not included in the Ubuntu 20.04 LTS repositories.

Check version:

$ pip3 --version
pip 20.0.2 from /usr/lib/python3/dist-packages/pip (python 3.8)

Install additional software globally

You don't need to install all the packages listed below when you first set up a web server. Most of these are development requirements and you may defer installation until you need the package. They are simply put together in this section for easy reference.

Some of the tools described in this section may already be installed as part of the standard distribution you're using. Always check if the software is already installed before trying to install.

Install browser-sync

Browser-Sync is an automation tool that is typically used to make tweaking and testing faster by synchronizing file changes and interactions across many devices.

$ sudo npm install -g browser-sync
…
$ browser-sync --version
2.26.12

However, if you run browser-sync on a typical web server (i.e. a headless environment) it never completes, but hangs there. Please see: this Drupal.org issue and this question on AskUbuntu.com for a possible solution (not yet).

Install gcc

To install the Gnu c-compiler, install a metapackage called build-essential. This installs the compiler and some related/necessary packages.

$ sudo apt install build-essential

Install git

Git is a distributed version control system that comes pre-installed with Ubuntu 22.04 LTS. Do this check if git is already installed, do this:

$ git --version
git version 2.37.2

If it is not installed, then install it by means of apt, the default Ubuntu package manager:

$ sudo apt install git

Install gulp

Gulp is a build tool similar to Unix make. It is used for automation of time-consuming and repetitive tasks involved in web development like CSS pre-processing, minification, concatenation, unit testing, linting, optimization, etc. It is built on node and is written in Javascript.

It requires two files: package.json (lists the various plugins for gulp), and gulpfile.js (script with the logic to run the tasks).

To install gulp using npm, do:

$ sudo npm install --global gulp-cli
…
$ gulp --version
CLI version: 2.3.0
Local version: 4.0.2

It can also be installed using Ubuntu's standard package manager:

$ sudo apt install gulp
…
$ gulp --version
CLI version: 2.3.0
Local version: 4.0.2

Install CSS pre-processors

If you plan to do any theming, you will probably need a CSS pre-processor.

A pre-processor brings nested rules, variables, mixins, selector inheritance, and more to CSS. Compiling stylesheets written in either less (not an acronym) or sass (Syntactically Awesome StyleSheets) generates standard CSS and makes stylesheets easier to organize and maintain.

A common tools for working with less are lessc. It is a compiler (written in JavaScript) for compiling less stylesheets.

The latest stable version can be installed by means of the npm (a package manager for Node.js libraries):

$ sudo npm install -g less
…
$ sudo lessc --version
lessc 2.7.2 (Less Compiler) [JavaScript]

If you already have less installed using npm, but want to update to the latest version, do this:

$ sudo npm update -g less
$ sudo lessc --version
lessc 4.2.0 (Less Compiler) [JavaScript]

Examples of how to use it:

$ lessc example.less > compiled_example.css

Another useful tool for working with stylesheets is yui-compressor. It is a stylesheet optimizer and compressor,

$ sudo apt install yui-compressor

Examples of how to use it:

$ yui-compressor -o small_and_compiled_example.css compiled_example.css

To work with the theme, you need to install sass.

If you plan to do theme development using the Bootstrap5 theme, you will need sass, a Ruby-based CSS preprocessor. To install Ruby Sass globally , do this:

$ sudo apt install ruby-sass
…
$ sass --version
Ruby Sass 3.7.4

There exists many other SASS preprocessors that can be used as alternatives.

Install ExifTool

$ type exiftool
-bash: type: exiftool: not found

If it is not installed, then install it (otherwise, skip this step):

$ sudo apt install libimage-exiftool-perl

Links: ExifTool home, ExifTool notes.

Install Unison

Unison is a file-synchronization tool developend by Benjamin C. Pierce that exists for OS/X, Unix, and Windows. It allows two replicas of a collection of files and directories to be stored on different hosts (or different directories on the same host), modified separately, and then brought up to date by propagating the changes in each replica to the other.

To install Unison on Ububtu up to, and including Ubuntu 18.04 LTS:

$ sudo apt install unison-all
$ unison -version
unison version 2.48.4

noteThe above works on all supported versions of Ubuntu except Ubuntu 20.04 LTS and later. However Unison is extremely picky, not only about the version number of the tool itself, but also the OCaml compiler used to compile it. It looks like the team that put together the binaries recompiled with a newer version of the OCaml compiler than what was previously used, without thinking about backwards and cross-platform compatibility is the main feature of syncing tool. IMHO, that's a major goof-up.

Until this is fixed on Ubuntu 20.04 LTS (and later), you need to first purge Unison via apt (if you've installed it). Then reinstall it “by hand” by using the static file unison-2.48.4-linux-i386-text-static.tar.gz that can be downloaded from: Urs Müller's repo (looks like the dynamic version works as well). If you already have the binary 2.48.4 somewhere, you can copy that instead.

See alsoThis is Ubuntu bug #1568459 and Ubuntu bug #1875475. For more information, see AskUbuntu, GitHub and LaunchPad 1568459, LaunchPad 1875475.

Install Webmin/Virtualmin

Webmin with Virtualmin based free replacement for cPanel/Plesk. I have not tested it yet, just recorded some helpful links:

Install xterm

$ sudo apt install xterm

Install zip

$ sudo apt install zip

Install composer

To use composer, zip and unzip must be installed, so make sure it is:

$ unzip -v
UnZip 6.00 of 20 April 2009, by Debian. Original by Info-ZIP.
…

The dependecy manager composer can be used to download Drupal, Drupal contributed projects (modules, themes, etc.), and all of their respective dependencies. Before installing it, check if it is already installed:

$ type composer
-bash: type: composer: not found

If it is not installed, then install it (otherwise, skip this step).

On Ubuntu 18.04 LTS and later, composer can be installed from the official repo with the apt tool. For example, on Ubuntu 22.04 LTS:

$ sudo apt install composer
…
$ composer -V
Composer 2.4.1 2022-08-20 11:33:50
$ type composer
composer is hashed (/usr/bin/composer)
$ ls -l /usr/bin/composer
-rwxr-xr-x 1 root root 2393 Aug 21 07:54 /usr/bin/composer

Installing from the Ubuntu 18.04 LTS repo resulted in a too old version of composer (version 1.6.3, from 2018) being installed.

On Ubuntu 22.04 LTS, using apt to install from the Ubuntu repo installs version 2.4.1 (2022-08-20). This is more current, and may still be usable. However, this version lacks the selfupdate command. The latest version (with selfupdate) is – at the time of writing – version 2.6.3 (2023-09-19).

To install the most recent version of composer (recommended for Ubuntu 20.04 LTS and earlier), use curl to download and run the installer as follows:

$ curl -sS https://getcomposer.org/installer | php
…
# Use it: php composer.phar
$ sudo mv composer.phar /usr/local/bin/composer

See alsoDO: Setting Up Composer for Dependency Management.

Provided the selfupdate command is available, the following forces install of the most recent version of the ver. 1 branch and ver. 2 branch respictively:

$ composer selfupdate --1
$ composer selfupdate --2

See alsoRead more at Drupal.org: Using composer with Drupal, Getcomposer.org: Composer: Getting started, Getcomposer.org: Is it safe to run Composer as root? and SO.com: How to downgrade or install a specific version of Composer?.

Install webpack

Webpack is a static module bundler that is part of the Node.js ecosystem. It is not recommended to install it globally.

Installation is described in a separate chapter.

Test your configuration

After setting up, you should run some simple tests to verify that everything works as it should. Here are some suggestions.

To test that the Apache HTTP server is operating correctly, just point your browser to the domain you've created.

If Apache is working, and you've not yet created any content, you'll get some default page (exactly how it looks like depends on how the web server is configured).

Creating a file /var/www/html/index.php with the following content will let you test PHP:

<html>
<head><title>PHP test</title></head>
<body>
<h1>Hello, world!</h1>
<?php
phpinfo();
?>
</body>
</html>

If you again point your browser to the domain you've been allocated after creating and installing this file in the webroot, and the browser shows a page with a long table with detailed information about the PHP configuration, both Apache and PHP works.

User administration

In Ubuntu, there are two command-line tools that you can use to create a new user account: useradd and adduser.

The command useradd is a low-level utility for adding users. It will create the user and corresponding group. It will not set a password and not create the user' home directory.

The adduser a interactive frontend to low-level command written in Perl.

Here is an example of the latter:

$ sudo adduser username
Adding user `username' ...
Adding new group `username' (1002) ...
Adding new user `username' (1002) with group `username' ...
Creating home directory `/home/username' ...
Copying files from `/etc/skel' ...

You will then be asked a series of questions. The password is required, and all other fields are optional.

New password: 
Retype new password: 
passwd: password updated successfully
Changing the user information for username
Enter the new value, or press ENTER for the default
	Full Name []: 
	Room Number []: 
	Work Phone []: 
	Home Phone []: 
	Other []: 
Is the information correct? [Y/n] 

This command will create the new user's home directory, and copy the relevant files from /etc/skel directory to the user's home directory, and set permissions to let user write, edit, and delete files and directories in the home directory.

To delete the user, without removing the user files, run:

$ sudo deluser username

If you want to delete the user and its home directory and mail spool, use the --remove-home option:

$ sudo deluser --remove-home username

To add a user to an existing group:

$ sudo usermod -a -G groupname username

On Ubuntu, members of the group sudo are granted sudo access. To do this to a new user:

$ sudo usermod -a -G sudo username

The -a (append) option is essential. Otherwise, the user will be removed from any groups, not in the list.

The -G option takes a (comma-separated) list of additional groups to assign the user to.

To delete a user from a group:

$ sudo deluser username groupname

There are alternatives, but this is the safe way to do it.

noteYou may also edit the system file group file manually, but if you make a typo your system might become inaccessible.

Troubleshooting

Below the most common problems that may show up on a clean install are listed, whit some suggestions for how to resolve them.

WSOD

The first place to look, if you get the “white screen of death”, is the watchdog logs:

$ drush watchdog-show
$ drush ws

To truncate the log, use the following:

$ drush wd-del all -y

The next place to lok is in the Apache error logs.

The location of the Apache logs for Ubuntu is set by ${APACHE_LOG_DIR} (defined in /etc/apache2/envvars), but the default location is the directory /var/log/apache2/.

The following will display Apache errors as they appear:

$ sudo tail -f /var/log/apache2/error.log

Cannot allocate memory

Sometimes, when a PHP operation fails because it is unable to allocate more memory, you may also experience that trivial CLI-commands results in a “Cannot allocate memory” error. Example:

$ whoami
-bash: fork: Cannot allocate memory
$ free
-bash: fork: Cannot allocate memory

This happens when the computer runs out of memory. This can be for a number of reasons, but basically, some process is eating up all the memory and not leaving any left for even basic command usage.

The quick fix is to reboot, and the log on and then run top or htop. Keep an eye on the memory usage and see what process is using up all the memory.

Source: AskUbuntu.

Too many open files

The default number og allowed file descriptors in Ubuntu 20.04 is small:

$ ulimit -n
1024

While different limits may be configured for the web-server or database server, if the preset limit is exceeded, it throws the following exception:

PDOException: SQLSTATE[HY000]: General error: 1016 Can't open file:
'./database/semaphore.frm' (errno: 24 - Too many open files):
SELECT expire, value FROM {semaphore} WHERE name = :name; Array (
[:name] => variable_init ) in lock_may_be_available() (line 167 of
/var/www/example.org/html/includes/lock.inc).

The limit for the number of open file descriptors for a process can be set explicitly using the builtin bash ulimit -n command. Note that this only effects the current shell and any subshells.

The maximum number of file descriptors for the web server process is controlled in two ways:

  1. By setting the the value of APACHE_ULIMIT_MAX_FILES in /etc/apache2/envvars.
  2. By setting hard and soft limits for the web-server in/etc/security/limits.conf.

Both of these requires a reboot to take effect.

By setting the value of the environment variable APACHE_ULIMIT_MAX_FILES in /etc/apache2/envvars, you can set the limit to use when the web server process starts. For example, uncomment the following line to increase this from 8192 (the default) to 65536.

APACHE_ULIMIT_MAX_FILES='ulimit -n 65536'

To see the current soft and hard limits for the user www-data, run this as root:

$ su - www-data -c 'ulimit -Sn' -s '/bin/bash'
65536
$ su - www-data -c 'ulimit -Hn' -s '/bin/bash'
65536

Hard and soft limits for the databse (mysql) and the web-server (www-data) may be set in /etc/security/limits.conf:

# <domain>  <type>  <item>  </value>
mysql       hard    nofile  8192
mysql       soft    nofile  8192
www-data    hard    nofile  65536
www-data    soft    nofile  65536

To check the limits for open processes:

$ ps aux | fgrep www-data
www-data 12345 …
$ cat /proc/12345/limits | egrep "Max open files|Limit"
Limit           Soft limit  Hard limit  Units
Max open files  65536        65536      files
$ ps aux | fgrep mysql
mysql 23456 …
$ cat /proc/23456/limits | egrep "Max open files|Limit"
Limit           Soft limit  Hard limit  Units
Max open files  8192        8192        files

To change the limit for mysql, you also need to create a new override using this command:

$ systemctl edit mysql

This fires up your default editor and let you enter the following data:

[Service]
LimitNOFILE=8192

Edit /etc/mysql/my.cnf and add the following section:

[mysqld]
open_files_limit=50000

Now, reload the services:

$ systemctl daemon-reload
$ service mysql restart

Source: Werk.21.de.

The following two commands let you look at some data from the kernel.

$ cat /proc/sys/fs/file-nr
2272 0 65535
$ sudo lsof | wc -l
39946

The first command shows three integers. The first is supposed to be the number of the file handles allocated during the current session. The second is allocated but unused file handles. The last number shows the maximum number of files handles per process.

The second command shows the total number of file handles currently open for all processes.

The following commands may be used to identify the culprit (packaged as a script named lsoftop.sh @ bar):

$ sudo lsof > lsof.log
$ cat lsof.log | awk '{ print $1; }' | sort -rn | uniq -c | sort -rn | head -10
  35315 mysqld
   1952 apache2
    308 lxcfs
    224 sshd
    221 gmain
    171 systemd
    128 master
     95 gdbus
     67 unattende
     64 systemd-j

Source: Ask Ubuntu.

I am not yet sure what is required, but so far I've tried the following:

  1. Set a higher value in /etc/apache2/envvars.
  2. Increased soft and hard limits for www-data in /etc/security/limits.conf.
  3. Added the following line to /etc/pam.d/common-session:
    session required pam_limits.so.
  4. Added the following line to /etc/sysctl.conf:
    fs.file-max = 65535.
  5. Run sysctl -p. It repeats the previous.
  6. Changed the systemd configuration to have LimitNOFILE=8192.
  7. Increased open_files_limit for mysqld from 5000 to 50000 in my.cnf.

However, steps 1-5 did not fix the problem. The jury is out on the last two.

Background:

Full root-partition

First, check the situation and identify what uses the space under /var:

$ sudo df -h
Filesystem      Size  Used Avail Use% Mounted on
…
/dev/vda1        49G   49G    0K 100% /
…
$ sudo du -sh /var/* | sort -hr | head
18G     /var/lib
5.9G    /var/www
2.9G    /var/log
110M    /var/cache
96M     /var/backups
1M      /var/spool
972K    /var/private
152K    /var/mail
28K     /var/tmp

Here is the steps I currently take (expanded below):

  1. Clean out /var/cache/apt.
  2. Trim down MySQL binary logs.
  3. Delete mailbox contents.
  4. Check for websites for spam.

This list should be expanded.

To clean out /var/cache/apt, do:

$ sudo apt clean

To trim down MySQL binary logs do:

$ sudo mysql
mysql> SHOW BINARY LOGS;
mysql> PURGE BINARY LOGS TO 'binlog.000142';

To delete mailbox contents, start mutt and then Shift+D, followed by ~s .*. Press q to quit, qnd reply "Yes" to prompt about deleting.

If a website allows spammer to post, the database can become very big. You can find an outsized database by using the following set of commands:

$ sudo -i
# du -sh /var/lib/mysql | sort -hr | head
178G    db_spamsink
986M    db_bigone
298M    db_medium
233M    db_alpha
179M    db_beta
177M    db_gamma

The huge size of db_spamsink indicates that there is a loophole allowing spammers to post.

Sources:

If you cannot get enough free space by using the above steps, you may need to add disk space and/or block storage. How to this on DigitalOcean is described here.

Final word

[TBA]


Last update: 2022-12-10 [gh].