Setup Ashwini

Running documentation for the server setup in Ashwini. Here are some Linux references which I find useful:

Internet Connection

For overview see, Configuring Networks on Ubuntu Server.

  • Check ethernet name:
ip -d address
  • Create configuration in /etc/netplan/01-netcfg.yaml.
network:
  version: 2
  renderer: networkd
  ethernets:
    # interface name
    ens18:
      dhcp4: false
      # IP address / subnet mask
      # removed exact numbers for security
      addresses:
        - xxx.xxx.xxx.xxx/25
      # default gateway
      routes:
        - to: default
          via: xxx.xxx.xxx.xxx
      nameservers:
        addresses:
        - 1.1.1.1
        - 8.8.8.8
        - 8.8.4.4
      dhcp6: false
  • Apply configuration
sudo netplan --debug apply
  • Ping local IP and external IP to check
ping xxx.xxx.xxx.xxx
ping google.com
  • I like to see what is happening!
ip -s link show ens18
ip route show

Update OS

sudo apt update
sudo apt upgrade
sudo reboot
sudo apt install vim build-essential git

Purge snap

I do not like things happening in the background without my consent. Therefore, I remove snap after a fresh Ubuntu install. I list the snap packages, stop the snap daemon service(s), and remove each snap package individually, starting with non-canonical packages. I remove the snapd package and all the related directories.

sudo snap list

sudo systemctl disable snapd.service
sudo systemctl disable snapd.socket
sudo systemctl disable snapd.seeded.service

sudo snap remove --purge lxd
sudo snap remove --purge core20

sudo snap list # check this does not list anything else

sudo apt remove --autoremove snapd
sudo rm -rf ~/snap /snap /var/snap /var/lib/snapd /root/snap

The last command complained about “read-only file system”. So, I had to reboot and rerun the command. Some systemd services were still blocking the mount points. I manually obliterated snap related systemd services, following this Stackoverflow answer.

cd /etc/systemd/system
sudo systemctl stop snap-snapd-18357.mount
sudo systemctl disable snap-snapd-18357.mount
sudo rm -f snap-snapd-18357.mount
sudo systemctl daemon-reload
sudo systemctl reset-failed

I did the same thing for all these services:

- snap-snapd-20092.mount
- snapd.aa-prompt-listener.service
- snapd.apparmor.service
- snapd.autoimport.service
- snapd.core-fixup.service
- snapd.snap-repair.timer
- snapd.system-shutdown.service
- snapd.recovery-chooser-trigger.service

To-Do: snapd.seeded.service is still being listed by systemctl list-units -a. How to remove?

Prevent snap from re-installing. Create the file /etc/apt/preferences.d/nosnap.pref with the following content and save.

Package: snapd
Pin: release a=*
Pin-Priority: -10

Add User For Tasks

user minion, group minion (not in sudoer list)

sudo groupadd minion
sudo usermod -a -G minion banskt
sudo useradd -g minion -m -c "User account for webstack" -s /bin/bash minion
sudo passwd minion

SSH Security

  • Select a port to change the default SSH Port. Do not choose a port already in use. Ports from 0 to 1023 are reserved and should not be used. Choose a port greater than 1023 and less than 65535. Check using:
ss
ss - tulpn
  • Change the following relevant lines in /etc/ssh/sshd_config
# Change protocol
Protocol 2
# 
Port xxxxx
LogLevel VERBOSE
PermitRootLogin no
# trusted hosts are still considered a security risk
HostbasedAuthentication no
IgnoreRhosts yes
# do not allow eavesdropping
PermitEmptyPasswords no
# better to not allow TCP forwarding, but required for SSH tunnels
AllowTcpForwarding yes
# don't have X11 on server
X11Forwarding no
# last login info can provide available users to hackers
PrintLastLog no

Save the file and restart SSH service.

sudo systemctl restart ssh
sudo systemctl status ssh
  • Open the port in firewall
sudo apt install ufw
sudo ufw app list
sudo ufw status verbose
sudo ufw allow xxxxx/tcp # enter the SSH port here
sudo ufw enable
sudo ufw status verbose
  • Set up ~/.ssh/config and SSH key on the client side.
  • Check if SSH is working!

Add Swap Memory

sudo -i
dd if=/dev/zero of=/swapfile bs=1024 count=1048576 #1GB: 1024 x 1024 = 1048576
chmod 0600 /swapfile
mkswap /swapfile
swapon /swapfile
echo "/swapfile none swap sw 0 0" >> /etc/fstab
swapon -s
free -m

Mount Hard Disks

  • Check existing partitions and harddisks
sudo lsblk
sudo fdisk -l
sudo lshw -C disk
  • Partition using parted.
  • Create entry in etc/fstab. You can check all uuid from ls -lh /dev/disk/by-uuid/ or individually from sudo blkid /dev/sdb1.
# /dev/sdb1 --> /opt
/dev/disk/by-uuid/xxxx /opt ext4 defaults 0 2
# /dev/sdb2 --> ~/local
/dev/disk/by-uuid/xxxx /home/banskt/local ext4 user,rw,suid,dev,exec,auto,async 0 2
# /dev/sdb3 --> ~/data
/dev/disk/by-uuid/xxxx /home/banskt/data ext4 user,rw,suid,dev,exec,auto,async 0 2

The sequence of options are important. - After mounting you have to change the ownership of the directories, if required. - Reboot and check if the partitions are mounted automatically.

Bash

Install dotfiles from Github. But, I did not want to install everything from .dotfiles. So, I made a dryrun to import the functions and link individual dotfiles.

git clone https://github.com/banskt/dotfiles.git ~/.dotfiles
source ~/.dotfiles/install dryrun
DRYRUN=false
backup_and_link .bashrc bash/bashrc
backup_and_link .vimrc vim/vimrc
backup_and_link .vim vim/dotvim
cp ~/.dotfiles/git/gitconfig ~/.gitconfig
cp ~/.dotfiles/ssh/config ~/.ssh/
chmod 600 ~/.ssh/config

I edited the .ssh/config file and kept configurations for Github and my other servers (vultr and feral). I edited the .gitconfig to include my email. Logout and login for the changes to take effect.

Finally, create SSH key for Github and add that key to the Github account!

cd ~/.ssh
ssh-keygen -t ed25519 -f github_key
git remote set-url origin git@github.com:banskt/dotfiles.git
git remote show origin

Note: Any custom bash script for this server must go to ~/.custom_dotfiles/bashrc. Common bash scripts go to the ~/.dotfiles.

To-Do: Include a server flag for installation.

Reduce Automatic Network Usage

There are some default packages or services that connects to the internet without my consent. AskUbuntu Question.

Environment Modules

I love modules to manage my shell environment. For a dedicated server, it may not be necessary, but it helps me to manage versions and updates.

First, install the dependencies.

# use apt-cache search for the latest version
sudo apt install tcl8.6-dev 
# dejagnu provides runtest. Only install if you want to run `make test`, skip otherwise
# without dejagnu, configure produces this -->
## WARNING: Install `dejagnu' if you want to run the testsuite
sudo apt install dejagnu 

Note: I have not installed dejagnu.

Next, install the package.

wget github.com/cea-hpc/modules/releases/download/v5.3.1/modules-5.3.1.tar.gz
tar -zxf modules-5.3.1.tar.gz
./configure --prefix=/opt/modules --modulefilesdir=/opt/modulefiles --with-tcl-ver=8.6
make -j 2
sudo make install

Finally, configure. Initialize modules in ~/.custom_dotfiles/bashrc: This has to be included in bashrc of each user who need to access modulefiles. Alternative is to include it in /etc/bash.bashrc or /etc/profile but those are not guranteed to be invoked (see decision tree for loading config files in different shell environments).

# Environment modules
source /opt/modules/init/bash

Also, put all the example dotfiles in a separate directory so that do not show up in module avail.

sudo mkdir -p /opt/modules/examples
cd /opt/modulefiles
sudo mv dot module-git module-info modules null use.own /opt/modules/examples/

Webtack (nginx, SSL, PHP)

Previously, I installed a webstack in Kalindi.

Install nginx from source

Compiling nginx from source affords more flexibility than prebuilt packages: I can add particular modules (from nginx or third parties), and apply latest security patches. See official documentation for help.

1. Install prerequisites.

PCRE2

I prefer to manage the library files in the OS using apt. So, I use apt install.

sudo apt install libpcre2-dev

Surprise: pcre3 is older than pcre2. Do not install pcre3.

If you want to configure, build and install PCRE2 from scratch, it can be done as:

sudo apt install zlib1g-dev libbz2-dev libreadline-dev libedit-dev
wget github.com/PCRE2Project/pcre2/releases/download/pcre2-10.42/pcre2-10.42.tar.gz
tar -zxf pcre2-10.42.tar.gz
cd pcre2-10.42
./configure --prefix /opt/pcre2/10.42 --enable-unicode --enable-jit --enable-pcre2-16 --enable-pcre2-32 --enable-pcre2grep-libz --enable-pcre2grep-libbz2 --enable-pcre2test-libreadline --disable-static
make -j 2
make check
sudo make install

Zlib

sudo apt install zlib1g-dev

OpenSSL

nginx requires libssl-dev package. But before installing anything, I checked the distribution of current OpenSSL package:

openssl version -a

I have version 3.0.2 installed by default. I wanted the latest version of OpenSSL, hence I configured myself (see documentation) using the configuration options of Linux from Scratch. Run ./Configure LIST to see a list of configuration options.

wget https://www.openssl.org/source/openssl-3.1.3.tar.gz
tar -zxf openssl-3.1.3.tar.gz
mkdir openssl-3.1.3-build
cd openssl-3.1.3-build/
../openssl-3.1.3/Configure linux-x86_64 --prefix=/opt/openssl/3.1.3/ zlib-dynamic shared
make -j 2
make test
sudo make install

I created a module file to load/unload the required OpenSSL version easily.

module load openssl/3.1.3
module unload openssl/3.1.3

2. Install nginx

Configure

Download the source files from nginx.org. I used the latest stable release.

wget https://nginx.org/download/nginx-1.24.0.tar.gz
tar -zxf nginx-1.24.0.tar.gz

There are many configuration options for nginx – official docs | open source project.

Other resources include:

Here are the configuration options I used which can also be checked later using nginx -V.

./configure \
  --prefix=/opt/nginx/1.24.0 \
  --builddir=../nginx-1.24.0-build \
  --with-http_ssl_module \
  --with-http_v2_module \
  --with-http_realip_module \
  --with-http_addition_module \
  --with-http_sub_module \
  --with-http_dav_module \
  --with-http_flv_module \
  --with-http_mp4_module \
  --with-http_gunzip_module \
  --with-http_gzip_static_module \
  --with-http_random_index_module \
  --with-http_secure_link_module \
  --with-http_stub_status_module \
  --with-http_auth_request_module \
  --with-stream \
  --with-stream_ssl_module \
  --with-http_slice_module \
  --with-mail \
  --with-mail_ssl_module \
  --with-pcre \
  --with-threads \
  --with-file-aio \
  --with-cc-opt="-I /opt/openssl/3.1.3/include" \
  --with-ld-opt="-L /opt/openssl/3.1.3/lib64 -ldl -Wl,-rpath,/opt/openssl/3.1.3/lib64"

One liner (for copy/paste):

./configure --prefix=/opt/nginx/1.24.0 --builddir=../nginx-1.24.0-build --with-http_ssl_module --with-pcre --with-http_v2_module --with-http_realip_module --with-http_addition_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_stub_status_module --with-http_auth_request_module --with-threads --with-stream --with-stream_ssl_module --with-http_slice_module --with-mail --with-mail_ssl_module --with-file-aio --with-cc-opt="-I /opt/openssl/3.1.3/include" --with-ld-opt="-L /opt/openssl/3.1.3/lib64 -ldl -Wl,-rpath,/opt/openssl/3.1.3/lib64"

Here is the configuration output which may be helpful later:

Configuration summary
  + using threads
  + using system PCRE2 library
  + using system OpenSSL library
  + using system zlib library

  nginx path prefix: "/opt/nginx/1.24.0"
  nginx binary file: "/opt/nginx/1.24.0/sbin/nginx"
  nginx modules path: "/opt/nginx/1.24.0/modules"
  nginx configuration prefix: "/opt/nginx/1.24.0/conf"
  nginx configuration file: "/opt/nginx/1.24.0/conf/nginx.conf"
  nginx pid file: "/opt/nginx/1.24.0/logs/nginx.pid"
  nginx error log file: "/opt/nginx/1.24.0/logs/error.log"
  nginx http access log file: "/opt/nginx/1.24.0/logs/access.log"
  nginx http client request body temporary files: "client_body_temp"
  nginx http proxy temporary files: "proxy_temp"
  nginx http fastcgi temporary files: "fastcgi_temp"
  nginx http uwsgi temporary files: "uwsgi_temp"
  nginx http scgi temporary files: "scgi_temp"

Build

Build nginx in the new build directory.

make -f ../nginx-1.24.0-build/Makefile -j 2
sudo make -f ../nginx-1.24.0-build/Makefile install

3. Post-install configuration

I initially considered using custom ports for HTTP and HTTPS (instead of the default 80 and 443) but I rejected the idea because Let’s Encrypt requires port 80 for generating and renewing certificates. These articles also helped in making the decision:

nginx.conf

Modify the configuration file for external access. Here is my initial nginx configuration in /opt/nginx/1.24.0/conf/nginx.conf.

user  minion;
worker_processes  1;

events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       80;
        server_name  127.0.0.1 <ipv4_address>;
        location / {
            root   html;
            index  index.html index.htm;
        }
        # redirect server error pages to the static page /50x.html
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

Open port in firewall

To register nginx as an ufw application, create a file /etc/ufw/applications.d/nginx. Replace xx with port numbers for HTTP and HTTPS. If you are running nginx as a non-root user, then you have to change the accessible port because the default port 80 is not available to non-root users. Therefore, it’s necessary to use a port > 1000.

[Nginx HTTP]
title=Web Server (Nginx, HTTP)
description=Small, but very powerful and efficient web server
ports=80/tcp

[Nginx HTTPS]
title=Web Server (Nginx, HTTPS)
description=Small, but very powerful and efficient web server
ports=443/tcp

[Nginx Full]
title=Web Server (Nginx, HTTP + HTTPS)
description=Small, but very powerful and efficient web server
ports=80,443/tcp

Check the registered app and allow nginx HTTP.

sudo ufw app info 'Nginx HTTP'
sudo ufw allow 'Nginx HTTP'

Log files should be accessible by the user minion.

chown -R root:minion /opt/nginx/1.24.0/logs
chown minion:minion /opt/nginx/1.24.0/logs/access.log
chown minion:minion /opt/nginx/1.24.0/logs/error.log
chmod 775 /opt/nginx/1.24.0/logs

Useful commands

Check nginx configuration and start nginx.

# check configuration
sudo /opt/nginx/1.24.0/sbin/nginx -t
# start
sudo /opt/nginx/1.24.0/sbin/nginx
# stop
sudo /opt/nginx/1.24.0/sbin/nginx -s stop
# restart
sudo /opt/nginx/1.24.0/sbin/nginx -s reload

systemd

Create a systemd service file to control nginx using systemctl as described in the official documentation. In Ubuntu 22.04, the file is /lib/systemd/system/nginx.service.

[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/opt/nginx/1.24.0/logs/nginx.pid
ExecStartPre=/opt/nginx/1.24.0/sbin/nginx -t
ExecStart=/opt/nginx/1.24.0/sbin/nginx
ExecReload=/opt/nginx/1.24.0/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Now, I can use systemctl commands to control nginx.

sudo systemctl status nginx
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx

Check

Fire up the browser and check both links:

  • http://127.0.0.1
  • http://<ipv4_address>

If you are installing on the server and you do not have access to the localhost (127.0.0.1) of the remote server, then you can ssh-tunnel to the remote server from your local machine. For example, I defined ashwini in ssh/config of the local machine and installed ssh-localportfwd from here.

ssh-localportfwd open ashwini 80 <local_port>

Then, I can access the localhost of the remote machine using http://127.0.0.1:xx where xx is the local_port of my local machine through which I tunneled. The second IPv4 address should work from external network.

The SSL connection will still not work. Do not worry yet.

Modular control

Once everything was working, I removed the external IP from the base configuration of nginx and created server configurations in ~/local/etc/nginx/sites-available/ For example, here is my 000-default-server.conf:

server {
    listen      80 default_server;
    server_name <ipv4_address>;
    location / {
        root        /path/to/webdata/base;
        index       index.html index.htm;
    }
    # redirect server error pages to the static page /50x.html
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root        /path/to/webdata/base;
    }

    # produce a directory listing using ngx_http_autoindex_module 
    autoindex            on;
    autoindex_exact_size off;
    autoindex_localtime  on;

    # Deny access to anything starting with .ht
    location ~ /\.ht {
        deny  all;
    }
}

I added the following line to /opt/nginx/1.24.0/conf/nginx.conf:

include /home/banskt/local/etc/nginx/sites-enabled/*

And then created the sites-enabled directory:

mkdir -p ~/local/etc/nginx/sites-enabled
cd ~/local/etc/nginx/sites-enabled
ln -s /home/banskt/local/etc/nginx/sites-available/000-default-server.conf ./default

This way, I can manage adding domains, sub-domains, sub-directories on my server directly from my sites-available and sites-enabled directories.

At this point, 127.0.0.1 should serve the nginx welcome message and <ipv4_address> should serve the HTML files from /path/to/webdata/base. Again, check they are working with ssh-localportfwd.

SSL

I did not use certbot because it enforced installation using snap.

  • Install acme. The options used for the installation are explained in the documentation.
git clone https://github.com/acmesh-official/acme.sh.git
cd acme.sh/
./acme.sh --install --home /home/banskt/local/apps/acme --accountemail banskt.saikat@gmail.com

Delete the entry in ~/.bashrc and add it to ~/.custom_dotfiles/bashrc.

# Source acme.sh for SSL certificates
source "/home/banskt/local/apps/acme/acme.sh.env"

After sourcing, acme.sh will be available on commandline. Check.

  • Create the acme-challenge directory in the document root.
cd /path/to/webdata/base
mkdir -p .well-known/acme-challenge/
sudo chown -R minion:minion .well-known
sudo chmod -R 770 .well-known

and give permissions according to your setup.

  • Create directory to store SSL certificates
cd /home/banskt/local/etc/nginx/
mkdir -p ssl/mydomain.com/
  • Generate dhparams file
cd /home/banskt/local/etc/nginx/ssl/
module load openssl/3.1.3
openssl dhparam -out dhparams.pem 4096
  • Issue certificates. Optionally, use DNS API. You can either issue certificate for a single domain,
acme.sh --issue -d foo.mydomain.com -w /path/to/webdata/base -k 4096

or have multiple domains in the same certificate.

acme.sh --issue -d mydomain.com -d www.mydomain.com -w /path/to/webdata/base -k 4096

Here, -w specifies the document root of the website where the acme-challenge is created, -k specifies the domain key length.

  • Generate SSL configurations from Mozilla SSL Configuration Generator. Write the configuration in /opt/nginx/1.24.0/conf/ssl_params.conf so that it can be included for all servers. Remember to update your DNS resolver from /etc/resolv.conf. For each server, create the SSL nginx configuration, for example,
  ssl_certificate         /home/banskt/local/etc/nginx/ssl/sbanerjee.in/fullchain.pem;
  ssl_certificate_key     /home/banskt/local/etc/nginx/ssl/sbanerjee.in/privkey.pem;
  # verify chain of trust of OCSP response using Root CA and Intermediate certs
  ssl_trusted_certificate /home/banskt/local/etc/nginx/ssl/sbanerjee.in/fullchain.pem;
  include                 /opt/nginx/1.24.0/conf/ssl_params.conf;
  • Install the issued certificate to nginx server
acme.sh --install-cert -d mydomain.com \
        --fullchain-file /home/banskt/local/etc/nginx/ssl/mydomain.com/fullchain.pem \
        --key-file       /home/banskt/local/etc/nginx/ssl/mydomain.com/privkey.pem \
        --reloadcmd ""
#
acme.sh --install-cert -d foo.mydomain.com \
        --fullchain-file /home/banskt/local/etc/nginx/ssl/foo.mydomain.com/fullchain.pem \
        --key-file       /home/banskt/local/etc/nginx/ssl/foo.mydomain.com/privkey.pem \
        --reloadcmd "sudo systemctl restart nginx"
acme.sh --help
acme.sh --list

Improve speed and performance

I modified my nginx.conf to improve speed, performance and security.

I also setup custom locations for the worker process in home/banskt/local/etc/nginx and setup proper access.

mkdir -p /home/banskt/local/etc/nginx/worker
sudo chown -R minion:minion /home/banskt/local/etc/nginx/worker
# use ssh port to login for user minion
ssh minion@localhost -p xxxxx 
cd /home/banskt/local/etc/nginx
sudo mkdir worker && cd worker
mkdir client_body fastcgi uwscgi scgi proxy logs
chmod -R 700 client_body/ fastcgi/ proxy/ scgi/ uwscgi/
cd logs
touch access.log error.log
chmod 644 access.log error.log
cd ..
# exit and change owner of logs to root
exit
sudo chown -R root:minion /home/banskt/local/etc/nginx/worker/logs

Specify the new directories in nginx.conf and /lib/systemd/system/nginx.service. The PID file location must be same in both. After changing systemd file, we need to reload daemon.

sudo systemctl stop nginx
sudo systemctl daemon-reload
sudo systemctl reset-failed
sudo systemctl start nginx

Bug: nginx -t still tries to open the default error log file with which it was compiled. It throws an alert if it does not find the file. I do not like alerts. Therefore, I created a dummy file at the compilation location.

Update: My nginx configuration files are now backed up here.

Install MySQL

Installing MySQL from source is mildly convoluted and risk non-optimal settings leading to reduced functionality, performance, or security (see here). So, I installed a generic binary as explained here.

wget https://dev.mysql.com/get/Downloads/MySQL-8.1/mysql-8.1.0-linux-glibc2.28-x86_64.tar.xz --no-check-certificate
sudo mkdir -p /opt/mysql/
sudo tar xf mysql-8.1.0-linux-glibc2.28-x86_64.tar.xz -C /opt/mysql/
sudo ln -s /opt/mysql/mysql-8.1.0-linux-glibc2.28-x86_64 /opt/mysql/8.1.0
cd /opt/mysql/8.1.0
sudo chown -R root:root *
sudo mkdir etc

Post-install setup and testing

  • Create an initial configuration file in /opt/mysql/8.1.0/etc/my.cnf.
[mysqld]
basedir=/opt/mysql/mysql-8.1.0-linux-glibc2.28-x86_64
datadir=/opt/mysql/mysql-8.1.0-linux-glibc2.28-x86_64/data

# Enable error log
[mysqld_safe]
log_error=/home/banskt/local/etc/mysql/logs/mysql_error.log

[mysqld]
log_error=/home/banskt/local/etc/mysql/logs/mysql_error.log

# General query log
general_log_file = /home/banskt/local/etc/mysql/logs/mysql.log
general_log      = 1

# Slow Query Log
slow_query_log = 1
slow_query_log_file = /home/banskt/local/etc/mysql/logs/mysql_slow.log
long_query_time  = 2
log_queries_not_using_indexes = 1

# 
secure_file_priv = /home/banskt/local/etc/mysql/mysql-files
cd /home/banskt/local/etc/
mkdir mysql && cd mysql
mkdir logs mysql-files
sudo chown minion:minion logs mysql-files
sudo chmod 750 logs mysql-files

sudo /opt/mysql/8.1.0/bin/mysqld --defaults-file=/opt/mysql/8.1.0/etc/my.cnf --initialize --user=minion 
# Note the temporary password created in the log file logs/mysql_error.log
  • Create systemd file /lib/systemd/system/mysql.service.
[Unit]
Description=MySQL Community Server
Documentation=man:mysqld(8)
Documentation=http://dev.mysql.com/doc/refman/en/using-systemd.html
After=network.target
After=syslog.target

[Install]
WantedBy=multi-user.target

[Service]
User=minion
Group=minion

# Have mysqld write its state to the systemd notify socket
Type=notify
# PermissionsStartOnly=true
# ExecStartPre=/mysql-systemd-start pre

# Start main service
ExecStart=/opt/mysql/mysql-8.1.0-linux-glibc2.28-x86_64/bin/mysqld --defaults-file=/opt/mysql/mysql-8.1.0-linux-glibc2.28-x86_64/etc/my.cnf $MYSQLD_OPTS

# Disable service start and stop timeout logic of systemd for mysqld service.
TimeoutSec=0

# Sets open_files_limit
LimitNOFILE = 10000
Restart=on-failure
RestartPreventExitStatus=1

# Always restart when mysqld exits with exit code of 16. This special exit code
# is used by mysqld for RESTART SQL.
RestartForceExitStatus=16

# Set enviroment variable MYSQLD_PARENT_PID. This is required for restart.
Environment=MYSQLD_PARENT_PID=1

There are multiple ways to specify environment variable values for use by the MySQL server process managed by systemd. To specify options for mysqld without modifying systemd configuration files directly, set or unset the MYSQLD_OPTS systemd variable. For example:

systemctl set-environment MYSQLD_OPTS="--general_log=1"
systemctl unset-environment MYSQLD_OPTS

MYSQLD_OPTS can also be set in the /etc/sysconfig/mysql file.

  • Start the server and secure installation.
sudo systemctl daemon-reload
sudo systemctl reset-failed
sudo systemctl start mysql
sudo systemctl status mysql
/opt/mysql/8.1.0/bin/mysql -u root -p
# Enter password from the error log
mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'rootpassword';
mysql> FLUSH PRIVILEGES;
mysql> SELECT user,authentication_string,plugin,host FROM mysql.user;
mysql> exit;

Install PHP from source

  • Download latest package and untar.
wget https://www.php.net/distributions/php-8.2.10.tar.gz
tar -zxf php-8.2.10.tar.gz
  • Install prerequisites
sudo apt install pkg-config libxml2-dev libxmlrpc-epi-dev libsqlite3-dev libcurl4-openssl-dev libjpeg-dev libpng-dev libonig-dev libxslt-dev libzip-dev
sudo apt install autoconf
  • Create configuration bash script
#!/bin/bash
module load openssl/3.1.3
export OPENSSL_CFLAGS='-I/opt/openssl/3.1.3/include -L/opt/openssl/3.1.3/lib64 -lssl -lcrypto -Wl,-rpath,/opt/openssl/3.1.3/lib64'
export OPENSSL_LIBS='/opt/openssl/3.1.3/lib64'

./configure \
  --prefix=/opt/php/8.2.10 \
  --enable-mbstring \
  --with-curl \
  --with-openssl \
  --enable-opcache \
  --with-zip \
  --enable-gd \
  --with-jpeg \
  --enable-fpm \
  --enable-cli \
  --enable-xml \
  --with-xmlrpc \
  --enable-intl \
  --with-xsl \
  --with-zlib \
  --with-mysql=mysqlnd \
  --with-pdo-mysql=mysqlnd \
  --with-mysqli=mysqlnd
  • Configure and build. Compilation takes a long time, better to use tmux or screen.
source config.sh
make 
make test
sudo make install
  • Setup a development environment.
cd /opt/php/8.2.10/var/run/ && sudo mkdir php-fpm
cd -
sudo cp php.ini-development /opt/php/8.2.10/lib/php.ini
sudo cp sapi/fpm/php-fpm.conf /opt/php/8.2.10/etc/php-fpm.conf
sudo cp sapi/fpm/www.conf /opt/php/8.2.10/etc/php-fpm.d/www.conf
sudo vim /opt/php/8.2.10/etc/php-fpm.d/www.conf
# edit the following:
# user = minion
# group = minion
# listen.owner = minion
# listen.group = minion
# listen = /opt/php/8.2.10/var/run/php-fpm/php-fpm.sock
sudo mkdir -p /opt/php/8.2.10/etc/conf.d
sudo vim /opt/php/8.2.10/etc/conf.d/modules.ini
# zend_extension=opcache.so

To-Do: Change it to production environment.

  • Create systemd file /lib/systemd/system/php-fpm.service
[Unit]
Description=The PHP FastCGI Process Manager
After=syslog.target network.target

[Service]
Type=simple
PIDFile=/opt/php/8.2.10/var/run/php-fpm/php-fpm.pid
ExecStart=/opt/php/8.2.10/sbin/php-fpm --nodaemonize --fpm-config /opt/php/8.2.10/etc/php-fpm.conf
ExecReload=/bin/kill -USR2 $MAINPID

[Install]
WantedBy=multi-user.target
  • Start PHP. Check socket and version
sudo systemctl daemon-reload
sudo systemctl reset-failed
sudo systemctl start php-fpm
/opt/php/8.2.10/bin/php -v
ls -lh /opt/php/8.2.10/var/run/php-fpm/
  • Create modulefile /opt/modulefiles/php/8.2.10 and load module.
#%Module

proc ModulesHelp { } {
    global version prefix
    puts stderr "\tLoads PHP bin directory in PATH."
}

module-whatis   "loads PHP bin directory in PATH"

# for Tcl script use only
set version     8.2.10
set prefix      {/opt/php/8.2.10}

prepend-path    PATH            $prefix/bin
  • Install xmlrpc extension (required by rtorrent)
wget https://pecl.php.net/get/xmlrpc-1.0.0RC3.tgz --no-check-certificate
tar zxf xmlrpc-1.0.0RC3.tgz
cd xmlrpc-1.0.0RC3/
module load php/8.2.10
phpize
./configure
make

Move the extension to the PHP extension directory.

php -i | grep "extension_dir"
extension_dir => /opt/php/8.2.10/lib/php/extensions/no-debug-non-zts-20220829 => /opt/php/8.2.10/lib/php/extensions/no-debug-non-zts-20220829
sudo cp modules/xmlrpc.so /opt/php/8.2.10/lib/php/extensions/no-debug-non-zts-20220829/

Edit the php.ini file to include the line:

extension=xmlrpc.so

Restart the php-fpm service. To verify the installation, you can use something like this (should at least return the line “xmlrpc”):

php -i | grep xmlrpc | grep -v "xmlrpc_error"
  • Configure the nginx server to use the php-fpm socket.