Данная статья больше как незавершенный черновик, на уровень выше у меня инструкция на Apache
Домашняя хранилка у меня представляет малюсенький ПК (Terramaster f4-423) с 4 дисками в RAID-10,
большинство ресурсов доступны только из локальной сети, но вот появилась необходимость редактировать документики (таблички)
и синхронизировать папки через приложение, это немного не сетевая папка, а именно синхронизация, когда нет интернета папка доступна, а когда интернет появляется, то она синхронизируется и обновляется.
про установку certbot
Установка mariadb
Установка докера
Инструкция по раскатке на докер-хабе
| Что | Какое |
|---|---|
| Папка, в которой будут храниться данные пользователей | /storage/nextCloud |
| Папка, в которой будут данные контейнера в т.ч. compose конфиги итд… | /storage/DockerApps/nextCloud |
| Порт fpm, который будем прокидывать на хост из докера доступен только на localhost | 9103 |
# Создаем директорию и файл mkdir -p /storage/DockerApps/nextCloud touch /storage/DockerApps/nextCloud/.env chmod 600 /storage/DockerApps/nextCloud/.env
ENV="/storage/DockerApps/nextCloud/.env" mkdir -p /storage/DockerApps/nextCloud touch $ENV chmod 600 $ENV # --- Сеть --- grep -q "^DB_HOST=" $ENV || echo 'DB_HOST="172.22.0.1"' >> $ENV grep -q "^NETWORK_NAME=" $ENV || echo 'NETWORK_NAME="nextcloud_net"' >> $ENV grep -q "^NETWORK_SUBNET=" $ENV || echo 'NETWORK_SUBNET="172.22.0.0/16"' >> $ENV grep -q "^DB_HOST_SUBNET=" $ENV || echo 'DB_HOST_SUBNET="172.22.%"' >> $ENV # 🔴 Домен Nextcloud (уникальное имя) grep -q "^DOMAIN_NC=" $ENV || echo 'DOMAIN_NC="oblako.example.com"' >> $ENV # --- Порт --- grep -q "^FPM_HOST_PORT=" $ENV || echo 'FPM_HOST_PORT="9103"' >> $ENV # --- База данных --- grep -q "^DB_NAME=" $ENV || echo 'DB_NAME="nextcloud"' >> $ENV grep -q "^DB_USER=" $ENV || echo 'DB_USER="nextcloud"' >> $ENV grep -q "^DB_PASS=" $ENV || echo "DB_PASS=\"$(pwgen -s 32 1)\"" >> $ENV # --- Nextcloud --- grep -q "^NEXTCLOUD_ADMIN_USER=" $ENV || echo 'NEXTCLOUD_ADMIN_USER="admin"' >> $ENV grep -q "^NEXTCLOUD_ADMIN_PASSWORD=" $ENV || echo "NEXTCLOUD_ADMIN_PASSWORD=\"$(pwgen -s 24 1)\"" >> $ENV # --- Redis --- grep -q "^REDIS_PASSWORD=" $ENV || echo "REDIS_PASSWORD=\"$(pwgen -s 32 1)\"" >> $ENV # --- OnlyOffice (задел на будущее) --- grep -q "^DOMAIN_OO=" $ENV || echo 'DOMAIN_OO="office.example.com"' >> $ENV grep -q "^ONLYOFFICE_JWT_SECRET=" $ENV || echo "ONLYOFFICE_JWT_SECRET=\"$(pwgen -s 32 1)\"" >> $ENV echo "✅ .env готов с раздельными доменами"
Далее при желании откроем файл
/storage/DockerApps/nextCloud/.env
и отредактируем хосты/домены/юзеров…
source /storage/DockerApps/nextCloud/.env
Чтобы заранее знать подсеть…
# Проверяем, существует ли сеть docker network inspect ${NETWORK_NAME} &> /dev/null if [ $? -ne 0 ]; then # Сети нет - создаем docker network create \ --driver=bridge \ --subnet=${NETWORK_SUBNET} \ --gateway=172.22.0.1 \ ${NETWORK_NAME} echo "✅ Сеть ${NETWORK_NAME} создана" else echo "ℹ️ Сеть ${NETWORK_NAME} уже существует" fi
Проверка
docker network inspect ${NETWORK_NAME} --format=' Сеть: {{.Name}} Драйвер: {{.Driver}} Подсеть: {{range .IPAM.Config}}{{.Subnet}}{{end}} Шлюз: {{range .IPAM.Config}}{{.Gateway}}{{end}} '
source /storage/DockerApps/nextCloud/.env
Проверим что все переменные заполнились корректно
echo "База: ${DB_NAME}" echo "Пользователь: ${DB_USER}" echo "Хост: ${DB_HOST}" echo "Подсеть: ${DB_HOST_SUBNET}"
Создадим базу, пользователя
mariadb -e "CREATE DATABASE IF NOT EXISTS ${DB_NAME} CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" mariadb -e "CREATE USER IF NOT EXISTS '${DB_USER}'@'${DB_HOST_SUBNET}' IDENTIFIED BY '${DB_PASS}';" mariadb -e "GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'${DB_HOST_SUBNET}';" mariadb -e "FLUSH PRIVILEGES;" mariadb -e "SELECT User, Host FROM mysql.user WHERE User='${DB_USER}';"
Структура будет примерно такой
/storage/DockerApps/nextCloud/ ├── .env ├── docker-compose.yml ├── nextcloud/ │ ├── config/ │ └── html/ └── redis/
# Основной каталог приложения mkdir -p /storage/DockerApps/nextCloud/{nextcloud/{html,config},redis} # Отдельный каталог для данных пользователей mkdir -p /storage/nextCloud_data # Данные пользователей (только контейнер FPM) chown -R 33:33 /storage/nextCloud_data chmod -R 750 /storage/nextCloud_data # Код приложения (контейнер FPM + хост-Nginx для чтения) chown -R 33:33 /storage/DockerApps/nextCloud/nextcloud/html chmod -R 755 /storage/DockerApps/nextCloud/nextcloud/html # Конфигурация (только контейнер FPM) chown -R 33:33 /storage/DockerApps/nextCloud/nextcloud/config/ chmod -R 750 /storage/DockerApps/nextCloud/nextcloud/config/ # Redis (только контейнер Redis) chown -R 33:33 /storage/DockerApps/nextCloud/redis chmod -R 750 /storage/DockerApps/nextCloud/redis # Сам файл .env (максимальная защита) chmod 600 /storage/DockerApps/nextCloud/.env
Проверим
tree -L 3 /storage/DockerApps/nextCloud/
cat > /storage/DockerApps/nextCloud/docker-compose.yml <<EOF version: '3.8' services: redis: image: redis:alpine container_name: nc_redis restart: always networks: - nextcloud_net command: redis-server --requirepass ${REDIS_PASSWORD} volumes: - ./redis:/data nextcloud-fpm: image: nextcloud:fpm container_name: nc_fpm restart: always depends_on: - redis networks: - nextcloud_net ports: - "127.0.0.1:${FPM_HOST_PORT}:9000" volumes: - ./nextcloud/html:/var/www/html - /storage/nextCloud_data:/var/www/html/data - ./nextcloud/config:/var/www/html/config environment: - MYSQL_HOST=${DB_HOST} - MYSQL_DATABASE=${DB_NAME} - MYSQL_USER=${DB_USER} - MYSQL_PASSWORD=${DB_PASS} - REDIS_HOST=redis - REDIS_HOST_PASSWORD=${REDIS_PASSWORD} - NEXTCLOUD_ADMIN_USER=${NEXTCLOUD_ADMIN_USER} - NEXTCLOUD_ADMIN_PASSWORD=${NEXTCLOUD_ADMIN_PASSWORD} - NEXTCLOUD_TRUSTED_DOMAINS=${DOMAIN_NC} - PHP_MEMORY_LIMIT=512M - PHP_UPLOAD_LIMIT=10G networks: nextcloud_net: external: true name: ${NETWORK_NAME} EOF echo "✅ docker-compose.yml создан"
Запускаем
cd /storage/DockerApps/nextCloud docker compose up -d docker logs -f nc_fpm
Если установка не прошла, то ее можно руками запустить
docker exec -u www-data nc_fpm php occ maintenance:install \ --database "mysql" \ --database-host "${DB_HOST}" \ --database-name "${DB_NAME}" \ --database-user "${DB_USER}" \ --database-pass "${DB_PASS}" \ --admin-user "${NEXTCLOUD_ADMIN_USER}" \ --admin-pass "${NEXTCLOUD_ADMIN_PASS}"
Статус установки узнать так
docker exec -u www-data nc_fpm php occ status
Так проверить трастовый домен если не пускает…
docker exec -u www-data nc_fpm php occ config:system:get trusted_domains
А так можно проверить…
docker exec -u www-data nc_fpm php occ config:system:set trusted_domains 1 --value="${DOMAIN_NC}"
source /storage/DockerApps/nextCloud/.env cat > /etc/nginx/sites-available/nextcloud <<EOF # Set the `immutable` cache control options only for assets with a cache busting `v` argument map $arg_v $asset_immutable { "" ""; default ", immutable"; } server { listen 80; listen [::]:80; server_name oblako.example.com; # Prevent nginx HTTP Server Detection server_tokens off; # Enforce HTTPS return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name oblako.example.com; # Path to the root of your installation (on HOST) root /storage/DockerApps/nextCloud/nextcloud/html; # Use Mozilla's guidelines for SSL/TLS settings # https://mozilla.github.io/server-side-tls/ssl-config-generator/ ssl_certificate /etc/letsencrypt/live/oblako.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/oblako.example.com/privkey.pem; # Prevent nginx HTTP Server Detection server_tokens off; # HSTS settings # WARNING: Only add the preload option once you read about # the consequences in https://hstspreload.org/. This option # will add the domain to a hardcoded list that is shipped # in all major browsers and getting removed from this list # could take several months. #add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; # set max upload size and increase upload timeout: client_max_body_size 512M; client_body_timeout 300s; fastcgi_buffers 64 4K; # Proxy and client response timeouts # Uncomment an increase these if facing timeout errors during large file uploads #proxy_connect_timeout 60s; #proxy_send_timeout 60s; #proxy_read_timeout 60s; #send_timeout 60s; # Enable gzip but do not remove ETag headers 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; # Pagespeed is not supported by Nextcloud, so if your server is built # with the `ngx_pagespeed` module, uncomment this line to disable it. #pagespeed off; # The settings allows you to optimize the HTTP2 bandwidth. # See https://blog.cloudflare.com/delivering-http-2-upload-speed-improvements/ # for tuning hints client_body_buffer_size 512k; # HTTP response headers borrowed from Nextcloud `.htaccess` 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; # Remove X-Powered-By, which is an information leak fastcgi_hide_header X-Powered-By; # Set .mjs and .wasm MIME types # Either include it in the default mime.types list # and include that list explicitly or add the file extension # only for Nextcloud like below: include mime.types; types { text/javascript mjs; application/wasm wasm; } # Specify how to handle directories -- specifying `/index.php$request_uri` # here as the fallback means that Nginx always exhibits the desired behaviour # when a client requests a path that corresponds to a directory that exists # on the server. In particular, if that directory contains an index.php file, # that file is correctly served; if it doesn't, then the request is passed to # the front-end controller. This consistent behaviour means that we don't need # to specify custom rules for certain paths (e.g. images and other assets, # `/updater`, `/ocs-provider`), and thus # `try_files $uri $uri/ /index.php$request_uri` # always provides the desired behaviour. index index.php index.html /index.php$request_uri; # Rule borrowed from `.htaccess` to handle Microsoft DAV clients 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; } # Make a regex exception for `/.well-known` so that clients can still # access it despite the existence of the regex rule # `location ~ /(\.|autotest|...)` which would otherwise handle requests # for `/.well-known`. location ^~ /.well-known { # The rules in this block are an adaptation of the rules # in `.htaccess` that concern `/.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; } # Let Nextcloud's API for `/.well-known` URIs handle all other # requests by passing them to the front-end controller. return 301 /index.php$request_uri; } # Rules borrowed from `.htaccess` to hide certain paths from clients location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/) { return 404; } location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) { return 404; } # Ensure this block, which passes PHP files to the PHP process, is above the blocks # which handle static assets (as seen below). If this block is not declared first, # then Nginx will encounter an infinite rewriting loop when it prepends `/index.php` # to the URI, resulting in a HTTP 500 error response. 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; # The path inside the container fastcgi_param SCRIPT_FILENAME /var/www/html$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 # Connect directly to the FPM port on the host fastcgi_pass 127.0.0.1:9103; fastcgi_intercept_errors on; fastcgi_request_buffering on; # Required as PHP-FPM does not support chunked transfer encoding and requires a valid ContentLength header. # PHP-FPM 504 response timeouts # Uncomment and increase these if facing timeout errors during large file uploads #fastcgi_read_timeout 60s; #fastcgi_send_timeout 60s; #fastcgi_connect_timeout 60s; fastcgi_max_temp_file_size 0; } # Serve static files location ~ \.(?:css|js|mjs|svg|gif|ico|jpg|png|webp|wasm|tflite|map|ogg|flac|mp4|webm)$ { try_files $uri /index.php$request_uri; # HTTP response headers borrowed from Nextcloud `.htaccess` 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; access_log off; # Optional: Don't log access to assets } location ~ \.(otf|woff2?)$ { try_files $uri /index.php$request_uri; expires 7d; # Cache-Control policy borrowed from `.htaccess` access_log off; # Optional: Don't log access to assets } # Rule borrowed from `.htaccess` location /remote { return 301 /remote.php$request_uri; } location / { try_files $uri $uri/ /index.php$request_uri; } } EOF echo "✅ Конфиг создан в sites-available"
Активируем хост
ln -sf /etc/nginx/sites-available/nextcloud /etc/nginx/sites-enabled/nextcloud
nginx -t && systemctl reload nginx
certbot --nginx \ -d ${DOMAIN_NC} \ --email admin@example.com \ --agree-tos \ --non-interactive \ --redirect
version: '3.8' services: redis: image: redis:alpine container_name: nc_redis restart: always networks: - nextcloud_net command: redis-server --requirepass ${REDIS_PASSWORD} volumes: - ./redis:/data nextcloud-fpm: image: nextcloud:fpm container_name: nc_fpm restart: always depends_on: - redis networks: - nextcloud_net ports: - "127.0.0.1:${FPM_HOST_PORT}:9000" volumes: - ./nextcloud/html:/var/www/html - /storage/nextCloud_data:/var/www/html/data - ./nextcloud/config:/var/www/html/config environment: - MYSQL_HOST=${DB_HOST} - MYSQL_DATABASE=${DB_NAME} - MYSQL_USER=${DB_USER} - MYSQL_PASSWORD=${DB_PASS} - REDIS_HOST=redis - REDIS_HOST_PASSWORD=${REDIS_PASSWORD} - NEXTCLOUD_ADMIN_USER=${NEXTCLOUD_ADMIN_USER} - NEXTCLOUD_ADMIN_PASSWORD=${NEXTCLOUD_ADMIN_PASSWORD} - NEXTCLOUD_TRUSTED_DOMAINS=${DOMAIN_NC} - PHP_MEMORY_LIMIT=512M - PHP_UPLOAD_LIMIT=10G # --- НОВЫЙ СЕРВИС ONLYOFFICE --- onlyoffice-document-server: image: onlyoffice/documentserver:latest # Можно заменить на конкретную версию container_name: nc_onlyoffice restart: always networks: - nextcloud_net # Та же сеть, что и nextcloud-fpm volumes: - ./onlyoffice/logs:/var/log/onlyoffice # Логи на хост - ./onlyoffice/data:/var/www/onlyoffice/Data # Данные документов на хост - ./onlyoffice/fonts:/usr/share/fonts/truetype/custom # Кастомные шрифты (опционально) # - ./onlyoffice/example:/etc/onlyoffice/documentserver-example # Примеры конфигов (опционально) environment: - JWT_ENABLED=true - JWT_SECRET=${ONLYOFFICE_JWT_SECRET} # Используем переменную из .env - JWT_HEADER=AuthorizationJwt # Заголовок, который будет использовать Nextcloud # ports: # Не публикуем порты на хост, т.к. общение идет внутри сети # - "8080:80" # Только если нужно отдельно тестировать OnlyOffice networks: nextcloud_net: external: true name: ${NETWORK_NAME}
mkdir -p /storage/DockerApps/nextCloud/onlyoffice/{logs,data,fonts} chown -R 33:33 /storage/DockerApps/nextCloud/onlyoffice # UID/GID для пользователя www-data в OnlyOffice chmod -R 750 /storage/DockerApps/nextCloud/onlyoffice
cd /storage/DockerApps/nextCloud docker compose down cd /storage/DockerApps/nextCloud docker compose down rm -r /storage/DockerApps/nextCloud/* rm -r /storage/nextCloud_data/* rm -r /storage/DockerApps/nextCloud/.env # 1. Загружаем переменные из .env source /storage/DockerApps/nextCloud/.env # 2. Удаляем базу данных mariadb -e "DROP DATABASE IF EXISTS ${DB_NAME};" # 3. Удаляем пользователя mariadb -e "DROP USER IF EXISTS '${DB_USER}'@'${DB_HOST_SUBNET}';" # 4. Применяем изменения mariadb -e "FLUSH PRIVILEGES;" rm -r /storage/nextCloud_data/* # 1. Загружаем переменные из .env source /storage/DockerApps/nextCloud/.env # 2. Удаляем базу данных mariadb -e "DROP DATABASE IF EXISTS ${DB_NAME};" # 3. Удаляем пользователя mariadb -e "DROP USER IF EXISTS '${DB_USER}'@'${DB_HOST_SUBNET}';" # 4. Применяем изменения mariadb -e "FLUSH PRIVILEGES;"