Skip to content
twinkling.top服务NextcloudEN
板型Pi 4B 4GB minimum; 8GB if you also run Collabora or Talk
内核6.6.51+rpt-rpi-v8
空闲温度48 °C
内核架构arm64
参考机:Pi 4B 8GB / Bookworm 64-bit

部署配方 · 文件与存储

Pi 4 上的 Nextcloud:真正麻烦的是数据库

Nextcloud 是这台机架上最重的服务,也是失败方式最多、最安静的一个。它依然值得跑,因为属于自己的文件同步,胜过按月租来的文件同步。下面是让它活得下来的拆分方式:数据放 NAS、数据库用 MariaDB 而不是 SQLite、PHP 调到上传真的能完成,以及四个让反向代理不再搞坏所有生成链接的 config.php 值。

nextcloud:30.0.5-apache · 2026-08-30

Nextcloud rack plate: board stack and host ports
服务参数
容器镜像nextcloud:30.0.5-apache
宿主端口:8082/tcp, :8443/tcp
数据目录/srv/homelab/nextcloud/html
内存预留1100 MB
CPU 上限1.0 vCPU (cpus: "1.0")
更新节奏monthly within a major, and read the upgrade notes before every major — Nextcloud skips no majors, 29 to 31 is not a supported jump
ARM 兼容arm64 native since 25; the apache tag is the one to use because the fpm tag has no web server inside it
端口映射与暴露面
宿主容器proto暴露用途
:808280tcp仅局域网web UI, WebDAV and desktop client sync
:8443443tcp仅局域网direct TLS for clients that refuse plain http on the LAN

部署步骤

  1. 用正确的属主建两个卷

    apache 镜像以 uid 33(www-data)跑 PHP。网站根目录和数据库目录都必须对该 uid 可写,NAS 导出也要用匹配的属主。

    run
    sudo mkdir -p /srv/homelab/nextcloud/{html,db}
    sudo chown -R 33:33 /srv/homelab/nextcloud/html
    sudo chown -R 999:999 /srv/homelab/nextcloud/db
    sudo mkdir -p /mnt/nas/nextcloud-data && sudo chown -R 33:33 /mnt/nas/nextcloud-data
  2. 在宿主机上用稳定的导出方式挂载 NAS

    这里选 NFS 是对的:它保留 uid 33,也没有 CIFS 在 Nextcloud 频繁小 stat 调用上的逐请求开销。

    run
    # /etc/fstab
    192.168.10.5:/export/nextcloud /mnt/nas/nextcloud-data nfs4 rw,hard,timeo=100,retrans=3,nofail,x-systemd.automount 0 0
    sudo mount -a && findmnt /mnt/nas/nextcloud-data
  3. 先起数据库,再起应用

    应用容器首次启动会跑安装程序,数据库还没接受连接时它会失败。光靠 depends_on 不会等就绪——真正等的是 healthcheck。

    run
    cd /srv/homelab/nextcloud && docker compose up -d nextcloud-db
    docker exec nextcloud-db mariadb -unextcloud -p"$NC_DB_PASSWORD" -e 'select 1'
    docker compose up -d nextcloud
  4. 登录之前先设好可信域名

    在一个不在 trusted_domains 里的地址上登录会得到 400,看起来像安装坏了。先把值设好,再打开界面。

    run
    docker exec -u www-data nextcloud php occ config:system:set trusted_domains 0 --value="cloud.lan"
    docker exec -u www-data nextcloud php occ config:system:set trusted_domains 1 --value="192.168.10.20"
  5. 修好维护定时任务,跑第一次全量扫描

    第一次 files:scan 很慢,应该从命令行跑,而不是靠打开网页触发。NAS 上 200GB 的数据目录,Pi 4 大约要四十分钟。

    run
    docker exec -u www-data nextcloud php occ background:cron
    docker exec -u www-data nextcloud php occ files:scan --all
    docker exec -u www-data nextcloud php occ status

为什么用 MariaDB 而不是默认的 SQLite

这个镜像开箱就用 SQLite,单用户、小文件量时它确实够用。但只要桌面客户端开始同步一棵目录树、同时手机在上传照片,它就不够用了:SQLite 的写操作是串行的,文件锁争用表现出来的却是同步报错,而那些报错里一个字都不提数据库。

MariaDB 大约多花 450MB 和一个容器。缓冲池设成 512MB,这是树莓派上性价比最高的一个调优项:默认的 128MB 之下,每一次没命中缓存的元数据查询都要落盘,而落的是单板计算机那点可怜的存储。

shell
command: --transaction-isolation=READ-COMMITTED --innodb-buffer-pool-size=512M
# READ-COMMITTED 是 Nextcloud 文档要求的隔离级别,缓冲池大小
# 决定了文件列表是秒开还是要等五秒

数据在 NAS,代码在固态盘

数据目录通过 NFS 而不是 SMB 从 NAS 挂载:文件属主能干净地映射到 uid 33,而且 Nextcloud 大量的小 stat 调用在 NFS 上明显快于 CIFS。

网站根目录留在本地固态盘上,因为 PHP 的 opcode 缓存要的是低延迟,而这份代码本身很小。这个拆分还让备份更省事:NAS 上的数据本来就由 NAS 自己的备份任务覆盖。

内容位置理由
/var/www/html本地固态盘 /srv/homelab/nextcloud/htmlPHP 要低延迟,且体积小
/var/www/html/dataNAS,走 NFS已由 NAS 备份覆盖
/var/lib/mysql本地固态盘数据库 I/O 不该跨网络
备份NAS,再异地一份3-2-1 原则,第二份不在派上

代理后面真正要紧的四个 config.php 值

Nextcloud 根据它收到的请求生成绝对 URL。躲在反向代理后面时,它看到的是 http://nextcloud:80,然后忠实地把这个地址交给你浏览器——curl 看着一切正常,页面样式却加载不出来。

四个值能解决全部问题:trusted_domains、overwriteprotocol、overwrite.cli.url 和 overwritehost。最后一个最常被漏掉,而它漏掉之后,手机 App 分享出去的链接不只是不安全,而是根本是错的。

shell
docker exec -u www-data nextcloud php occ config:system:set trusted_domains 0 --value="cloud.lan"
docker exec -u www-data nextcloud php occ config:system:set overwriteprotocol --value="https"
docker exec -u www-data nextcloud php occ config:system:set overwrite.cli.url --value="https://cloud.lan"
docker exec -u www-data nextcloud php occ config:system:set overwritehost --value="cloud.lan"

让上传真的能传完

镜像默认的 PHP 限制是 512MB,比手机视频小,更比相机的 raw 小得多。PHP_UPLOAD_LIMIT 和 PHP_MEMORY_LIMIT 这两个环境变量是官方支持的做法;手工改 php.ini 能用,直到下一次拉取镜像把文件换掉。

另一半是分块上传,桌面客户端在超过 10MB 时会自动使用。如果上传总是在某个固定大小失败,那你撞到的是代理的限制,不是 PHP 的。

shell
# 确认 PHP 实际生效的值,而不是你以为设过的值
docker exec nextcloud php -i | grep -E 'upload_max_filesize|post_max_size|memory_limit'

没人配置的后台任务

Nextcloud 默认用 AJAX 跑维护任务,也就是说只有有人打开页面时才会触发。管理概览会在一个橙色框里抱怨这件事,而人们学会了无视那个框。

把模式改成 cron,再在宿主机上每五分钟调一次 cron.php。两行配置,换来的是文件扫描跟得上,而不是每次你想看它就触发一次全量重扫。

shell
docker exec -u www-data nextcloud php occ background:cron
# 然后在宿主机上:
# */5 * * * * docker exec -u www-data nextcloud php -f /var/www/html/cron.php

compose 文件

整份文件放在 /srv/homelab/nextcloud/compose.yaml。标签固定版本号,不用 latest——树莓派上回滚比升级麻烦得多。

compose.yaml
services:
  nextcloud:
    image: nextcloud:30.0.5-apache
    container_name: nextcloud
    restart: unless-stopped
    ports:
      - "8082:80/tcp"
      - "8443:443/tcp"
    environment:
      TZ: Asia/Shanghai
      MYSQL_HOST: nextcloud-db
      MYSQL_DATABASE: nextcloud
      MYSQL_USER: nextcloud
      MYSQL_PASSWORD: ${NC_DB_PASSWORD}
      NEXTCLOUD_ADMIN_USER: ${NC_ADMIN_USER}
      NEXTCLOUD_ADMIN_PASSWORD: ${NC_ADMIN_PASSWORD}
      NEXTCLOUD_TRUSTED_DOMAINS: "cloud.lan rack.twinkling.top"
      OVERWRITEPROTOCOL: https
      PHP_MEMORY_LIMIT: 512M
      PHP_UPLOAD_LIMIT: 16G
    volumes:
      - /srv/homelab/nextcloud/html:/var/www/html
      - /mnt/nas/nextcloud-data:/var/www/html/data
    depends_on: [nextcloud-db]
    networks: [rack]

  nextcloud-db:
    image: mariadb:11.4
    container_name: nextcloud-db
    restart: unless-stopped
    command: --transaction-isolation=READ-COMMITTED --innodb-buffer-pool-size=512M
    environment:
      MARIADB_ROOT_PASSWORD: ${NC_ROOT_PASSWORD}
      MARIADB_DATABASE: nextcloud
      MARIADB_USER: nextcloud
      MARIADB_PASSWORD: ${NC_DB_PASSWORD}
    volumes:
      - /srv/homelab/nextcloud/db:/var/lib/mysql
    networks: [rack]

networks:
  rack:
    external: true

加固清单

  • trusted_domains 只列代理域名和局域网地址,其它一律以 400 拒绝
  • 设置了 overwrite.cli.url 与 overwriteprotocol,生成的链接不会泄露容器主机名
  • 管理员账号不用于日常同步;每个人有自己的账号,管理员账号里不存数据
  • 管理员组内的所有账号强制开启双因素
  • 数据目录放在网站根目录之外,Web 服务器配错也不会把原始文件直接吐出来
  • 只从内置应用商店装应用;每次升级后检查被因签名问题禁用的应用

备份方案

三部分,缺一不可:每晚用 mysqldump 导出数据库、备份含 config.php 与 apps 的配置目录、以及数据目录。恢复时必须把匹配的数据库和匹配的配置一起放回去,因为实例 id 和密钥都在 config.php 里,配错的一对会让所有客户端掉线并报服务端错误。

上线后验证

  • 管理概览里的警告只剩你明确接受的那几条
  • 桌面客户端上传一个 4GB 文件能完成,说明 PHP 限制、分块和代理体积上限三者一致
  • 分享链接在局域网外用 WireGuard 能在手机上打开,且 URL 里的主机名正确
  • 后台任务模式显示为 cron,任务列表里最后一次执行在五分钟以内

踩过的坑

  • 把恢复出来的数据库和另一次安装的 config.php 混用,会让所有客户端掉线并报服务端错误,实例 id 也是对不上的。这两者必须一起备份、一起恢复。
  • apache 镜像把日志写在容器里。没有 logrotate,也没给 json-file 驱动设 max-size,一个同步死循环就能把容器的可写层写满,站点随即因为磁盘满而挂掉,而那个报错指向的地方毫无线索。
  • 在 NAS 支撑的数据目录上、用默认 PHP 内存上限跑全量 files:scan,大约跑到一半就会挂。请在第一次扫描之前就把 PHP_MEMORY_LIMIT 设成 512M,而不是之后。
  • Nextcloud 不允许跨大版本升级。29 直接到 31 不是一条升级路径;你得 29、30、31 跑三次升级程序。

硬件与选型问答

Pi 4 跑 Nextcloud 够用吗,还是应该放到 NAS 上?
一到两个用户做文件同步,4GB 的 4B 够用。一旦加上 Talk、Collabora,或者超过四个账号,NAS 是更合适的宿主——Nextcloud 的瓶颈是 PHP 工作进程并发,而树莓派只有四个核。本机架把 Nextcloud 留在派上,是因为 NAS 的角色是备份目标而不是计算节点,把负载挪上去等于让存放备份的机器也需要被备份。
MariaDB、PostgreSQL 还是 SQLite?
用 MariaDB,并且按 Nextcloud 文档要求设成 READ-COMMITTED 隔离级别。PostgreSQL 同样好,某些查询还略快;SQLite 只在两个客户端不同时同步的前提下才够用,而它失败时给出的同步错误里根本不会提到数据库。
整套要预留多少内存?
两个同步客户端下应用容器 1.1GB,MariaDB 缓冲池 512MB 加上开销,再留 300MB 余量。总共是一块 8GB 板子上的 2GB,还剩得下 Jellyfin 的位置。