Установка Nextcloud + Nginx + PHP-FPM на Ubuntu

Nextcloud - открытый проект приватного облака для хранения и обработки файлов.

Можно установить на локальном домашнем сервере или на арендованом сервере. В этой инструкции рассматривается пример установки на виртуальный выделенный сервер с системой Ubuntu 24.04.

Подготовка системы

Обновляем и устанавливаем необходимые пакеты:

apt update && \
apt -y upgrade && \
apt -y dist-upgrade && \
apt -y autoremove && \
apt -y install unzip && \ ## Утилита распаковки zip архивов
apt -y install redis-server && \ ## Устанавливаем Redis Server
apt -y install postgresql-16 && \ ## Сервер баз данных
apt -y install nginx && \ ## Веб сервер
apt -y install certbot ## Утилита выпуска SSL сетификатов LetsEncrypt

Выполним предварительную настройку, прежде чем начать развертывание Nextcloud.

Выставляем нужный часовой пояс:

timedatectl set-timezone Europe/Moscow

Установка и настройка PHP-FPM

Перед установкой php, проверить системные требования Nextcloud. Для корректной работы нужно установить рекомендуемую версию PHP.

На момент обновления инструкции, рекомендуется установить PHP версии 8.3. Для дальнейшего удобства работы, мы сохраним ее в переменную:

export PHP_VERSION=8.3

Устанавливаем пакеты, которые будут необходимы для работы Nextcloud:

apt -y install php${PHP_VERSION}-fpm php${PHP_VERSION}-common php${PHP_VERSION}-zip php${PHP_VERSION}-xml php${PHP_VERSION}-intl php${PHP_VERSION}-gd php${PHP_VERSION}-pgsql php${PHP_VERSION}-apcu php${PHP_VERSION}-redis php${PHP_VERSION}-mbstring php${PHP_VERSION}-curl php${PHP_VERSION}-imagick php${PHP_VERSION}-gmp php${PHP_VERSION}-bcmath libmagickcore-6.q16-6-extra

После установки, открываем конфигурационный файл php-fpm:

nano /etc/php/${PHP_VERSION}/fpm/pool.d/www.conf

путь к данной папке зависит от установленной версии php. В данном примере через переменную PHP_VERSION задана 8.3.

В редакторе nano используй сочетания клавиш:

  • Ctrl+W - поиск
  • Ctrl+O - сохранить файл
  • Ctrl+X - закрыть редактор

Раскомментируем строку с параметром удалив ;

env[PATH] = /usr/local/bin:/usr/bin:/bin

Настраиваем php.ini:

nano /etc/php/${PHP_VERSION}/fpm/php.ini

Снимаем комментарии со следующей строки:

opcache.enable_cli = 1  
opcache.interned_strings_buffer = 32  
opcache.revalidate_freq = 1
memory_limit = 1024M

Включаем локальное кэширование в файле:

nano /etc/php/${PHP_VERSION}/mods-available/apcu.ini

Добавляем строку:

apc.enable_cli=1

Разрешаем автозапуск php-fpm и перезапускаем его:

systemctl enable php${PHP_VERSION}-fpm

systemctl restart php${PHP_VERSION}-fpm

Выпускаем LetsEncrypt сертификат

Для безопасной работы с облаком нам необходим SSL сертификат. Воспользуемся бесплатным сервисом LetsEncrypt.

Выпускаем сертификат:

systemctl stop nginx ## Для получения сертификата останавливаем сервер nginx
certbot certonly -d nextcloud.domain.tld -m mymail@mail.ru ## Выпускаем SSL сертификат утилитой Certbot

Команда создаст сертификат на 3 месяца для домена nextcloud.domain.tld.

Для того, что бы сертификат выпускался регулярно и не приходилось делать это вручную, создадим systemd таймер и сервис.

nano /etc/systemd/system/certbot-renewal.service

Содержимое файла certbot-renewal.service:

[Unit]
Description=Certbot Renewal

[Service]
ExecStart=/usr/bin/certbot renew --force-renewal --post-hook "systemctl reload nginx.service"

Создадим новый файл certbot-renewal.timer в той же директории, где находится certbot-renewal.service. Настройки ниже будут запускать обновление SSL-сертификата раз в неделю, не раньше чем через 300 секунд с момента включения сервера:

nano /etc/systemd/system/certbot-renewal.timer

Содержимое файла certbot-renewal.timer:

[Unit]
Description=Timer for Certbot Renewal

[Timer]
OnBootSec=300
OnUnitActiveSec=1w

[Install]
WantedBy=multi-user.target

Включим таймер, чтобы он начал свой отсчёт:

systemctl start certbot-renewal.timer

Настроим автостарт таймера на случай перезапуска сервера:

systemctl enable certbot-renewal.timer

Заглянем в статус таймера, чтобы узнать когда было последнее обновление SSL-сертификата и когда планируется следующее:

systemctl status certbot-renewal.timer

● certbot-renewal.timer - Timer for Certbot Renewal
     Loaded: loaded (/etc/systemd/system/certbot-renewal.timer; enabled; preset: enabled)
     Active: active (waiting) since Thu 2024-07-25 13:40:36 UTC; 19h ago
    Trigger: Thu 2024-08-01 13:40:36 UTC; 6 days left
   Triggers: ● certbot-renewal.service

Установка и настройка NGINX

Создаем конфиг nextcloud для веб сервера nginx:

nano /etc/nginx/sites-available/nextcloud.conf
upstream php-handler {
    server unix:/run/php/php8.3-fpm.sock; # Путь до  файла сокета зависит от версии PHP
}

map $arg_v $asset_immutable {
    "" "";
    default ", immutable";
}

server {
    listen 80;
    listen [::]:80;
    server_name nextcloud.domain.tld; # доменное имя для nextcloud
    server_tokens off;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443      ssl http2;
    listen [::]:443 ssl http2;
    server_name nextcloud.domain.tld;
    root /opt/nextcloud; # каталог файлов nextcloud

    ssl_certificate /etc/letsencrypt/live/nextcloud.domain.tld/fullchain.pem; # каталог для сертификатов LetsEncript; 
    ssl_certificate_key /etc/letsencrypt/live/nextcloud.domain.tld/privkey.pem;

    server_tokens off;

    client_max_body_size 10G;
    client_body_timeout 300s;
    fastcgi_buffers 64 4K;

    gzip on;
    gzip_vary on;
    gzip_comp_level 4;
    gzip_min_length 256;
    gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
    gzip_types application/atom+xml text/javascript application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/wasm application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;

    client_body_buffer_size 512k;

    add_header Strict-Transport-Security "max-age=31536000;includeSubDomains" always;
    add_header Referrer-Policy                   "no-referrer"       always;
    add_header X-Content-Type-Options            "nosniff"           always;
    add_header X-Frame-Options                   "SAMEORIGIN"        always;
    add_header X-Permitted-Cross-Domain-Policies "none"              always;
    add_header X-Robots-Tag                      "noindex, nofollow" always;
    add_header X-XSS-Protection                  "1; mode=block"     always;

    fastcgi_hide_header X-Powered-By;

    include mime.types;
    types {
        text/javascript js mjs;
    }

    index index.php index.html /index.php$request_uri;

    location = / {
        if ( $http_user_agent ~ ^DavClnt ) {
            return 302 /remote.php/webdav/$is_args$args;
        }
    }

    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    }

    location ^~ /.well-known {
        location = /.well-known/carddav { return 301 /remote.php/dav/; }
        location = /.well-known/caldav  { return 301 /remote.php/dav/; }

        location /.well-known/acme-challenge    { try_files $uri $uri/ =404; }
        location /.well-known/pki-validation    { try_files $uri $uri/ =404; }

        return 301 /index.php$request_uri;
    }

    location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/)  { return 404; }
    location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console)                { return 404; }

    location ~ \.php(?:$|/) {
        # Required for legacy support
        rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|ocs-provider\/.+|.+\/richdocumentscode(_arm64)?\/proxy) /index.php$request_uri;

        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        set $path_info $fastcgi_path_info;

        try_files $fastcgi_script_name =404;

        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $path_info;
        fastcgi_param HTTPS on;

        fastcgi_param modHeadersAvailable true;         # Avoid sending the security headers twice
        fastcgi_param front_controller_active true;     # Enable pretty urls
        fastcgi_pass php-handler;

        fastcgi_intercept_errors on;
        fastcgi_request_buffering off;

        fastcgi_max_temp_file_size 0;
    }


    location ~ \.(?:css|js|mjs|svg|gif|ico|jpg|png|webp|wasm|tflite|map|ogg|flac)$ {
        try_files $uri /index.php$request_uri;

        add_header Cache-Control                     "public, max-age=15778463$asset_immutable";
        add_header Referrer-Policy                   "no-referrer"       always;
        add_header X-Content-Type-Options            "nosniff"           always;
        add_header X-Frame-Options                   "SAMEORIGIN"        always;
        add_header X-Permitted-Cross-Domain-Policies "none"              always;
        add_header X-Robots-Tag                      "noindex, nofollow" always;
        add_header X-XSS-Protection                  "1; mode=block"     always;
        access_log off;
    }

    location ~ \.woff2?$ {
        try_files $uri /index.php$request_uri;
        expires 7d;
        access_log off;
    }


    location /remote {
        return 301 /remote.php$request_uri;
    }

    location / {
        try_files $uri $uri/ /index.php$request_uri;
    }
}

Поменяте nextcloud.domain.tld в конфигурации на свой домен.

Включаем запуск конфига сайта с помощью добавления символьной ссылки:

ln -s /etc/nginx/sites-available/nextcloud.conf /etc/nginx/sites-enabled/nextcloud.conf

Проверяем конфигурацию nginx:

nginx -t

Перезагружаем конфигурацию и включаем автозапуск сервиса nginx:

systemctl restart nginx

systemctl enable nginx

Настройка сервера баз данных

В качестве СУБД будем использовать Postgresql, который установили ранее.

Разрешаем автозапуск и стартуем сервис:

systemctl enable postgresql

systemctl start postgresql

Переключаемся на пользователя от которого запускается сервер баз данных Postgres:

sudo su postgres

Запускаем интерактивныую утилиту запросов к базе данных

psql

Создаем пользователя и базу данных:

CREATE ROLE nextcloud_database_user WITH LOGIN;
ALTER USER nextcloud_database_user WITH PASSWORD 'password';
CREATE DATABASE nextcloud OWNER nextcloud_database_user;
quit;

Записываем имя пользователя, пароль и название базы данных в файл. Они потребуются для дальнейшей установки.

Установка Nextcloud

Переходим в папку где у нас будет установлен Nextcloud и скачиваем архив с последним релизом:

cd /opt

wget https://download.nextcloud.com/server/releases/latest.zip

Распаковываем скачанный архив:

unzip latest.zip

Удаляем архив:

rm /opt/latest.zip

Задаем права доступа для пользователя:

chown -R www-data:www-data /opt/nextcloud

Открываем браузер и переходим по адресу https://nextcloud.domain.tld, где расположен наш портал.

Вводим логин, пароль и название базы данных, которые указали при создании и записали в файл.

Вводим параметры для установки Nextcloud

Нажимаем Установить и ждем. После установки откроется панель управления порталом.

Оптимизируем работу базы данных:

sudo -u www-data php /opt/nextcloud/occ db:convert-filecache-bigint

sudo -u www-data php /opt/nextcloud/occ db:add-missing-indices

Настройка Nextcloud

После установки Nexcloud необходимо настроить его для работы. Нужно залогиниться под данными администратора системы, которые мы указывали при установке. Открываем меню и переходим в Параметры сервера

Переходим в параметры сервера Nextcloud

Затем переходим в раздел Основные сведения. В разделе Проверка безопасности и параметров увидим рекомендации по настройке:

В разделе «Проверка безопасности и параметров» мы можем увидеть список проблем:

«Проверка безопасности и параметров»

Настройка выполнения фоновых задач

Отключаем параметр выставления окна времени технического обслуживания:

sudo -u www-data php /opt/nextcloud/occ config:system:set maintenance_window_start --type=integer --value=100

Далее нам надо создать повторяющийся таймер запуска cron.php для выполнения задач. В качестве альтернативы cronjob будем использовать таймер systemd, так как он надежнее.

Этот подход требует наличия двух файлов: nextcloudcron.service и nextcloudcron.timer. Создайте эти два файла в каталоге /etc/systemd/system/

nano /etc/systemd/system/nextcloudcron.service

nextcloudcron.service должен выглядеть следующим образом:

[Unit]
Description=Nextcloud cron.php job

[Service]
User=www-data
ExecCondition=/usr/bin/php -f /opt/nextcloud/occ status -e
ExecStart=/usr/bin/php -f /opt/nextcloud/cron.php
KillMode=process

ExecCondition перед запуском фонового задания проверяет, что nextcloud работает нормально и пропускает его в противном случае.

Параметр KillMode=process необходим для того, чтобы внешние программы, запускаемые заданием cron, продолжали работать после завершения задания cron.

nano /etc/systemd/system/nextcloudcron.timer

nextcloudcron.timer должен выглядеть следующим образом:

[Unit]
Description=Запуск Nextcloud cron.php каждые 30 минут

[Timer]
OnBootSec=30min
OnUnitActiveSec=30min
Unit=nextcloudcron.service

[Install]
WantedBy=timers.target

OnBootSec запускает таймер через 30 минут после загрузки, иначе пришлось запускать его вручную. OnUnitActiveSec 30 минутный таймер между запусками.

Теперь осталось только запустить systemd unit и включить таймер, выполнив эту команду:

systemctl enable --now nextcloudcron.timer

Настройка системы кеширования Redis

Открываем конфигурационный файл для nextcloud:

nano /opt/nextcloud/config/config.php

И добавим:

  'memcache.local' => '\OC\Memcache\APCu',
  'memcache.locking' => '\OC\Memcache\Redis',
  'redis' => array(
      'host' => 'localhost',
      'port' => 6379,
      'timeout' => 0.0,
      ),

Готово.

Настраиваем регион по умолчанию

Для решения проблемы с форматом телефонного номера необходимо добавить параметр в конфиг:

nano /opt/nextcloud/config/config.php

Добавляем:

...  
'default_phone_region' => 'RU',

Защита от перебора паролей

Данная возможность позволяет блокировать подключения от IP-адресов, с которых было много неудачных попыток войти в систему. Рассмотрим возможность изменения числа неудачных попыток или отключения возможности.

Текущее значение для числа попыток ввода пароля можно посмотреть командой:

sudo -u www-data php /opt/nextcloud/occ config:system:get brute-force-attempts

Если она вернет пустое значение, значит используется значение по умолчанию — 10.

Чтобы его изменить, запускаем команду, где 5 новое значение:

sudo -u www-data php /opt/nextcloud/occ config:system:set brute-force-attempts --type=integer --value=5