跳至正文
老丹的足迹 —— 代码写给机器,游记写给自己,感悟写给时间
老丹的足迹 老丹的足迹
老丹的足迹 老丹的足迹
  • 首页
  • 示例页面
  • 首页
  • 示例页面
老丹的足迹 老丹的足迹
老丹的足迹 老丹的足迹
  • 首页
  • 示例页面
  • 首页
  • 示例页面

在 Ubuntu 上用 Docker Compose 手动搭建 Nextcloud:从基础到扩展的完整指南

一、为什么选择手动搭建而不是 AIO

你之前用 AIO 时遇到的核心限制是:数据库、配置、用户数据全部被锁在 AIO 固定的卷管理里。AIO 的 NEXTCLOUD_DATADIR 只允许整体迁移用户数据目录,无法把“元数据”和“用户文件”拆到不同磁盘上。

手动搭建后,这个问题完全消失:

对比维度AIO 方案手动 Docker Compose
存储布局固定卷结构,仅支持整体迁移用户数据每个目录可独立挂载到不同磁盘
容器数量8-9 个(含所有可选组件)3 个起步,按需添加
组件控制通过 AIO 界面勾选直接编辑 Compose 文件
升级方式AIO 界面统一管理你决定何时更新、更新哪个组件
资源占用较高(含多个默认组件)可精确控制每个服务的资源

基础栈只需要 3 个容器:Nextcloud 应用、MariaDB 数据库、Redis 缓存。后续需要在线 Office 时,再单独添加 Collabora 容器即可。

二、密码管理的核心认知:环境变量只是安装引导

这是本文最重要的一个概念,理解它之后,密码管理就变得非常清晰。

Docker 环境变量中的密码,只是首次安装时的“一次性引导参数”。 安装完成后,密码会被写入 config/config.php,之后容器每次启动都直接从 config.php 读取,不再看环境变量。

Nextcloud 官方 Docker 文档明确写道:

“请记住,在安装时设置这些环境变量后,以后再移除它们并不会从你的 config/config.php 中移除这些值,这是因为 Nextcloud 在安装时会生成并合并初始配置。”

这意味着:

阶段密码来源
首次启动从环境变量读取,写入 config.php
安装完成后从 config.php 读取,环境变量不再被使用
删除环境变量后重启仍然从 config.php 读取,密码有效

结论:安装完成后,你可以安全地删掉 Compose 文件里所有密码相关的环境变量,以及 .env 文件。密码已经持久化在 config.php 里了。

三、项目目录规划与权限设置

3.1 将项目放在 Home 目录下

mkdir -p ~/nextcloud
cd ~/nextcloud
chmod 700 ~/nextcloud

3.2 目录结构规划

将元数据留在项目目录内(SSD),用户文件独立挂载到 HDD:

内容宿主机路径说明
程序 + 配置./html与 compose.yaml 同目录,放 SSD
数据库./db项目目录内,放 SSD
Redis 数据./redis项目目录内,放 SSD
用户文件/mnt/hdd/nextcloud/data独立挂载,放 HDD

四、编写 .env 文件

4.1 生成随机密码

openssl rand -base64 24

为数据库 root 密码、数据库用户密码、Redis 密码、Nextcloud 管理员密码各生成一个。

4.2 创建 .env 文件

nano ~/nextcloud/.env

内容:

MARIADB_ROOT_PASSWORD=你的数据库root密码
MARIADB_PASSWORD=你的数据库密码
REDIS_PASSWORD=你的Redis密码
NEXTCLOUD_ADMIN_USER=admin
NEXTCLOUD_ADMIN_PASSWORD=你的强密码
NEXTCLOUD_TRUSTED_DOMAINS=cloud.example.com
OVERWRITECLIURL=https://cloud.example.com

4.3 锁死 .env 文件权限

chmod 600 ~/nextcloud/.env

验证:

ls -la ~/nextcloud/.env

应显示 -rw-------。

4.4 防止误提交到 Git(可选)

echo ".env" >> ~/nextcloud/.gitignore

五、编写 Compose 文件

创建 ~/nextcloud/docker-compose.yml:

services:
  db:
    image: mariadb:11.4
    restart: unless-stopped
    command: --transaction-isolation=READ-COMMITTED --log-bin=binlog --binlog-format=ROW
    volumes:
      - ./db:/var/lib/mysql
    environment:
      MARIADB_AUTO_UPGRADE: "1"
      MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD}
      MARIADB_DATABASE: nextcloud
      MARIADB_USER: nextcloud
      MARIADB_PASSWORD: ${MARIADB_PASSWORD}
    networks:
      - nextcloud_net

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: redis-server --requirepass ${REDIS_PASSWORD}
    volumes:
      - ./redis:/data
    networks:
      - nextcloud_net

  app:
    image: nextcloud:31-apache
    restart: unless-stopped
    ports:
      - "127.0.0.1:8080:80"
    depends_on:
      - db
      - redis
    volumes:
      - ./html:/var/www/html
      - /mnt/hdd/nextcloud/data:/var/www/html/data
    environment:
      MYSQL_HOST: db
      MYSQL_DATABASE: nextcloud
      MYSQL_USER: nextcloud
      MYSQL_PASSWORD: ${MARIADB_PASSWORD}
      REDIS_HOST: redis
      REDIS_HOST_PASSWORD: ${REDIS_PASSWORD}
      NEXTCLOUD_ADMIN_USER: ${NEXTCLOUD_ADMIN_USER}
      NEXTCLOUD_ADMIN_PASSWORD: ${NEXTCLOUD_ADMIN_PASSWORD}
      NEXTCLOUD_TRUSTED_DOMAINS: ${NEXTCLOUD_TRUSTED_DOMAINS}
      OVERWRITEPROTOCOL: https
      OVERWRITECLIURL: ${OVERWRITECLIURL}
      TRUSTED_PROXIES: 172.16.0.0/12
      PHP_MEMORY_LIMIT: 1G
      PHP_UPLOAD_LIMIT: 10G
    networks:
      - nextcloud_net

networks:
  nextcloud_net:

关键配置说明

--transaction-isolation=READ-COMMITTED:Nextcloud 在 MySQL/MariaDB 上必须使用此隔离级别。

image: nextcloud:31-apache:固定主版本号,不要用 latest。Nextcloud 不允许跳过主版本升级。

ports: "127.0.0.1:8080:80":只绑定到本地回环地址,不对外暴露。

TRUSTED_PROXIES: 172.16.0.0/12:Docker 默认网络地址池的范围,告诉 Nextcloud 信任来自此范围的代理请求。

显式定义 networks:避免与宿主机上其他 Docker 项目的网络冲突。

六、设置用户数据目录权限

sudo mkdir -p /mnt/hdd/nextcloud/data
sudo chown -R 33:33 /mnt/hdd/nextcloud/data
sudo chmod -R 750 /mnt/hdd/nextcloud/data

七、启动并完成安装

7.1 首次启动

cd ~/nextcloud
docker compose up -d

等待所有容器 Running。此时 Nextcloud 会自动执行安装,读取环境变量中的密码,写入 config.php。

7.2 验证安装完成

docker exec -u www-data nextcloud_app php occ status

如果显示 installed: true,说明安装已完成。

7.3 验证密码已写入 config.php

docker exec nextcloud_app grep -E "dbpassword|passwordsalt|secret" /var/www/html/config/config.php

八、安装完成后:删除密码环境变量(核心步骤)

这是本文的关键操作。 安装完成后,你可以安全地删除所有密码相关的环境变量。

8.1 修改 docker-compose.yml

将 app 服务的 environment 部分精简为:

    environment:
      MYSQL_HOST: db
      MYSQL_DATABASE: nextcloud
      MYSQL_USER: nextcloud
      REDIS_HOST: redis
      NEXTCLOUD_TRUSTED_DOMAINS: ${NEXTCLOUD_TRUSTED_DOMAINS}
      OVERWRITEPROTOCOL: https
      OVERWRITECLIURL: ${OVERWRITECLIURL}
      TRUSTED_PROXIES: 172.16.0.0/12
      PHP_MEMORY_LIMIT: 1G
      PHP_UPLOAD_LIMIT: 10G

删掉的行:MYSQL_PASSWORD、REDIS_HOST_PASSWORD、NEXTCLOUD_ADMIN_USER、NEXTCLOUD_ADMIN_PASSWORD。

db 服务的 MARIADB_ROOT_PASSWORD 和 MARIADB_PASSWORD 也可以删掉——数据库用户和密码已经存在于 ./db 数据目录中,MariaDB 重启后会从数据目录加载。

8.2 修改 .env 文件

删除已不再需要的密码行,只保留:

NEXTCLOUD_TRUSTED_DOMAINS=cloud.example.com
OVERWRITECLIURL=https://cloud.example.com

8.3 重建容器

docker compose up -d

8.4 验证

docker compose ps
curl -I http://127.0.0.1:8080

8.5 为什么可以这样做

组件密码存储位置删除环境变量后
MariaDB./db 数据目录中的 mysql.user 表从数据目录加载,正常
Nextcloud./html/config/config.php从配置文件读取,正常

MariaDB 的密码在首次初始化数据目录时创建,之后改环境变量不影响已存在的密码。Nextcloud 的密码在首次安装时写入 config.php,之后环境变量不再被读取。

九、配置 Redis 缓存

docker exec -u www-data nextcloud_app php occ config:system:set redis host --value=redis
docker exec -u www-data nextcloud_app php occ config:system:set redis port --value=6379 --type=integer
docker exec -u www-data nextcloud_app php occ config:system:set memcache.local --value='\OC\Memcache\APCu'
docker exec -u www-data nextcloud_app php occ config:system:set memcache.distributed --value='\OC\Memcache\Redis'
docker exec -u www-data nextcloud_app php occ config:system:set memcache.locking --value='\OC\Memcache\Redis'

如果 Redis 仍设置了密码:

docker exec -u www-data nextcloud_app php occ config:system:set redis password --value=你的Redis密码

十、Nginx 反向代理配置

10.1 Nginx 配置

在 /etc/nginx/sites-available/nextcloud 中创建:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 80;
    server_name cloud.example.com;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl http2;
    server_name cloud.example.com;

    ssl_certificate /etc/letsencrypt/live/cloud.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/cloud.example.com/privkey.pem;

    client_max_body_size 10G;
    client_body_timeout 300s;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_buffering off;
    }

    location /.well-known/carddav {
        return 301 $scheme://$host/remote.php/dav;
    }

    location /.well-known/caldav {
        return 301 $scheme://$host/remote.php/dav;
    }
}

10.2 Nextcloud config.php 补充配置

'trusted_proxies' => ['127.0.0.1'],
'overwritehost' => 'cloud.example.com',
'overwriteprotocol' => 'https',

10.3 启用配置并申请证书

sudo ln -s /etc/nginx/sites-available/nextcloud /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d cloud.example.com

十一、可选:添加 Client Push(notify_push)

  notify_push:
    image: nextcloud/notify_push:latest
    restart: unless-stopped
    depends_on:
      - app
      - redis
    environment:
      - PORT=7867
      - BIND=0.0.0.0
      - REDIS_HOST=redis
    volumes:
      - ./html/config:/var/www/html/config
    networks:
      - nextcloud_net

激活:

docker compose up -d notify_push
docker exec -u www-data nextcloud_app php occ notify_push:setup https://cloud.example.com/push

十二、可选:添加在线 Office(Collabora Online)

  collabora:
    image: collabora/code:latest
    container_name: collabora
    restart: unless-stopped
    ports:
      - "127.0.0.1:9980:9980"
    environment:
      - aliasgroup1=https://cloud.example.com:443
      - server_name=office.example.com
      - username=admin
      - password=你的Collabora管理密码
      - extra_params=--o:ssl.enable=false --o:ssl.termination=true
    cap_add:
      - MKNOD
    networks:
      - nextcloud_net

Collabora 需要独立子域名 office.example.com,Nginx 配置参见其官方文档。

十三、备份策略

13.1 完整备份脚本

#!/bin/bash
set -euo pipefail

NC_DIR="$HOME/nextcloud"
DEST="/mnt/backup/nextcloud/$(date +%F)"
OCC="docker exec -u www-data nextcloud_app php /var/www/html/occ"

mkdir -p "$DEST"

# 1. 启用维护模式
$OCC maintenance:mode --on

# 2. 备份文件目录和用户数据
rsync -Aax --delete "$NC_DIR/html/" "$DEST/html/"
rsync -Aax --delete /mnt/hdd/nextcloud/data/ "$DEST/data/"

# 3. 备份数据库
docker exec nextcloud_db mariadb-dump -u root -p \
  --single-transaction --default-character-set=utf8mb4 nextcloud \
  | gzip > "$DEST/nextcloud-db.sql.gz"

# 4. 备份配置文件
cp "$NC_DIR/html/config/config.php" "$DEST/config.php"

# 5. 关闭维护模式
$OCC maintenance:mode --off

echo "Backup completed: $DEST"

十四、更新策略

cd ~/nextcloud

# 1. 备份
./backup.sh

# 2. 拉取新镜像
docker compose pull

# 3. 进入维护模式
docker exec -u www-data nextcloud_app php occ maintenance:mode --on

# 4. 重建容器
docker compose up -d

# 5. 执行升级
docker exec -u www-data nextcloud_app php occ upgrade

# 6. 关闭维护模式
docker exec -u www-data nextcloud_app php occ maintenance:mode --off

十五、常见问题排查

现象可能原因处理方向
502 Bad GatewayNginx 无法连接到 8080确认 app 容器 Running
删除环境变量后无法登录config.php 未正确写入检查 config.php 中 dbpassword 是否存在
MariaDB 启动失败数据目录与 root 密码不匹配确认 ./db 目录完整
上传大文件失败client_max_body_size 太小设为 0 或足够大的值
生成不安全 URLoverwriteprotocol 未设置在 config.php 中添加

十六、重要提醒

关于密码环境变量的删除时机:必须在确认安装完成(occ status 显示 installed: true)之后,才能删除环境变量。如果安装尚未完成就删除,Nextcloud 会因缺少必要参数而无法完成安装。

关于数据库密码:MariaDB 的 MARIADB_ROOT_PASSWORD 和 MARIADB_PASSWORD 在首次初始化数据目录后就不再需要。删除后 MariaDB 仍能正常启动,因为用户和密码已持久化在 ./db 中。

关于版本固定:使用 nextcloud:31-apache 这样的标签,不要用 latest。

关于 Redis:Redis 不是“可选”的。没有它,Nextcloud 会退回到文件锁机制。

关于备份:数据库和文件必须同时备份、同时恢复。只恢复一半会导致文件列表中有条目但实际文件不存在。

作者

老丹

关注我
其他文章
上一个

Headscale 用户使用手册

下一个

Firewalld 区域详解:从概念到实践

关于博主

    老丹是一名C/C++后台开发工程师,信奉“无抽象不设计,无性能不生产”。

  • 技术栈:Modern C++、Linux环境编程、多线程/并发、网络编程等。
  • 信条:能用constexpr解决的问题绝不拖到运行时,能靠RAII避免的泄漏绝不写析构。
  • 正在填坑:从解封装到渲染的C++全链路实现,正在驯服FFmpeg与H.264/H.265。
  • 输出原则:这里的每一段代码都经过-Wall -Wextra -Werror -O2的洗礼。

近期文章

  • 在 Windows 里养一只 Linux:WSL 完整指南 2026年10月24日
  • Paho MQTT C 与 C++ 客户端库完全指南:从概念到 Ubuntu 26.04 下的编译、安装与使用 2026年10月23日
  • Mosquitto $SYS 主题详解:Broker 自监控指南 2026年10月23日
  • 应用级 MQTT 服务搭建完全指南:Mosquitto + 宿主机 Nginx 2026年10月22日
  • OpenWrt 固件签名三剑客:usign、ucert 与 fwtool 深度解析 2026年10月21日

文章分类

  • C/C++开发 (22)
  • Docker容器 (5)
  • Linux工具包 (21)
  • Linux服务配置 (53)
  • Linux系统 (16)
  • MQTT技术 (6)
  • OpenWrt路由 (5)
  • Shell脚本 (3)
  • 代码管理 (1)
  • 安防技术 (4)
  • 数据安全 (36)
  • 网络协议 (25)
  • 计算机理论 (23)
  • 问题解决 (3)
  • 防火墙 (6)
  • 音视频技术 (6)
联系我们:📍 地址:中国·广东省深圳市   |   ✉️ 邮箱:support@tanglinux.com   |   💬 QQ:870866607
版权所有:老丹的足迹粤ICP备2026061170号-1       公安备案图标 粤公网安备44030002013274号