Содержание

NextCloud + OnlyOffice

Предыдущая статья, к сожалению, на fpm работает не так стабильно, вернулся к классике на Apache (за Nginx)

Домашняя хранилка у меня представляет малюсенький ПК (Terramaster f4-423) с 4 дисками в RAID-10,
большинство ресурсов доступны только из локальной сети, но вот появилась необходимость редактировать документики (таблички)
и синхронизировать папки через приложение, это немного не сетевая папка, а именно синхронизация, когда нет интернета папка доступна, а когда интернет появляется, то она синхронизируется и обновляется.

Общая архитектура

про установку certbot
Установка mariadb
Установка докера
Инструкция по раскатке на докер-хабе

Инструкция создана частичнео при использовании ИИ,
но, приходилось читать документации и скармливать примеры настроек…

Создадим конфиг

Мы применяем очень интересную технологию, когда у вас есть .env файл в котором прописаны все домены, порты, пароли…
И все команды задействуют эти переменные, самое главное это docker-compose, который не содержит секретов, а дергает из переменных при раскатке.
При таком подходе можно смело просить что-то переделать ИИ-ботов, не отправляя им секретных данных, \\ну и писать подобные статьи с листингами, не боясь опубликовать что-то секретное!!!


  1. ENV="/storage/DockerApps/nextCloud/.env"
  2. mkdir -p /storage/DockerApps/nextCloud
  3. touch $ENV
  4. chmod 600 $ENV
  5.  
  6. # --- Сеть ---
  7. grep -q "^DB_HOST=" $ENV || echo 'DB_HOST="172.22.0.1"' >> $ENV
  8. grep -q "^NETWORK_NAME=" $ENV || echo 'NETWORK_NAME="nextcloud_net"' >> $ENV
  9. grep -q "^NETWORK_SUBNET=" $ENV || echo 'NETWORK_SUBNET="172.22.0.0/16"' >> $ENV
  10. grep -q "^DB_HOST_SUBNET=" $ENV || echo 'DB_HOST_SUBNET="172.22.%"' >> $ENV
  11.  
  12. # 🔴 Домен Nextcloud (уникальное имя)
  13. grep -q "^DOMAIN_NC=" $ENV || echo 'DOMAIN_NC="oblako.example.com"' >> $ENV
  14.  
  15. # 🔴 Домен OnlyOffice (уникальное имя)
  16. grep -q "^DOMAIN_OO=" $ENV || echo 'DOMAIN_OO="office.oblako.example.com"' >> $ENV
  17.  
  18. # --- Порт Apache в контейнере ---
  19. grep -q "^APACHE_HOST_PORT=" $ENV || echo 'APACHE_HOST_PORT="9104"' >> $ENV
  20.  
  21. # --- Порт OnlyOffice в контейнере ---
  22. grep -q "^OO_HOST_PORT=" $ENV || echo 'OO_HOST_PORT="8081"' >> $ENV
  23.  
  24. # --- База данных ---
  25. grep -q "^DB_NAME=" $ENV || echo 'DB_NAME="nextcloud"' >> $ENV
  26. grep -q "^DB_USER=" $ENV || echo 'DB_USER="nextcloud"' >> $ENV
  27. grep -q "^DB_PASS=" $ENV || echo "DB_PASS=\"$(pwgen -s 32 1)\"" >> $ENV
  28.  
  29. # --- Nextcloud ---
  30. grep -q "^NEXTCLOUD_ADMIN_USER=" $ENV || echo 'NEXTCLOUD_ADMIN_USER="admin"' >> $ENV
  31. grep -q "^NEXTCLOUD_ADMIN_PASSWORD=" $ENV || echo "NEXTCLOUD_ADMIN_PASSWORD=\"$(pwgen -s 24 1)\"" >> $ENV
  32.  
  33. # --- Redis ---
  34. grep -q "^REDIS_PASSWORD=" $ENV || echo "REDIS_PASSWORD=\"$(pwgen -s 32 1)\"" >> $ENV
  35.  
  36. # --- OnlyOffice ---
  37. grep -q "^ONLYOFFICE_JWT_SECRET=" $ENV || echo "ONLYOFFICE_JWT_SECRET=\"$(pwgen -s 32 1)\"" >> $ENV
  38.  
  39. echo "✅ .env файл создан и заполнен. Проверьте и отредактируйте его."

Далее откроем файл

/storage/DockerApps/nextCloud/.env

и отредактируем хосты/домены/юзеров…

Создаем подсеть

Если на данном этапе что-то не удалось, например подсеть занята, то идем к прошлому шагу и меняем всё что относится к подсетям
от этого зависит какого пользователя БД создавать и всё остальное.

  1. source /storage/DockerApps/nextCloud/.env # Убедитесь, что переменные экспортированы
  2. docker network create \
  3. --driver=bridge \
  4. --subnet=${NETWORK_SUBNET} \
  5. --gateway=172.22.0.1 \
  6. ${NETWORK_NAME}

Проверка

  1. docker network inspect ${NETWORK_NAME} --format='
  2. Сеть: {{.Name}}
  3. Драйвер: {{.Driver}}
  4. Подсеть: {{range .IPAM.Config}}{{.Subnet}}{{end}}
  5. Шлюз: {{range .IPAM.Config}}{{.Gateway}}{{end}}
  6. '

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

  1. source /storage/DockerApps/nextCloud/.env

Проверим что все переменные заполнились корректно

  1. echo "База: ${DB_NAME}"
  2. echo "Пользователь: ${DB_USER}"
  3. echo "Хост: ${DB_HOST}"
  4. echo "Подсеть: ${DB_HOST_SUBNET}"

Создадим базу, пользователя

  1. mariadb -e "CREATE DATABASE IF NOT EXISTS ${DB_NAME} CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;"
  2.  
  3. mariadb -e "CREATE USER IF NOT EXISTS '${DB_USER}'@'${DB_HOST_SUBNET}' IDENTIFIED BY '${DB_PASS}';"
  4.  
  5. mariadb -e "GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'${DB_HOST_SUBNET}';"
  6. mariadb -e "FLUSH PRIVILEGES;"
  7.  
  8. mariadb -e "SELECT User, Host FROM mysql.user WHERE User='${DB_USER}';"

Создадим контейнер NextCloud

Сперва создадим все каталоги (замените на своё), у меня в storage просто примонтирован raid-10 и там весь хлам…

  1. # Основной каталог приложения
  2. mkdir -p /storage/DockerApps/nextCloud/{nextcloud/{html,config},redis,onlyoffice/{logs,data,fonts}}
  3. mkdir -p /storage/nextCloud_data # Для данных пользователей
  4.  
  5. # Права доступа (www-data в контейнерах использует UID/GID 33)
  6. chown -R 33:33 /storage/nextCloud_data
  7. chmod -R 750 /storage/nextCloud_data
  8.  
  9. chown -R 33:33 /storage/DockerApps/nextCloud/nextcloud/html
  10. chmod -R 755 /storage/DockerApps/nextCloud/nextcloud/html
  11.  
  12. chown -R 33:33 /storage/DockerApps/nextCloud/nextcloud/config/
  13. chmod -R 750 /storage/DockerApps/nextCloud/nextcloud/config/
  14.  
  15. chown -R 33:33 /storage/DockerApps/nextCloud/onlyoffice
  16. chmod -R 750 /storage/DockerApps/nextCloud/onlyoffice
  17.  
  18. chown -R 33:33 /storage/DockerApps/nextCloud/redis
  19. chmod -R 750 /storage/DockerApps/nextCloud/redis

Проверим

  1. tree -L 3 /storage/DockerApps/nextCloud/

Получается что-то такое…

  1. /storage/DockerApps/nextCloud/
  2. ├── nextcloud
  3. │   ├── config
  4. │   └── html
  5. ├── onlyoffice
  6. │   ├── data
  7. │   ├── fonts
  8. │   └── logs
  9. └── redis

/storage/DockerApps/nextCloud/docker-compose.yml

  1. version: '3.8'
  2.  
  3. services:
  4. redis:
  5. image: redis:alpine
  6. container_name: nc_redis
  7. restart: always
  8. networks:
  9. - nextcloud_net
  10. command: redis-server --requirepass ${REDIS_PASSWORD}
  11. volumes:
  12. - ./redis:/data
  13.  
  14. nextcloud-apache:
  15. image: nextcloud:apache
  16. container_name: nc_apache
  17. restart: always
  18. depends_on:
  19. - redis
  20. networks:
  21. - nextcloud_net
  22. ports:
  23. - "127.0.0.1:${APACHE_HOST_PORT}:80"
  24. volumes:
  25. - ./nextcloud/html:/var/www/html
  26. - /storage/nextCloud_data:/var/www/html/data
  27. - ./nextcloud/config:/var/www/html/config
  28. # Монтируем конфигурацию Apache для доверия заголовкам X-Forwarded-Proto
  29. - ./apache-conf/remoteip.conf:/etc/apache2/conf-enabled/remoteip.conf
  30. environment:
  31. - MYSQL_HOST=${DB_HOST}
  32. - MYSQL_DATABASE=${DB_NAME}
  33. - MYSQL_USER=${DB_USER}
  34. - MYSQL_PASSWORD=${DB_PASS}
  35. - REDIS_HOST=redis
  36. - REDIS_HOST_PASSWORD=${REDIS_PASSWORD}
  37. - NEXTCLOUD_ADMIN_USER=${NEXTCLOUD_ADMIN_USER}
  38. - NEXTCLOUD_ADMIN_PASSWORD=${NEXTCLOUD_ADMIN_PASSWORD}
  39. - NEXTCLOUD_TRUSTED_DOMAINS=${DOMAIN_NC}
  40. - PHP_MEMORY_LIMIT=512M
  41. - PHP_UPLOAD_LIMIT=10G
  42.  
  43. onlyoffice-document-server:
  44. image: onlyoffice/documentserver:latest
  45. container_name: nc_onlyoffice
  46. restart: always
  47. networks:
  48. - nextcloud_net
  49. volumes:
  50. - ./onlyoffice/logs:/var/log/onlyoffice
  51. - ./onlyoffice/data:/var/www/onlyoffice/Data
  52. - ./onlyoffice/fonts:/usr/share/fonts/truetype/custom
  53. environment:
  54. - JWT_ENABLED=true
  55. - JWT_SECRET=${ONLYOFFICE_JWT_SECRET}
  56. - JWT_HEADER=AuthorizationJwt
  57. ports:
  58. - "127.0.0.1:${OO_HOST_PORT}:80"
  59.  
  60. networks:
  61. nextcloud_net:
  62. external: true
  63. name: ${NETWORK_NAME}

Далее конфиг для apache, чтобы он себя адекватно вел за nginx, не пытался куда то редиректить и говорить что мы не безопасно подключены…

  1. cd /storage/DockerApps/nextCloud
  2. mkdir apache-conf
  3. chown 33:33 apache-conf/
  4. chmod 755 apache-conf/
  5.  
  6. nano /storage/DockerApps/nextCloud/apache-conf/remoteip.conf

/storage/DockerApps/nextCloud/apache-conf/remoteip.conf

  1. # Настраиваем Apache на доверие заголовкам от Nginx
  2. RemoteIPHeader X-Forwarded-For
  3. # Укажи ТОЛЬКО корректные IP-адреса/подсети, с которых Nginx делает proxy_pass
  4. # Обычно это IP хоста в Docker-сети и/или localhost (если docker-proxy привязан к 127.0.0.1)
  5. RemoteIPTrustedProxy 127.0.0.1
  6. RemoteIPTrustedProxy 172.22.0.1
  7.  
  8. # Устанавливаем переменные окружения на основе заголовков
  9. SetEnvIf X-Forwarded-Proto "^https$" HTTPS=on
  10. SetEnvIf X-Forwarded-Proto "^https$" REQUEST_SCHEME=https
  11. SetEnvIf X-Forwarded-Port "^(.*)$" REQUEST_PORT=$1

Запускаем контейнеры

  1. cd /storage/DockerApps/nextCloud
  2. docker compose up -d

Ждем пока установится, наблюдаем за логами

  1. docker logs -f nc_apache

Вас должно встречать что-то такое, процесс длится минуту-две

  1. Configuring Redis as session handler
  2. Initializing nextcloud 33.0.0.16 ...
  3. New nextcloud instance
  4. Installing with MySQL database
  5. => Searching for hook scripts (*.sh) to run, located in the folder "/docker-entrypoint-hooks.d/pre-installation"
  6. ==> Skipped: the "pre-installation" folder is empty (or does not exist)
  7. Starting nextcloud installation

Если по какой то причине процесс не завершился, то при рестарте он не завершится и будут всякие проблемки, тут надо и nextCloud_data чистить (или переносить) и html и config папки или руками стартовать

Смотрим логи

  1. docker logs nc_apache
  2. docker logs nc_onlyoffice
  3. docker logs nc_redis

Решение проблем

Если установка не прошла, то ее можно руками запустить

  1. docker exec -u www-data nc_apache php occ maintenance:install \
  2. --database "mysql" \
  3. --database-host "${DB_HOST}" \
  4. --database-name "${DB_NAME}" \
  5. --database-user "${DB_USER}" \
  6. --database-pass "${DB_PASS}" \
  7. --admin-user "${NEXTCLOUD_ADMIN_USER}" \
  8. --admin-pass "${NEXTCLOUD_ADMIN_PASS}"

Узнать статус установки

  1. docker exec -u www-data nc_apache php occ status

Так проверить трастовый домен если не пускает…

  1. docker exec -u www-data nc_apache php occ config:system:get trusted_domains

А так можно добавить…

  1. docker exec -u www-data nc_apache php occ config:system:set trusted_domains 1 --value="${DOMAIN_NC}"

Настроим nginx

Генерим конфиги примерно так на onlyoffice пока не понял как спрятать домен, кажется что у них взаимодействие идет через фронт

  1. source /storage/DockerApps/nextCloud/.env
  2.  
  3. cat > /etc/nginx/sites-available/nextcloud << EOF
  4. # http -> https redirect
  5. server {
  6.   listen 80;
  7.   listen [::]:80;
  8.   server_name ${DOMAIN_NC};
  9.  
  10.   # Enforce HTTPS
  11.   return 301 https://\$server_name\$request_uri;
  12. }
  13.  
  14. server {
  15.   listen 443 ssl http2;
  16.   listen [::]:443 ssl http2;
  17.   server_name ${DOMAIN_NC};
  18.  
  19.   # SSL certificates (полученные через Certbot)
  20.   ssl_certificate /etc/letsencrypt/live/${DOMAIN_NC}/fullchain.pem;
  21.   ssl_certificate_key /etc/letsencrypt/live/${DOMAIN_NC}/privkey.pem;
  22.  
  23.   # Prevent nginx HTTP Server Detection
  24.   server_tokens off;
  25.  
  26.   # HSTS settings (опционально)
  27.   # add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
  28.  
  29.   # set max upload size and increase upload timeout:
  30.   client_max_body_size 512M;
  31.   client_body_timeout 300s;
  32.  
  33.   # Enable gzip
  34.   gzip on;
  35.   gzip_vary on;
  36.   gzip_comp_level 4;
  37.   gzip_min_length 256;
  38.   gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
  39.   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;
  40.  
  41.   # HTTP response headers borrowed from Nextcloud \`.htaccess\`
  42.   add_header Referrer-Policy "no-referrer" always;
  43.   add_header X-Content-Type-Options "nosniff" always;
  44.   add_header X-Frame-Options "SAMEORIGIN" always;
  45.   add_header X-Permitted-Cross-Domain-Policies "none" always;
  46.   add_header X-Robots-Tag "noindex, nofollow" always;
  47.  
  48.   location / {
  49.   proxy_pass http://127.0.0.1:${APACHE_HOST_PORT}; # Порт Apache в контейнере
  50.  
  51.   # Заголовки для правильной работы
  52.   proxy_set_header Host \$host;
  53.   proxy_set_header X-Real-IP \$remote_addr;
  54.   proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
  55.   proxy_set_header X-Forwarded-Proto \$scheme; # ВАЖНО для HTTPS
  56.   proxy_set_header X-Forwarded-Port \$server_port; # ВАЖНО для HTTPS
  57.   proxy_set_header X-Forwarded-Host \$server_name;
  58.  
  59.   # Таймауты
  60.   proxy_connect_timeout 600s;
  61.   proxy_send_timeout 600s;
  62.   proxy_read_timeout 600s;
  63.  
  64.   # Буферизация
  65.   proxy_buffering off;
  66.   proxy_request_buffering off;
  67.   }
  68. }
  69. EOF
  1. cat > /etc/nginx/sites-available/onlyoffice << EOF
  2. # http -> https redirect
  3. server {
  4.   listen 80;
  5.   listen [::]:80;
  6.   server_name ${DOMAIN_OO};
  7.  
  8.   return 301 https://\$server_name\$request_uri;
  9. }
  10.  
  11. # HTTPS server
  12. server {
  13.   listen 443 ssl http2;
  14.   listen [::]:443 ssl http2;
  15.   server_name ${DOMAIN_OO};
  16.  
  17.   # SSL certificates (полученные через Certbot)
  18.   ssl_certificate /etc/letsencrypt/live/${DOMAIN_OO}/fullchain.pem;
  19.   ssl_certificate_key /etc/letsencrypt/live/${DOMAIN_OO}/privkey.pem;
  20.  
  21.   # Отключение отображения версии nginx
  22.   server_tokens off;
  23.  
  24.   # ЕДИНСТВЕННЫЙ location блок, проксирующий всё в OnlyOffice
  25.   location / {
  26.   proxy_pass http://127.0.0.1:${OO_HOST_PORT}; # Порт OnlyOffice в контейнере
  27.  
  28.   # Обычные заголовки
  29.   proxy_set_header Host \$host;
  30.   proxy_set_header X-Real-IP \$remote_addr;
  31.   proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
  32.   proxy_set_header X-Forwarded-Proto \$scheme;
  33.   proxy_set_header X-Forwarded-Host \$server_name;
  34.  
  35.   # Настройки для WebSocket
  36.   proxy_set_header Upgrade \$http_upgrade;
  37.   proxy_set_header Connection "upgrade";
  38.   proxy_read_timeout 3600s;
  39.   proxy_send_timeout 3600s;
  40.   proxy_connect_timeout 3600s;
  41.  
  42.   # Буферизация
  43.   proxy_buffering off;
  44.   proxy_request_buffering off;
  45.   }
  46. }
  47. EOF

Включаем сайты

  1. ln -s /etc/nginx/sites-available/nextcloud /etc/nginx/sites-enabled/
  2. ln -s /etc/nginx/sites-available/onlyoffice /etc/nginx/sites-enabled/
  3.  
  4. # Проверим синтаксис
  5. nginx -t
  6.  
  7. # Перезагрузим Nginx
  8. systemctl reload nginx

Certbot

Чтобы не мучаться с сертификатами, просто удаляете из конфигов секции с listen 80, а 443 переделываете на 80, убираете все пункты с путями на серты и выполняете команду в стиле

  1. certbot --nginx -d onlyoffice.example.com --email admin@example.com --agree-tos --non-interactive --redirect

у вас и сертификаты получит и редирект с 80 порта сделает и весь конфиг подправит на серты

Первичные настройки Nextcloud (onlyoffice)

Сперва заходим на сайт onlyoffice, там вы должны увидеть что то такое

Теперь идем в nextcloud, заходим под паролем, который у вас лежит в .env
В правом углу заходим в пункт приложения

Заходим в офис и там сверху выбираем плашку OnlyOffice, ждем пока сверху всплывет уведомление что он устанеовлен

Теперь идем в параметры сервера

Там выбираем nextcloud слева onlyoffice

Просто указываем наш адрес DOMAIN_OO и токен ONLYOFFICE_JWT_SECRET из файла .env заголовок - AuthorizationJwt

Решение проблем с редактированием

Я столкнулся с таким, что кнопочка сохранения сама пропадает после изменения, но файл на диске не сохраняется
И вот стоит закрыть вкладку и скачать файл - он не изменен
Но проходит секунд 30 и ура файл обновился, т.е. какое то отслеживание что я перестал с файлом работать и его сохранение.

Мне захотелось чтобы файл гарантированно сохранялся, и что странно вот эта галочка с принудительным сохранением помогает, перед тем как закрыть файл я жсу ctrl+s

Часть 2?

Чтобы не затягивать статью, решил поделить ее на несколько частей...