Guide v6.4 / Updated 2023-11-24 / First published 2023-09-24 / echo (at) screaming (dot) computer

This is an updated version of the original LAMP server VM in UnRAID setup guide.
New in this version: LTS release of Linux Mint; upgrade to PHP 8; switch from MySQL to MariaDB

Preface

Over the years while working on various projects, I've often wished for a fully-featured web development environment confined to my local network. This would allow for offline development (before uploading to a public server), and also allow hosting of local projects that are never meant to be online.

This is an update of my previous LAMP setup guide, where we host a LAMP server in an UnRAID VM. Updated to use PHP 8 and replacing MySQL with (open source, drop-in replacement) MariaDB.

Large portions of this guide are unchanged from the previous version.

Note that I will refer to MariaDB as “MySQL” throughout, for consistency with traditional LAMP. MariaDB advertises substantial compatibility with MySQL, and all MySQL commands are aliased to the MariaDB executables.

Please Note: The choice of software and settings are entirely based on what I imagine my personal needs to be, and depend upon my current (possibly flawed) understanding of the software involved. This guide might not be suitable for you, and even if it is, it might contain errors. Proceed at your own discretion.

Content may be updated and expanded over time.... take note of the “Updated” date, above.

Overview

Operating system:

  • Linux Mint Xfce 21.2

Server:

  • Apache 2.4
  • MariaDB 10.11
  • PHP 8.2

Additional features:

  • phpMyAdmin 5.1
  • Samba network file sharing
  • Virtual hosts

* version numbers as of this writing

1. User accounts

Once you have completed all steps in this guide, a variety of accounts and users will exist.

Anywhere you see a VARIABLE in this document, substitute your chosen value. To change passwords later, refer to appendix A.

1.1 Linux Users

Linux Mint does not have a separate user named root. The first user you create during installation gets root privileges; in this guide the primary admin user is VMUSER.

root VMPASS Virtual root user; not a true account
VMUSER VMPASS Primary admin user; has root privileges; member of the www-data usergroup

1.2 SQL Users

root SQLPASS MySQL administrator; requires local superuser privileges to use
SQLUSER SQLPASS MySQL administrator; used to access phpMyAdmin
DBUSER DBPASS Limited user with permission to manage only the DBNAME database

You should create separate low-privilege users for accessing your databases from PHP projects, such as DBUSER, above; refer to phpMyAdmin user management, and section 8 of this guide.

1.3 Samba User

SMBUSER SMBPASS Used to connect to the network share \\mint-vm\www; member of the www-data usergroup

1.4 Linux Usergroups

www-data Any member of this group has full R/W access to web files, locally via /var/www or remotely via \\mint-vm\www

2. UnRAID VM setup

Download a long-term support (LTS) release of Linux Mint Xfce 64-bit
Place the ISO file in UnRAID /mnt/user/isos

UnRAID [VMs] tab → [Add VM] → [Linux]

Name: Linux Mint Xfce
Initial Memory: 8192 MB
Max Memory: 8192 MB
USB Controller: 3.0 (qemu XHCI)
OS Install ISO: [select downloaded ISO file]
Primary vDisk Size: 48G
[Create]

Launch [VNC Remote] from “Linux Mint Xfce” VM dropdown menu

3. Linux installation

From the boot menu, run [Start Linux Mint]
From the desktop, run [Install Linux Mint]
Accept all defaults, then [Install Now]

Your name: Administrator
Your computer's name: mint-vm
Pick a username: VMUSER
Choose a password: VMPASS
Confirm your password: VMPASS
Log in automatically: yes
[Continue]

Wait for installation, then [Restart Now]
“Please remove the installation medium then press enter” → just press enter
Wait for restart
Uncheck [Show this dialog at startup] on Welcome screen, then close it

Open [Update Manager] from the tray
[Edit] menu → [Preferences] → Notifications: set all values to 90 days
Close [Preferences] window
Install all available updates

Open [System Reports] from the tray (warning icon with exclamation mark)
[Ignore this report] for each detected issue

Open [Power Manager] from the tray (lightning bolt icon)
[Display] tab → uncheck [Display power management], set [Blank after] to never
[Security] tab → [Automatically lock the session] → [Never]

Start Menu → All Applications → [Settings Manager]
[Desktop] icon → [Icons] tab → Default Icons: ☑ Removable Devices

Start Menu → All Applications → [Synaptic Package Manager]
[Status] button → [Installed] → Uninstall packages by clicking the checkbox and selecting “Mark for complete removal”

  • celluloid
  • drawing
  • growisofs
  • hexchat
  • hexchat-common
  • hypnotix
  • libreoffice-common
  • libreoffice-*
  • mythes-*
  • pix
  • pix-data
  • rhythmbox
  • rhythmbox-data
  • thunderbird
  • transmission-common
  • uno-libs-private

[Apply] button → [Apply]

Start Menu → All Applications → [Session and Startup]
[Application Autostart] tab → Uncheck programs:

  • mintwelcome
  • PulseAudio Sound System
  • Update Manager

Start Menu → Power Icon → [Restart]

4. LAN configuration

These instructions apply to routers running Tomato firmware; tested on FreshTomato v2018.4

STATIC-IP is an IP address suitable for your LAN; ex: 192.168.0.55
HOSTNAME is a local hostname for your server; using an unassigned TLD such as .home or .lan is recommended (avoid .local as it is reserved for use with mDNS); ex: mint-vm.home

Log in to your router

4.1 Set a static IP address

[Status] → [Device List] → (your VM) → [static]
Set a STATIC-IP address, [Add], [Save]

[Status] → [Device List] → (your VM)(click on lease time to delete current lease)
Deleting the lease will expire the existing IP allocation

Reboot the Mint VM, then verify the static IP address is in use

4.2 Set a local hostname for the server

[Advanced] → [DHCP/DNS] → [Dnsmasq Custom Configuration]

local-ttl=1
address=/.HOSTNAME/STATIC-IP

[Save]

This allows any computer on your LAN to access the Mint VM via the host name HOSTNAME or any subdomain *.HOSTNAME

5. Install server software

Inside the Mint VM, open terminal and run these commands

5.1 Prepare to install AMP

sudo apt update && sudo apt -y upgrade
sudo reboot

5.2 Install MariaDB SQL server

Find out what Ubuntu version your Mint OS is based on; we need the DISTRIB_CODENAME:

cat /etc/upstream-release/lsb-release

In my case, Linux Mint 21.2 is based on Ubuntu 22.04 “jammy”. The MariaDB respository only supports particular versions, so keep an eye on any error message generated from the CURL command below.

We want to install a long-term support (LTS) release of MariaDB. Check the releases list, note the newest “long-term MariaDB stable release” version number, MARIADB-VERSION. As of this writing, the latest LTS version is 10.11.

curl -LsS https://r.mariadb.com/downloads/mariadb_repo_setup | sudo bash -s -- --os-type=ubuntu --os-version=DISTRIB_CODENAME --mariadb-server-version="mariadb-MARIADB-VERSION"
sudo apt-get install mariadb-server mariadb-client -y

mysql -V
sudo service mariadb status | grep "Active:"

sudo mysql_secure_installation

Enter current password for root (enter for none): [press enter]
Switch to unix_socket authentication [Y/n]: N
Change the root password? [Y/n]: YSQLPASSSQLPASS
Remove anonymous users? [Y/n]: Y
Disallow root login remotely? [Y/n]: Y
Remove test database and access to it? [Y/n]: Y
Reload privilege tables now? [Y/n]: Y

sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf

Edit the line containing bind-address:

bind-address = 0.0.0.0

Ctrl+O, Enter (to save)
Ctrl+X (to exit nano)

sudo service mariadb restart

5.3 Install Apache web server

sudo apt-get install apache2 apache2-utils -y
apache2 -v
sudo service apache2 status | grep "Active:"

Test Apache: Open Firefox, view http://localhost

5.4 Install PHP and extensions

Add the ondrej repository for access to newer PHP versions, check available versions, then install PHP.

sudo add-apt-repository ppa:ondrej/php
sudo apt-get update
apt-cache policy php

sudo apt-get install php php-common php-cli libapache2-mod-php -y
php -v

Install PHP extensions:

sudo apt-get install php-mysql php-mbstring php-curl php-xml php-intl php-gd php-imagick -y
sudo service apache2 restart

Test PHP:

sudo nano /var/www/html/phpinfo.php

<?php phpinfo(); ?>

Ctrl+O, Enter (to save)
Ctrl+X (to exit nano)

Open Firefox, view http://localhost/phpinfo.php

5.5 Install phpMyAdmin and configure SQL user

sudo apt-get install phpmyadmin -y

when prompted for a webserver, press space to tick the box selecting apache2
when prompted to configure database, select yes
when prompted for MySQL password, enter SQLPASS

sudo mysql -uroot -pSQLPASS

DROP USER 'phpmyadmin'@'localhost';
CREATE USER 'SQLUSER'@'%' IDENTIFIED BY '';
GRANT ALL PRIVILEGES ON *.* TO 'SQLUSER'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
SELECT User,Host FROM mysql.user;

Ctrl+Z (to exit)

mysqladmin --user=SQLUSER password "SQLPASS"

sudo nano -w /etc/dbconfig-common/phpmyadmin.conf

Edit the line containing dbc_dbuser:

dbc_dbuser='SQLUSER'

Ctrl+O, Enter (to save)
Ctrl+X (to exit nano)

sudo nano -w /etc/phpmyadmin/config-db.php

Edit the line containing $dbuser:

$dbuser='SQLUSER';

Ctrl+O, Enter (to save)
Ctrl+X (to exit nano)

sudo service mysql restart
sudo service apache2 restart

Open Firefox, view http://localhost/phpmyadmin
Username: SQLUSER
Password: SQLPASS

5.6 Enable “nologin” user accounts

sudo nano /etc/shells

Append this line to the end of the file:

/usr/sbin/nologin

Ctrl+O, Enter (to save)
Ctrl+X (to exit nano)

5.7 Set permissions on web folder

sudo adduser $USER www-data
sudo chown $USER:www-data -R /var/www
sudo chmod u=rwX,g=srwX,o=rX -R /var/www
sudo chmod 0664 /var/www/html/*

5.8 Allow .htaccess configuration in web folder

sudo nano -w /etc/apache2/apache2.conf

Inside configuration block <Directory /var/www/>, alter the AllowOverride line:

AllowOverride All

Ctrl+O, Enter (to save)
Ctrl+X (to exit nano)

sudo service apache2 restart

5.9 Enable mod_rewrite Apache module

sudo a2enmod rewrite
sudo service apache2 restart

5.10 Increase PHP upload and POST size limits

PHP defaults to 8 megabytes max POST size and 2 megabytes max file upload size. Changing these limits so they match can make debugging easier. Select a limit MAXUPLOAD megabytes that is suitable for your projects.

Note: the path below may require updating to match the installed PHP version.

sudo nano -w /etc/php/8.2/apache2/php.ini

Alter the line containing post_max_size:

post_max_size = MAXUPLOADM

Alter the line containing upload_max_filesize:

upload_max_filesize = MAXUPLOADM

Ctrl+O, Enter (to save)
Ctrl+X (to exit nano)

sudo service apache2 restart

5.11 Enable PHP error reporting

If you're using this server for development purposes, you will want to allow PHP to display errors. The display_errors setting should be On for development, and Off for production/live servers.

Note: the path below may require updating to match the installed PHP version.

sudo nano -w /etc/php/8.2/apache2/php.ini

Alter the line containing display_errors:

display_errors = On

Ctrl+O, Enter (to save)
Ctrl+X (to exit nano)

sudo service apache2 restart

Alternately, you can control this setting on a script-by-script basis by calling the PHP ini_set() function:

ini_set ('display_errors', 'On');

Error reporting can be further controlled by the error_reporting setting in php.ini or the corresponding error_reporting() function in your PHP script. For production I use a value of 0, or (E_ALL | E_STRICT) for development.

5.12 Clear command-line history and reboot

history -cw
sudo reboot

6. Install and configure Samba

6.1 Install Samba

sudo apt-get update
sudo apt-get install samba -y

6.2 Create Samba user

sudo useradd -s /usr/sbin/nologin SMBUSER
sudo passwd SMBUSER

when prompted for password, enter SMBPASS

sudo adduser SMBUSER www-data

sudo smbpasswd -a SMBUSER

when prompted for password, enter SMBPASS

sudo smbpasswd -e SMBUSER

6.3 Add network share

sudo nano -w /etc/samba/smb.conf

Append these configuration lines to the end of the file:

[www]
  comment = Web Data
  path = /var/www
  guest ok = no
  read only = no
  writeable = yes
  browseable = yes
  valid users = +www-data
  create mask = 0664
  directory mask = 0775

Ctrl+O, Enter (to save)
Ctrl+X (to exit nano)

sudo service smbd restart

Validate your Samba configuration:

testparm -s /etc/samba/smb.conf

normalized configuration file is printed; look for error messages

6.4 Test network share

On Windows you may need to flush all open network connections to use new credentials; run this from a command prompt:

net use * /delete

From a Windows PC, connect to the Samba share:
Start Menu → Run → "\\mint-vm\www"
Username: SMBUSER
Password: SMBPASS

7. Configure Apache virtual hosts

* Configuration tested on Apache v2.4

* For detailed reference, refer to the Apache documentation:

* Refer to section 4 in this guide to define a local hostname

Enable multi-project hosting using Apache virtual hosts; this example configures 4 hosts: delta, gamma, phpmyadmin, and a default host:

delta.HOSTNAME /var/www/delta
gamma.HOSTNAME /var/www/gamma
phpmyadmin.HOSTNAME /usr/share/phpmyadmin
HOSTNAME /var/www/www

Note that in this configuration the default host is served for any request by IP address and any request to an unknown hostname. This behaviour has security implications: if your server becomes publicly-accessible, your default host might be served. Therefore we remove all admin-related content (such as phpMyAdmin) from this area.

In this example we keep no content on the default host; all real content is served from specific subdomains.

7.1 Remove default host access to phpMyAdmin

Since phpMyAdmin will be accessed under its own subdomain, we should remove it from the default host; this allows us to explicitly control who can access it:

sudo nano -w /etc/phpmyadmin/apache.conf

Comment out (prepend a # character) this Alias line:

#Alias /phpmyadmin /usr/share/phpmyadmin

Ctrl+O, Enter (to save)
Ctrl+X (to exit nano)

sudo service apache2 restart

phpMyAdmin can no longer be accessed from http://localhost/phpmyadmin

7.2 Change the default host's server name and document root

By default Apache serves web files from /var/www/html; we change this directory to better integrate with a multi-host setup:

sudo nano -w /etc/apache2/sites-enabled/000-default.conf

Insert the following line at the top of the file:

ServerName HOSTNAME

This comment may be worth adding inside the <VirtualHost> configuration block:

# This first virtual host definition becomes the default.
# Default is served for all requests not matching hosts below,
# including requests by IP and requests for undefined hosts.

Add a ServerAlias line inside the <VirtualHost> configuration block:

ServerAlias www.HOSTNAME

Change the DocumentRoot line:

DocumentRoot "/var/www/www"

Ctrl+O, Enter (to save)
Ctrl+X (to exit nano)

Delete the previous html directory and create a new www directory
WARNING: all files in /var/www/html will be deleted!

rm -r /var/www/html
mkdir /var/www/www

Validate your configuration and restart the server:

apache2ctl configtest
sudo service apache2 restart

You may implement a website on the default host, however in this example setup we prefer to respond “404 Not Found” to all requests that don't match an expected virtual host subdomain:

nano /var/www/www/index.php

<?php
  http_response_code (404);
  header ('Content-Type: text/plain');
  echo '404 Not Found';
  die();
?>

Ctrl+O, Enter (to save)
Ctrl+X (to exit nano)

Visiting any address that resolves to this server should now yield a 404 error, generated from /var/www/www/index.php; for example:

7.3 Reenable phpMyAdmin under a virtual host

sudo nano -w /etc/apache2/sites-enabled/000-default.conf

Append the following configuration block to the end of the file:

<VirtualHost *:80>
  DocumentRoot "/usr/share/phpmyadmin"
  ServerName phpmyadmin.HOSTNAME
  ServerAlias *.phpmyadmin.HOSTNAME
</VirtualHost>

Ctrl+O, Enter (to save)
Ctrl+X (to exit nano)

Validate your configuration and restart the server:

apache2ctl configtest
sudo service apache2 restart

Visiting http://phpmyadmin.HOSTNAME now accesses phpMyAdmin

7.4 Add additional virtual hosts, as desired

This example sets up two projects, delta and gamma, each with their own hostname and server directory. In your configuration, you would instead use these placeholders as examples to configure your own projects.

mkdir /var/www/delta
mkdir /var/www/gamma

sudo nano -w /etc/apache2/sites-enabled/000-default.conf

Append the following configuration blocks to the end of the file:

<VirtualHost *:80>
  DocumentRoot "/var/www/delta"
  ServerName delta.HOSTNAME
  ServerAlias *.delta.HOSTNAME
</VirtualHost>

<VirtualHost *:80>
  DocumentRoot "/var/www/gamma"
  ServerName gamma.HOSTNAME
  ServerAlias *.gamma.HOSTNAME
</VirtualHost>

Ctrl+O, Enter (to save)
Ctrl+X (to exit nano)

Validate your configuration and restart the server:

apache2ctl configtest
sudo service apache2 restart

Access these sites via http://delta.HOSTNAME and http://gamma.HOSTNAME

8. Create a database and limited SQL user

Tip: Match the database name, username, and password of the database from your online hosting provider when configuring this local database. Migrating projects between the two then involves only changing the host name of the database server.

Execute this SQL code from the phpMyAdmin [SQL] tab to create a database:

CREATE DATABASE `DBNAME` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

Create a limited user who can only control this database:

CREATE USER 'DBUSER'@'%' IDENTIFIED WITH mysql_native_password AS '';

GRANT USAGE ON *.* TO 'DBUSER'@'%' REQUIRE NONE;

GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER,
CREATE TEMPORARY TABLES, CREATE VIEW, EVENT, TRIGGER, SHOW VIEW,
CREATE ROUTINE, ALTER ROUTINE, EXECUTE ON `DBNAME`.* TO 'DBUSER'@'%';

phpMyAdmin → [Home] → [User accounts] → (DBUSER) → [Edit privileges] → [Change password]
Assign a password DBPASS to the database user

From PHP, this database can now be accessed through mysqli or equivalent:

$db = new mysqli ('localhost', 'DBUSER', 'DBPASS', 'DBNAME');

Appendix A: Changing passwords

If you need to change an account password after initial setup, use the following procedures.

If you specify a password directly on the command-line, you can later clear the command-line history, for security:

history -cw

A.1 Linux User

sudo passwd VMUSER

sudo may ask for a password; use OLD-VMPASS
when prompted for new UNIX password, enter NEW-VMPASS

sudo reboot

A.2 SQL User

mysqladmin --user=SQLUSER --password=OLD-SQLPASS password "NEW-SQLPASS"

sudo nano -w /etc/dbconfig-common/phpmyadmin.conf

Edit the line containing dbc_dbpass:

dbc_dbpass='NEW-SQLPASS'

Ctrl+O, Enter (to save)
Ctrl+X (to exit nano)

sudo nano -w /etc/phpmyadmin/config-db.php

Edit the line containing $dbpass:

$dbpass='NEW-SQLPASS';

Ctrl+O, Enter (to save)
Ctrl+X (to exit nano)

sudo service mysql restart
sudo service apache2 restart

A.3 Samba User

sudo passwd SMBUSER

sudo may ask for a password; use VMPASS
when prompted for new UNIX password, enter NEW-SMBPASS

sudo smbpasswd SMBUSER

when prompted for password, enter NEW-SMBPASS

sudo service smbd restart

On Windows you may need to flush all open network connections to use new credentials; run this from a command prompt:

net use * /delete

Appendix B: Mount a network share for read/write access

Mount a network share in Linux: This allows your VM to read from and write to shared folders on your network.

Appendix C: Project-specific features

Install the escpos-php receipt printer library

Install and test the escpos-php library: used for communicating with thermal receipt printers via the ESC/POS protocol.

Additionally, if your printer does not natively support printing QR codes, you can add image-based QR code support to the escpos-php library by integrating the PhpQrCode library.

Install the PHP Simple HTML DOM Parser

Install the SimpleHtmlDom library: This PHP library allows for exceptionally-easy web scraping and parsing of arbitrary HTML pages.

[end of guide]