自部署Nextcloud

Naseele 技术工坊 42 次阅读 6379 字 预计阅读时间: 29 分钟 发布于 2025-11-27 最后更新于 2025-12-05


AI 摘要

厌倦了龟速网盘和U盘?这篇保姆级教程教你用Docker一键搭建私有云盘Nextcloud!从挂硬盘、配证书到性能优化,全程手把手,就像给你的数据安个专属“数字别墅”。还有Caddy自动管理HTTPS,安全又省心~

之前经常用各家的网盘,有时候临时存文件,但是不想带u盘;有时候给人家分享文件,看着进度条一点点挪动发不出来,忍无可忍,无需再忍,我们自己部署一个。

1、前置准备

域名一个(可以自己创建二级域名,然后解析到服务器),云服务器一台(也可以考虑家里云,但是要自己查询相关注意事项,以免被判成在跑PCDN),域名证书(我们使用caddy,所以下载pem/key格式,即nginx通用格式即可)(二级域名请单独给它申请证书)。

创建我们放nextcloud的目录:

mkdir /home/naseele/nextcloud

我们的项目基本路径大致如下(这里看看就行了,不用急着创建,需要手动创建的后面会提):

nextcloud
    ├── docker-compose.yml       # 核心编排文件
    ├── Caddyfile                # caddy反向代理配置
    ├── nextcloud_certs/         # 存放 SSL 证书 (.pem, .key)
    ├── html/                    # Nextcloud 数据 (自动生成)
       ├── config/config.php    # (后续修改) 核心配置
       └── .user.ini            # (后续新建) PHP 优化配置
    └── db/                      # 数据库文件 (自动生成)

2、挂载硬盘

我给nextcloud单独添加了一块小硬盘,如果你没有单独挂载硬盘的需求,请跳过本节,直接看第三章。

另外如果你用的是云服务器,我更推荐你去看你的服务器提供商的增挂硬盘教程,因为不同的服务商增加硬盘的预处理不同。这里仅展示雨云的作为参考。

2.1 查询硬盘情况

本节我们使用两个指令(lsblk和fdisk)确定新硬盘的情况。

首先查看硬盘和分区结构

sudo lsblk

我们能看到类似:

NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
sda      8:0    0   30G  0 disk 
├─sda1   8:1    0    1M  0 part 
└─sda2   8:2    0   30G  0 part /
sdb      8:16   0   30G  0 disk 
sr0     11:0    1 1024M  0 rom

参数解释:
sda (30G disk): 这是我的第一块硬盘

  • ├─sda1: 一个极小的分区(1M),通常是引导分区。
  • └─sda2: 主分区(30G)。
  • 注意看 MOUNTPOINTS 这一列,sda2 对应的是 /。这意味着它是系统盘(根目录),你的系统在这上面。最好不要动它

sdb (30G disk): 这是我的第二块硬盘

  • 有用信息 3MOUNTPOINTS 这一列是的(下面没有 ├─sdb1 这种分支)。也就是说,这块盘存在,但系统还没开始用它。这就是我们要操作的目标。

sr0: 这是虚拟光驱(CD-ROM),通常是云服务商挂载系统镜像用的,直接忽略。

查看硬盘分区表、

sudo fdisk -l

我们能看到类似:


Disk /dev/sda: 30 GiB, 32212254720 bytes, 62914560 sectors

Disk model: QEMU HARDDISK

Units: sectors of 1 * 512 = 512 bytes

Sector size (logical/physical): 512 bytes / 512 bytes

I/O size (minimum/optimal): 512 bytes / 512 bytes

Disklabel type: gpt

Disk identifier: CB7D37F2-E81E-412D-9A53-58CFF4AE9DC5



Device    Start  End      Sectors  Size Type

/dev/sda1 2048   4095     2048     1M   BIOS boot

/dev/sda2 4096   62914526 62910431 30G  Linux filesystem





Disk /dev/sdb: 30 GiB, 32212254720 bytes, 62914560 sectors

Disk model: QEMU HARDDISK

Units: sectors of 1 * 512 = 512 bytes

Sector size (logical/physical): 512 bytes / 512 bytes

I/O size (minimum/optimal): 512 bytes / 512 bytes

输出分三段,第二段详细列出了 sda 被切成了两块(sda1 和 sda2),并且明确标记了 sda2 是 "Linux filesystem",即我们的系统。这是先前就在使用的盘,不要动他,第三段是我们新加的盘,Disk /dev/sdb: 30 GiB 确认了它的容量大小。它没有任何 "Device ... Start ... End" 的列表,说明这是一块完全纯净的“白板”硬盘,里面连分区表都没有,更别提文件系统了。

综上,我们的新硬盘sdb的大小为30gb,没有分区信息,也没有挂载(MOUNTPOINTS 为空)。

2.2 硬盘初始化与挂载

因为硬盘没有分区信息,我们不能直接挂载,需要先格式化

1. 格式化硬盘

我们将 /dev/sdb 格式化为 ext4 文件系统:

sudo mkfs.ext4 /dev/sdb

2. 创建挂载点

创建我们想要挂载的那个目录:

mkdir -p /home/naseele/nextcloud

3. 获取硬盘 UUID

为了防止重启后盘符变化(比如 sdb 变成 sdc),我们需要用 UUID 挂载。输入:

sudo blkid /dev/sdb

复制输出结果中 UUID="xxxx-xxxx..." 引号里的那串字符。

4. 配置开机自动挂载

编辑 /etc/fstab 文件:

sudo nano /etc/fstab

在文件末尾添加一行(请把 你的UUID 替换为刚才复制的字符):

UUID=你的UUID   /home/naseele/nextcloud   ext4    defaults    0   0

按 Ctrl+O 保存,Enter 确认,Ctrl+X 退出。(nano的快捷键是这个,使用其他文本编辑器的请自行百度)

最后我们的fstab文件的内容类似这样:

UUID="7378aa-c12a-6c8d-xt89-ad57so69yhb3" /home/naseele/nextcloud ext4 defaults 0 0

5. 挂载并修复权限

现在立刻挂载,并把所有权交给我的宿主机用户 naseele

sudo mount -a
sudo chown -R naseele:naseele /home/naseele/nextcloud

2.3 创建必要目录

/home/naseele/nextcloud 下创建结构:

mkdir -p html db caddy_data caddy_config nextcloud_certs

3、创建配置文件

3.1 容器编排

现在来为我们的 Nextcloud 创建 docker-compose.yml

进入目录:

cd /home/naseele/nextcloud

新建 docker-compose.yml

nano docker-compose.yml
services:
  # === 1. 数据库服务 ===
  nextcloud-db:
    image: mariadb:10.6
    container_name: nextcloud_db
    restart: always
    # === 给它 最多 1 分钟的优雅退出的时间 ===
    stop_grace_period: 1m
    # 数据库性能优化参数
    command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
    volumes:
      - ./db:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=你的Root密码
      - MYSQL_PASSWORD=你的数据库密码
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - TZ=Asia/Shanghai

  # === 2. 缓存服务 (带密码) ===
  redis:
    image: redis:alpine
    container_name: nextcloud_redis
    restart: always
    # 启用密码保护
    command: redis-server --requirepass 你的Redis强密码
    environment:
      - TZ=Asia/Shanghai

  # === 3. Nextcloud 应用服务 ===
  nextcloud-app:
    image: nextcloud:latest
    container_name: nextcloud_app
    restart: always
    depends_on:
      - nextcloud-db
      - redis
    environment:
      - MYSQL_PASSWORD=你的数据库密码
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_HOST=nextcloud-db
      # 域名信任
      - NEXTCLOUD_TRUSTED_DOMAINS=你的nextcloud域名
      - PHP_MEMORY_LIMIT=4G
      # Redis 连接配置
      - REDIS_HOST=redis
      - REDIS_HOST_PASSWORD=你的Redis强密码
      - TZ=Asia/Shanghai
    # === 禁用 IPv6 配置 ===
    sysctls:
      - net.ipv6.conf.all.disable_ipv6=1
      - net.ipv6.conf.default.disable_ipv6=1
    # 国内环境 DNS 优化 (解决无法连接应用商店/更新)
    dns:
      - 223.5.5.5
      - 114.114.114.114
    volumes:
      - ./html:/var/www/html

  # === 4. Caddy 反向代理 (整合进来了) ===
  caddy:
    image: caddy:latest
    container_name: caddy
    restart: always
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - ./caddy_data:/data
      - ./caddy_config:/config
      # 挂载证书目录
      - ./nextcloud_certs:/etc/caddy/certs
    environment:
      - TZ=Asia/Shanghai
    depends_on:
      - nextcloud-app

请自行替换第12、13、24、37、45行的密码(填你自己编的密码就行,但是最好复杂一些,这个密码平时我们不用手动输入,不用担心记不住),并确保13行与37行的MYSQL_PASSWORD内容一致、24与45行的redis密码内容一致。

目录结构说明: 部署并启动后,你的 /home/naseele/nextcloud 下会自动生成两个文件夹(如果已经创建了也不影响):

  • ./db: 存放数据库文件(占用新硬盘空间)。
  • ./html: 存放 Nextcloud 程序和所有用户上传的照片/文件(占用新硬盘空间)。 这样就完美实现了数据隔离。

3.2 反向代理配置

/home/naseele/nextcloud下:

touch Caddyfile

修改Caddyfile:

# 全局配置:关闭 OCSP Stapling (解决国内启动卡顿/超时)
{
    ocsp_stapling off
}

你的域名 {
    encode gzip

    # 1. 完善的服务发现 (适配 iOS/macOS 客户端)
    redir /.well-known/carddav /remote.php/dav 301
    redir /.well-known/caldav /remote.php/dav 301
    redir /.well-known/webfinger /index.php/.well-known/webfinger 301
    redir /.well-known/nodeinfo /index.php/.well-known/nodeinfo 301

    # 2. 安全与 HSTS
    header {
        Strict-Transport-Security "max-age=15552000; includeSubDomains"
    }

    # 3. 反向代理核心配置
    reverse_proxy nextcloud_app:80 {
        # 头部透传 (修复登录循环、生成正确链接)
        header_up Host {host}
        header_up X-Real-IP {remote}
        header_up X-Forwarded-For {remote}
        header_up X-Forwarded-Proto {scheme}
        header_up X-Forwarded-Port {server_port}

        # 大文件上传超时优化 (防止 502 错误)
        transport http {
            response_header_timeout 7200s
        }
    }

    # SSL 证书 (容器内路径)
    tls /etc/caddy/certs/nextcloud.pem /etc/caddy/certs/nextcloud.key
}

这里我们关闭了OCSP功能,因为它会拖慢caddy的启动速度,详情请参阅这篇文章

3.3 上传域名证书

请将证书上传至 /home/naseele/nextcloud/nextcloud_certs下,并确保它们的名字与Caddyfile中的文件名称一致。我们Caddyfile中这一行

tls /etc/caddy/certs/nextcloud.pem /etc/caddy/certs/nextcloud.key

规定了两个文件的名字和路径,请自行适配。

3.4 启动容器

docker compose up -d

4、配置优化

4.1 PHP 运行限制优化 (.user.ini)

路径: /home/naseele/nextcloud/html/.user.ini (需手动创建或修改),如果无修改权限请加sudo,修改后记得确定文件所有者还是www-data。

目标:允许 100G 大文件上传,防止超时。

upload_max_filesize=100G
post_max_size=100G
memory_limit=1024M
max_input_time=7200
max_execution_time=7200
output_buffering=0

4.2 Nextcloud 核心配置 (config.php)

路径: /home/naseele/nextcloud/html/config/config.php

目标:开启 APCu/Redis 缓存、修复网络连接、本地化。

<?php
$CONFIG = array (
  // ... 其他自动生成的配置 ...

  // === 1. HTTPS 强制修正 ===
  'overwriteprotocol' => 'https',
  'overwrite.cli.url' => 'https://你的域名',

  // === 2. 信任代理 (Docker网段) ===
  'trusted_proxies' => ['172.16.0.0/12', '192.168.0.0/16', '10.0.0.0/8'],

  // === 3. 缓存策略 (APCu + Redis)(应该已经带了,不要重复添加) ===
  'memcache.local' => '\\OC\\Memcache\\APCu',
  'memcache.distributed' => '\\OC\\Memcache\\Redis',
  'memcache.locking' => '\\OC\\Memcache\\Redis',

  // === 4. 网络连接修复 (国内环境)(可选) ===
  'has_internet_connection' => true,
  'connectivity_request_domains' => ['www.baidu.com', 'www.qq.com'],
  'connectivity_request_url' => 'https://www.baidu.com',

  // === 5. 本地化与维护 ===
  'default_phone_region' => 'CN',
  'maintenance_window_start' => 2, // 对应北京时间 02:00 (因容器已设TZ)

  // === 6. 临时目录指向挂载盘 ===
  'tempdirectory' => '/var/www/html/data/tmp',
);

5. 性能与功能调优命令

本节命令均在 /home/naseele/nextcloud 目录下执行。

5.1 设置分块上传大小

如果上行带宽比较小,设置 20MB 分块以保证稳定性。

docker exec -u 33 -it nextcloud_app php occ config:system:set files.chunked_upload.max_size --value 20971520 --type int

5.2 预览图尺寸限制

如果你安装了Preview Generator插件,请看看本节。

默认情况下,当你打开手机相册或网页看图时,Nextcloud 是“实时”生成缩略图的。如果你存了几千张照片,滑动的时候 CPU 会瞬间飙升,界面卡顿,图片一片黑,加载半天才出来。

Preview Generator 插件会在后台利用 Cron 任务,提前把你所有照片的缩略图生成好。等你打开相册时,直接从硬盘读取。但是我们也要考虑硬盘占用的问题。

安装好先别急着执行php occ preview:generate-all,先做一些调优,防止我们的存储空间被占满。

设置预览图的最大宽高(推荐设为 1024 或 2048),防止生成过大缩略图占满硬盘。

docker exec -u 33 -it nextcloud_app php occ config:system:set preview_max_x --value 2048 --type int
docker exec -u 33 -it nextcloud_app php occ config:system:set preview_max_y --value 2048 --type int

限制系统级最大预览图

# 限制系统级最大预览图 (2048px)
docker exec -u 33 -it nextcloud_app php occ config:system:set preview_max_x --value 2048 --type int
docker exec -u 33 -it nextcloud_app php occ config:system:set preview_max_y --value 2048 --type int

启用 jpeg 压缩 (默认就是开的,确认一下)

docker exec -u 33 -it nextcloud_app php occ config:system:set preview_max_scale_factor --value 1 --type int
指定只生成哪几种预览 (可选,高级优化)
这个在启用插件后去 管理设置-回忆 修改
还可以将预览 JPEG 质量设置为 60,以更低的质量预览,节省更多空间。
docker exec -u 33 -it nextcloud_app php occ config:system:set jpeg_quality --value="60" --type=integer
docker exec -u 33 -it nextcloud_app php occ config:app:set preview jpeg_quality --value="60" --type=integer

我想重置/重新生成所有预览。

如果你以前已经生成了一堆巨大的缩略图,或者你发现 appdata_xxxx/preview 文件夹太大了,想反悔,可以随时清空所有缩略图重新来: (注意:这会让看图暂时变卡,直到新图生成完毕)(以下部分参考官方仓库

Nextcloud 31 及更高版本

请在服务器上运行

docker exec -u 33 -it nextcloud_app php occ preview:cleanup

更多信息请参阅文档。

Nextcloud 30 及更早版本

警告:此方法未经官方支持,但已有多位用户证实有效。请自行承担风险。务必保留备份。

  1. 删除文件夹your-nextcloud-data-directory/appdata_*/preview
  2. 可选:更改参数preview_max_x和(例如,更改为preview_max_y) ,并根据 README更改应用程序参数、和(或者更好的是,分别更改为较低的值,例如,和)。config.php512previewgeneratorheightSizessquareSizeswidthSizes512256512
  3. 运行occ files:scan-app-data(这将重置数据库中生成的预览)
  4. 运行occ preview:generate-all [user-id](如果您已完成步骤 2,则此操作将运行得非常快)

配置完成,开始生成缩略图(初次可以用一下这个,之后建议配置定时任务,详见6.自动化维护):

docker exec -u 33 -it nextcloud_app php occ preview:generate-all

如果你的服务器CPU核数较多,可以换用这个生成缩略图:

# 这里的 4 代表 4 个并行进程,根据你 CPU 核心数调整,太高会卡
docker exec -u 33 -it nextcloud_app php occ preview:generate-all --workers=4

注:如果报错OCP\Files\GenericFileException怎么办?

在宿主机执行

# 强制把 html 目录及其下所有文件的归属权交给 www-data (ID 33)
sudo chown -R 33:33 /home/naseele/nextcloud/html
清理可能损坏的缓存
因为刚才生成失败,可能留下了一些半成品的锁或者空文件夹,建议先清理一下应用数据缓存:
docker exec -u 33 -it nextcloud_app php occ files:scan-app-data

给命令“开挂”

我们不依赖配置文件的限制,直接在命令行里强制给 PHP 无限内存无限执行时间

请运行这条“满血版”命令(注意加了 -d 参数):

docker exec -u 33 -it nextcloud_app php -d memory_limit=-1 -d max_execution_time=0 occ preview:generate-all -vv
  • memory_limit=-1:内存无上限(只要物理内存够)。
  • max_execution_time=0:永不超时。
  • -vv:开启详细日志,这样如果还报错,我们能看到具体是哪一张图片导致的。

如果用这个“满血版”命令可行,请使用ctrl + c打断生成,然后用这个命令来。建议跑到后台运行,使用screen或者byobu保证后台常驻。多线程请用这个满血版命令:

docker exec -u 33 -it nextcloud_app php -d memory_limit=-1 -d max_execution_time=0 occ preview:generate-all --workers=4 -vv

多线程用户如果发现加上 --workers=4 后又开始报 GenericFileException 或者数据库锁死(因为 4 个进程同时抢着写数据库),请降低并发数

--workers=4 改成 --workers=2 试试。有时候“慢即是快”,2 个稳定的线程比 4 个总是报错的线程要快得多。

5.3 其他插件安装(可选)

Memories: 替代原生相册,体验极佳。如果要使用这个和Preview Generator配合,需要额外执行:

docker exec -u 33 -it nextcloud_app php occ config:app:set --value="256" previewgenerator coverWidthHeightSizes

关于memories的调优我单独写了一篇文章,如有需要请参考这篇文章

6. 自动化维护 (Crontab)

先到nextcloud网页端后台,管理设置-基本设置,将后台任务Cron模式,然后手动执行

docker exec -u 33 nextcloud_app php cron.php 

在宿主机执行

crontab -e

如果你安装了5.2所说的插件Preview Generator,请写入这个配置:

# ==================================================
# Nextcloud 自动化任务 (服务器时间: UTC)
# ==================================================

# 1. 核心后台任务 (Cron.php)
# 频率:每 5 分钟一次
# 优化:使用 [ -n ... ] 严格判断容器是否存活,防止容器关闭时报错刷屏
*/5 * * * * [ -n "$(docker ps -q -f name=nextcloud_app)" ] && docker exec -u 33 nextcloud_app php cron.php > /dev/null 2>> /home/naseele/nextcloud_cron_errors.log

# 2. 缩略图预生成 (Preview Generator)
# 目标:北京时间凌晨 04:00 -> 对应 UTC 时间 20:00
# 优化:设为 20:02 执行,故意错开核心任务 (20:00 那次),避免 CPU 撞车
*/20 * * * * [ -n "$(docker ps -q -f name=nextcloud_app)" ] && docker exec -u 33 nextcloud_app php occ preview:pre-generate > /dev/null 2>> /home/naseele/nextcloud_preview_errors.log

如果没有安装那个插件,我们只用考虑nextcloud本身的定时任务就行,请写入这个配置:

# 1. 核心后台任务 (Cron.php)
# 频率:每 5 分钟一次
# 优化:使用 [ -n ... ] 严格判断容器是否存活,防止容器关闭时报错刷屏
*/5 * * * * [ -n "$(docker ps -q -f name=nextcloud_app)" ] && docker exec -u 33 nextcloud_app php cron.php > /dev/null 2>> /home/naseele/nextcloud_cron_errors.log

7. 排除文件夹

nextcloud自带的照片文件夹以及我们安装的memories都会进行全盘扫描和图片聚合,但是有些图片我们不想让他聚合,怎么办?方法很简单,在我们想屏蔽的目录创建一个文件,命名为.nomedia,就可以了。创建之后它默认隐藏(nextcloud默认不显示.开头的文件),但是我们试着往这个文件夹上传叫.nomedia的文件的时候,会提示已经有一个叫这个名字的文件了。

注意,一旦我们在这个文件夹放了这个文件,此文件夹及其子文件夹的所有媒体文件都会被排除。

如果你不想在网页端这边创建,可以在电脑创建然后上传(用windows的务必先打开显示文件后缀再创建);或者用指令直接在服务器后台生成这个文件(记得替换路径中的用户名 Naseele 和目标文件夹路径):

docker exec -u 33 nextcloud_app touch "/var/www/html/data/Naseele/files/你的/文件夹/路径/.nomedia"

如果你没有装memories,到此应该没什么问题了,装memories的朋友可能发现图片还在展示,因为memories弄的索引还在,我们手动刷新一下:

docker exec -u 33 -it nextcloud_app php occ memories:index

这样就大功告成了,被屏蔽文件依然安全地保存在硬盘里,我们可以通过“文件”列表找到并下载它们,只是不再在“照片”(或记忆)里单独展示。

如果你发现虽然照片没了,但之前生成的缩略图好像还在占空间,可以跑一次清理(非必须):

docker exec -u 33 -it nextcloud_app php occ files:scan-app-data