Часто настраиваю сервера под MODX, устал держать в памяти шаги. Обычно поднимая сервер по памяти, забудешь какую-нибудь мелочь, с которой провозишься 3 часа. Пусть это руководство будет шпаргалкой. А я, по мере использования, буду его дополнять.

Поехали!
apt install sudo
sudo apt update
sudo apt install -y curl
sudo apt install -y software-properties-common
sudo apt install -y python-software-properties
sudo apt install -y language-pack-ru-base
sudo echo "ru_RU.CP1251 CP1251" >>/var/lib/locales/supported.d/ru
sudo locale-gen
sudo locale-gen en_US.UTF-8

Ставим корректную локаль на сервере:
tee /etc/default/locale <<'EOF'
LANGUAGE=en_US.UTF-8
LC_ALL=en_US.UTF-8
LANG=en_US.UTF-8
LC_TYPE=en_US.UTF-8
EOF
После этого перезаходим на сервер по SSH, чтобы изменения применились.

Ставим ноду и галп:
curl -sL https://deb.nodesource.com/setup_11.x | sudo -E bash -
sudo apt-get install -y nodejs
npm install --global gulp
Тут подробнее про установку на Ubuntu.

Git, zip, cron и т.д.:
sudo apt install -y git zip unzip mc nano htop cron python3-pip libyaml-dev

Зависимости для Python приложений:
sudo apt install -y python-ipaddr
sudo pip3 install pyyaml
sudo pip3 install pymysql

Возможно, при установке модулей через pip3, появится ошибка ImportError: No module named 'setuptools'. Решить её довольно просто:
pip3 install --upgrade pip
pip install --upgrade setuptools
И снова запустить установку модуля, на котором спотыкнулись.

Добавляем новую группу юзеров sftp и правим конфиг /etc/ssh/sshd_config:
sudo addgroup sftp; \
file_ssh_config=/etc/ssh/sshd_config; \
sudo sed -ri 's/^(Subsystem sftp \/usr\/lib\/openssh\/sftp-server)$/#\1/' $file_ssh_config; \
if ! grep -q "Subsystem sftp internal-sftp" $file_ssh_config; then \
    echo "" | sudo tee -a $file_ssh_config; \
    echo "Subsystem sftp internal-sftp" | sudo tee -a $file_ssh_config; \
    echo "Match Group sftp" | sudo tee -a $file_ssh_config; \
    echo "    ChrootDirectory %h" | sudo tee -a $file_ssh_config; \
    echo "    AllowTCPForwarding no" | sudo tee -a $file_ssh_config; \
    echo "    ForceCommand internal-sftp" | sudo tee -a $file_ssh_config; \
fi; \
sudo service ssh restart

Nginx, MySQL, PHP

Ставим репозиторий новых версий PHP:
sudo add-apt-repository ppa:ondrej/php
sudo apt update

Возможно, возникнет ошибка при попытке обновить список пакетов командой apt update, вроде этой:
Err:4 http://ppa.launchpad.net/ondrej/php/ubuntu xenial InRelease The following signatures couldn't be verified because the public key is not available: NO_PUBKEY [...]
Вместо 3 точек будет публичный ключ, копируем его и вставляем в конец команды, вместо трёх точек:
sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com ...
После этого снова apt update.

Устанавливаем:
sudo apt install -y nginx mysql-server \
&& sudo apt install -y php7.0-fpm php7.0-mcrypt php7.0-mysql php7.0-curl php-db php7.0-gd php7.0-xml php7.0-json php7.0-imap php7.0-dev php7.0-zip php7.0-yaml php7.0-mbstring \
&& sudo apt install -y php7.1-fpm php7.1-mcrypt php7.1-mysql php7.1-curl php7.1-gd php7.1-xml php7.1-json php7.1-imap php7.1-dev php7.1-zip php7.1-yaml php7.1-mbstring \
&& sudo apt install -y php7.2-fpm php7.2-mysql php7.2-curl php7.2-gd php7.2-xml php7.2-json php7.2-imap php7.2-dev php7.2-zip php7.2-yaml php7.2-mbstring \
&& sudo apt install -y php7.3-fpm php7.3-mysql php7.3-curl php7.3-gd php7.3-xml php7.3-json php7.3-imap php7.3-dev php7.3-zip php7.3-yaml php7.3-mbstring

Настраиваем:
sudo apt update && sudo apt upgrade
sudo service php7.0-fpm restart && sudo service php7.1-fpm restart && sudo service php7.2-fpm restart && sudo service php7.3-fpm restart
sudo nginx -t
sudo service nginx reload
sudo mkdir /etc/nginx/conf.inc && sudo mkdir /etc/nginx/conf.inc/main && sudo mkdir /etc/nginx/conf.inc/access && sudo mkdir /etc/nginx/conf.inc/domains && sudo touch /etc/nginx/blockips.conf

Возможно, придётся установить YAML иначе:
sudo apt install -y php-pear
sudo pecl install yaml

Если нам нужен PHP 5.6:
sudo apt install -y php5.6-fpm php5.6-mcrypt php5.6-mysql php5.6-curl php5.6-gd php5.6-xml php5.6-json php5.6-imap php5.6-dev php5.6-zip php5.6-yaml php5.6-mbstring

В файле /etc/nginx/nginx.conf заменить всё на:
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
    worker_connections 768;
    #multi_accept on;
}

http {
    # >> IP blocks
    include blockips.conf;
    # << IP blocks

    # >> Basic Settings
    merge_slashes off;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    client_max_body_size 100M;
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    # << Basic Settings

    # >> SSL Settings
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
    ssl_prefer_server_ciphers on;
    # << SSL Settings

    # >> Logging Settings
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;
    # << Logging Settings

    ##
    # Gzip Settings
    ##
    gzip on;
    gzip_disable "msie6";
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_http_version 1.0;
    gzip_types text/plain text/css application/json application/x-javascript application/javascript text/xml application/xml application/xml+rss text/javascript;

    # >> Virtual Host Configs
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
    # << Virtual Host Configs
}

Командуем:
sudo nginx -t
sudo service nginx reload

В файлах /etc/php/{version}/cli/php.ini и /etc/php/{version}/fpm/php.ini раскомментировать параметр date.timezone и установить значение:
php_timezone='Europe\/Moscow'; \
for version in `ls -F1 /etc/php/ | grep -e ./ | tr -d \/`; do \
    if [ -f "/etc/php/${version}/fpm/php.ini" ]; then \
        sed -ri 's/^;?(date\.timezone =).*$/\1 '${php_timezone}'/' /etc/php/$version/fpm/php.ini; \
    fi; \
    if [ -f "/etc/php/${version}/cli/php.ini" ]; then \
        sed -ri 's/^;?(date\.timezone =).*$/\1 '${php_timezone}'/' /etc/php/$version/cli/php.ini; \
    fi; \
done

Также, возможно придется добавить модуль yaml в php конфиг, если в папке ./fpm/conf.d и ./cli/conf.d нет файла 20-yaml.ini. Для этого добавим строку рядом с другими подобными:
extension=yaml.so

Командуем:
sudo service php7.0-fpm restart && sudo service php7.1-fpm restart && sudo service php7.2-fpm restart && sudo service php7.3-fpm restart

Есть вероятность, что SQL MODE будет не тот. От этого MODX будет сыпать ошибки в собственный лог и ТВ не будут сохраняться. Наверняка последствий больше, однако лечится это так: sudo mcedit /etc/mysql/my.cnf и в конец дописываем:
[mysqld]
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

Командуем:
sudo systemctl restart mysql

Composer глобально:
curl -sS https://getcomposer.org/installer -o composer-setup.php
sudo php composer-setup.php --install-dir=/usr/local/bin --filename=composer

Даймон для хост-панельки

Качаем, располагаем в директории /root/scripts/py/modxpaneldaemon/, создаем папки log и tmp в директории даймона:
mkdir -p /root/scripts/py/ \
&& curl -L -o modxpaneldaemon.zip "https://github.com/gvozdb/modxPanelDaemon/archive/master.zip" \
&& unzip modxpaneldaemon.zip -d "/root/scripts/py/" \
&& mv /root/scripts/py/modxPanelDaemon-master /root/scripts/py/modxpaneldaemon \
&& rm -f modxpaneldaemon.zip \
&& mkdir /root/scripts/py/modxpaneldaemon/log/ \
&& mkdir /root/scripts/py/modxpaneldaemon/tmp/

После, настраиваем config.yaml (обязательно заменить secret_key).

Выполняем команду для создания сайта хост-панели:
/bin/bash /root/scripts/py/modxpaneldaemon/script/sh/addmodx.sh -p "пароль_mysql" -h h1.you_main_domain.ru -u host -d h1.you_main_domain.ru

Чтобы даймон стал сервисом (/etc/init.d/modxpanel start|stop|restart или service modxpanel start|stop|restart), надо создать файл /etc/init.d/modxpanel:
#!/bin/bash

prog=modxpanel
RETVAL=0

start() {
    echo Starting $prog:
    /usr/bin/env python3 /root/scripts/py/modxpaneldaemon/modxpaneldaemon.py start
    RETVAL=$?
    return $RETVAL
}

stop() {
    echo Stopping $prog:
    /usr/bin/env python3 /root/scripts/py/modxpaneldaemon/modxpaneldaemon.py stop
    RETVAL=$?
    return $RETVAL
}

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        stop
        start
        ;;
    *)
        echo "Usage: $prog {start|stop|restart}"
        exit 1
    ;;
esac

exit 0

Сделать его исполняемым:
sudo chmod +x /etc/init.d/modxpanel

И запустить сервис:
/etc/init.d/modxpanel start

Хост-панель

Качаем и устанавливаем в систему сайта h1.you_main_domain.ru.
В системных настройках обязательно указать secret_key даймона, порт и другое.
Теперь можно создавать MODX сайты через неё!

Короткая команда chmod

В корневой директории каждого юзера есть файл chmod, который ставит права на файлы и папки. Периодически, после ковыряния рутом надо запускать его, чтобы не было проблем с правами. Можно запускать вручную так:
sh /var/www/{username}/chmod
А можно сократить до безобразия. Для этого идём в /root/.bashrc и в конец добавляем:
# short run chmod for user
ch() {
    sh /var/www/$1/chmod
}
Теперь запустить сброс прав на файлы и папки куда быстрее:
ch {username}

Docker и Sprut.io

Ставим Docker:
cd /tmp
wget -qO- https://get.docker.com/ | sh

Проверяем, что Docker установился:
sudo docker run hello-world

Ставим Sprut.io — sprut.io/ru/install

Бекапы с Gvozdb/Dumper

Здесь описано подробно, как настраивать и пользоваться библиотекой.

SSL сертификаты

Здесь подробно про SSL сертификаты от Let's encrypt.