在 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 Gateway | Nginx 无法连接到 8080 | 确认 app 容器 Running |
| 删除环境变量后无法登录 | config.php 未正确写入 | 检查 config.php 中 dbpassword 是否存在 |
| MariaDB 启动失败 | 数据目录与 root 密码不匹配 | 确认 ./db 目录完整 |
| 上传大文件失败 | client_max_body_size 太小 | 设为 0 或足够大的值 |
| 生成不安全 URL | overwriteprotocol 未设置 | 在 config.php 中添加 |
十六、重要提醒
关于密码环境变量的删除时机:必须在确认安装完成(occ status 显示 installed: true)之后,才能删除环境变量。如果安装尚未完成就删除,Nextcloud 会因缺少必要参数而无法完成安装。
关于数据库密码:MariaDB 的 MARIADB_ROOT_PASSWORD 和 MARIADB_PASSWORD 在首次初始化数据目录后就不再需要。删除后 MariaDB 仍能正常启动,因为用户和密码已持久化在 ./db 中。
关于版本固定:使用 nextcloud:31-apache 这样的标签,不要用 latest。
关于 Redis:Redis 不是“可选”的。没有它,Nextcloud 会退回到文件锁机制。
关于备份:数据库和文件必须同时备份、同时恢复。只恢复一半会导致文件列表中有条目但实际文件不存在。