A comprehensive guide for setting modern web environment from scratch

Image
Image
Image
Image
Image
Risorsa 81

Requires root privileges.

Includes Drupal specific installations and config.

Nowadays we use a modern environments and tools, like Docker or certain cloud, or locally DDEV, which provide complex installations and configurations more or less out-of-the-box. Still sometimes "the classic" remote host setup ends on the agenda and, in my case, every time in such situation I go for online resources, searching and reading/trying until the work is done. Yet, this remains work by just an enthusiast, far from expert. In the latest case I've decided to log down every step thoroughly, for my own sake for any next time but also to share here eventually.

The following is indeed just one possible case scenario that includes at this moment latest Ubuntu, NGINX, PHP, MariaDB, Letsencrypt, Composer, Drush, as well as a group of the other more or less common daemons and tools.

Ubuntu 24.x

# Refresh packages and update
Code
apt update && apt upgrade -y
# Set timezone
Code
# Check time/date information.
timedatectl
# List all timezones.
timedatectl list-timezones
# Set one found on previous list.
timedatectl set-timezone [timezone_string_from_the_list]
# Confirm change.
timedatectl
# Create sudo user
Code
# Add user
adduser your_user_name
# Add to 'sudo' group.
usermod -aG sudo your_user_name
# Add user's SSH public key, paste it in this file.
nano ~/.ssh/authorized_keys
# Memcached
# Rsnapshot

NGINX

The cleanest and probably the "safest" way to do this part is to generate configuration with Drush, it's amazing how this tool never fails to surprise in the most positive way! Otherwise, check below more extensive example - warning just an example - there is some extra security rules as well as gzip compression etc.

Additionally, there is a Logrotate utilization example.

Install
Code
# Install current stable version.
apt install nginx
# Check Firewall settings.
ufw app list
# Make sure NGINX has full access.
ufw allow 'Nginx Full'
# Confirm firewall status.
ufw status
# Check NGINX status.
systemctl status nginx
Generate server block with Drush
Code
drush generate misc:nginx-virtual-host
Advanced example, Drupal >= 10
Logrotate option

MariaDB

# Install
Code
apt install mariadb-server mariadb-client
# Check service status.
systemctl status mariadb
# Start service.
systemctl start mariadb
# Enable auto start.
systemctl enable mariadb
# Configuration
Code
# Create custom configuration file.
nano -w /etc/mysql/mariadb.conf.d/70-custom.cnf
# And add this there.
[mysqld]
transaction_isolation="READ-COMMITTED"
# Secure installation
Code
# Start secure installation
mysql_secure_installation
1. Enter root password if you have one, or skip by pressing Enter with key authentication only.
2. Don’t switch to 'unix_socket' authentication because MariaDB is already using 'unix_socket' authentication.
3. Don’t change the root password, no need when using unix_socket authentication.
4. Next, you can press Enter to answer all remaining questions, which will remove anonymous user, disable remote root login and remove test database. This step is a basic requirement for MariaDB database security.
# Login and status
Code
# Check MariaDB server version.
mariadb --version
# Test login
mariadb -u root
exit;

Letsencrypt

The classic, becomes essential for us who aim open-source.

Certbot
Code
# Install Certbot.
apt -y install certbot
# Create certificate for the given domain(s).
certbot certonly --webroot -w /var/nginx/share/html/ph-eu.dev -d ph-eu.dev
# Check timer and service.
systemctl list-timers certbot.timer --no-pager
systemctl status certbot.timer
systemctl cat certbot.timer
systemctl cat certbot.service
# For manual update, do:
certbot renew
# In case you get error like "Could not bind TCP port 80 because it is already in use by another process on this system...":
# Stop nginx.
service nginx stop
# Run Certbot to obtain the certificate.
certbot certonly --standalone -d ph-eu.dev
# Restart the web server.
service nginx start

PHP

# Install
Code
# Install PHP extensions (add php8.3 on the list in case it's not (pre)installed).
apt install php8.3 php8.3-fpm php8.3-cli php8.3-common php8.3-curl php8.3-gd php8.3-mbstring php8.3-mysql php8.3-opcache php8.3-readline php8.3-sqlite3 php8.3-xml php8.3-zip php8.3-apcu
# Configuration.
Code
# Configure APCu cache. Create '20-apcu.ini' file in case it does not exist.
nano -w /etc/php/8.3/fpm/conf.d/20-apcu.ini
# Place this there, for instance.
  
  apc.shm_size="96M"
# Set resources and timezone values for FPM.
nano -w /etc/php/8.3/fpm/conf.d/60-custom.ini
# Enter desired values there, for instance:
; Maximum amount of memory a script may consume. Default is 128M
memory_limit = 1G
; Maximum allowed size for uploaded files. Default is 2M.
upload_max_filesize = 260M
; Maximum size of POST data that PHP will accept. Default is 2M.
post_max_size = 280M
; The OPcache shared memory storage size. Default is 128
opcache.memory_consumption=256
; The amount of memory for interned strings in Mbytes. Default is 8.
opcache.interned_strings_buffer=32
; Set actual timezone.
date.timezone = [your_timezone_string]
# Set timezone to CLI php.
nano -w /etc/php/8.3/cli/conf.d/60-custom.ini
; Set actual timezone.
date.timezone = Europe/Amsterdam

Composer and Drush

Composer
Code
# Update packages and install unzip.
apt update
apt install unzip
# Download and verify Composer.
cd ~
curl -sS https://getcomposer.org/installer -o /tmp/composer-setup.php
HASH=`curl -sS https://composer.github.io/installer.sig`
echo $HASH
php -r "if (hash_file('SHA384', '/tmp/composer-setup.php') === '$HASH') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-set
up.php'); } echo PHP_EOL;"
# Install Composer and run it to confirm.
php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer
composer
Drush
Code
# Navigate to the project root, then:
composer require drush/drush
# Add Drush to the PATH, note this works only from project root.
nano ~/.bashrc
 # Append this.
export PATH="$PATH:./vendor/bin:/usr/local/bin"
# Create site aliases in
[project_root]/drush/sites
# Test aliases
drush sql:sync @prod @self 

Upgrade Drupal 9 to 10 or 11

Basic stuff and Sources for now. To be continued...

Lenient
Code
# Install Composer Lenient for being able to place non-compatible packages.
# @see https://github.com/mglaman/composer-drupal-lenient
composer require mglaman/composer-drupal-lenient
# To allow a package to have a lenient Drupal core version constraint, you must add it to: extra.drupal-lenient.allowed-list
composer config --merge --json extra.drupal-lenient.allowed-list '["drupal/token"]'
# Now you can require the module in question
composer require drupal/token:1.10.0
Rector
Code
# Make sure you are at the project's root. Then, get and copy rector.php
composer require --dev palantirnet/drupal-rector
cp vendor/palantirnet/drupal-rector/rector.php .
# Run check, in this example for a particular module. Remove dry run when ready. 
vendor/bin/rector process web/modules/contrib/token --dry-run

SPF, DKIM, and DMARC