Appearance
Nextcloud + Memories 完整部署(FPM + go-vod)
在 Synology NAS(Intel J4125)上部署 Nextcloud + Memories 相片管理,使用官方 FPM 映像 + 自訂 Dockerfile(加入 ffmpeg)+ 獨立 Nginx + go-vod VA-API 硬體加速影片轉碼 + Recognize AI 標籤。
概述
與 LSIO 版(NAS 自建 Nextcloud (LinuxServer.io))相比,本方案基於 Nextcloud 官方 FPM 映像,架構更複雜但功能更完整:
| 功能 | LSIO 版 | 本方案(FPM + Memories) |
|---|---|---|
| Nextcloud 基本功能 | ✅ | ✅ |
| 相片時間軸 | ❌ | ✅ |
| 影片 HLS 轉碼(go-vod) | ❌ | ✅ VA-API 硬體加速 |
| AI 人臉/物件辨識(Recognize) | ❌ | ✅ WASM 模式 |
| 地圖 + 地點名稱(Geocoding) | ❌ | ✅ |
| 架構複雜度 | 低(1 主容器) | 高(5 容器) |
Stack 包含 5 個服務:
- app(Nextcloud FPM + ffmpeg + supervisord)— 主應用 + 背景任務 + External Storage 掃描
- web(Nginx)— HTTP 前端,靜態資源快取 + FastCGI 轉發
- db(MariaDB 11)— 資料庫
- redis(Redis 8-alpine)— File locking + APCu 分散式快取
- go-vod(radialapps/go-vod)— 影片轉碼(VA-API 硬體加速)
核心內容
自訂 Nextcloud FPM 映像設計
官方 nextcloud:fpm 映像不含 ffmpeg(Memories 縮圖生成需要)和 supervisord(多程序管理)。需要自訂 Dockerfile 補裝。使用 NCHC 台灣鏡像加速 apt 安裝(debian-security repo 保留官方 CDN,僅替換主 repo)。
supervisord 管理三個程序:
php-fpm:主 PHP 處理程序/cron.sh:官方內建腳本,busybox crond 每 5 分鐘執行 Nextcloud 背景任務(cron.php)scan-cron:第二個獨立 busybox crond 實例,使用/var/spool/cron/scan-crontabs/作為目錄,與/cron.sh完全隔離
兩個 crond 使用不同 crontab 目錄完全隔離,互不干擾。idle 時整個 supervisord 開銷約 20-30MB RAM,CPU ≈ 0%。
go-vod 外部轉碼器架構
go-vod 是 Memories 的外部影片轉碼服務。Memories 將轉碼請求傳給 go-vod(go-vod:47788),go-vod 用 VA-API 將影片轉碼為 HLS 串流後回傳。
關鍵設定:
NEXTCLOUD_HOST=http://web:80:go-vod 需從 Nextcloud 下載 binary 和媒體,用 Docker 內部網路(不繞 Cloudflare),HTTP 不需 TLStrusted_domains必須包含web:go-vod 的 HTTP Host header 是web,Nextcloud 拒絕非信任 host,導致 go-vod 持續報Failed to fetch ... Retrying(首次部署在 Nextcloud + Memories 安裝完成前,此 log 為正常行為)
J4125 VA-API 硬體加速
Intel Celeron J4125(Gemini Lake Refresh,UHD 600)支援 H.264 和 HEVC 8-bit 的 VA-API 編解碼,啟用後轉碼效能提升 4-5 倍。radialapps/go-vod 基於 jellyfin/jellyfin 映像,已內建 Intel media driver(iHD)。若遇到 bug,設 LIBVA_DRIVER_NAME=i965 切換到舊版 driver。
確認 /dev/dri/renderD128 存在:
bash
ls -la /dev/dri/
# 若不存在,嘗試:sudo modprobe i915Nginx 配置重點
本方案的 nginx.conf 基於 Nextcloud 官方範例,關鍵特點:
fastcgi_request_buffering off:串流轉發,不暫存到 NAS 磁碟,減少 I/O,支援大檔上傳(⚠ DAVx⁵ 等 HTTP Transfer-Encoding: chunked 的客戶端需改為 on,否則產生 0-byte 檔案)- PHP location 在靜態資源之前:避免 .php 路徑進入靜態資源的 try_files 造成 rewrite loop
immutable快取:帶?v=xxx的靜態資源標記 immutable,提升重複訪問效能- 敏感路徑封鎖:
/config、/lib、/data等路徑回傳 404,防止原始碼外洩
Recognize AI 在 J4125 的特殊考量
J4125 無 AVX 指令集,Recognize 自動切換至 WASM 模式(tensorflow.purejs = true)。管理面板的「Tensorflow WASM mode」等項目可能持續顯示 Checking,這是已知 UI bug(#1123),不影響實際功能。
Recognize 背景分類屬於 TIME_INSENSITIVE 任務,只在維護時段(maintenance_window_start = UTC 19)執行,每次 cron 觸發只處理一小批。首次部署建議手動跑完整分類(背景 nohup),後續增量由 cron 自動處理,recognize:classify 支援斷點續跑。
--unscanned vs 全量掃描策略
本方案採混合掃描策略(與 LSIO 版純全量掃描不同):
| 模式 | 頻率 | 能偵測 |
|---|---|---|
--unscanned(增量) | 每 15 分鐘 | 新上傳檔案(filecache 標記未完成的條目) |
| 不帶 flag(全量) | 每日 UTC 19:00 | 所有變更,包含外部刪除、修改 |
增量掃描後自動執行 memories:index,確保 Memories 時間軸即時更新。
關鍵要點
trusted_domains必須包含web(讓 go-vod 的 HTTP Host header 通過)docker compose exec --user www-data是所有 occ 指令的正確方式(官方映像不同於 LSIO)- Bind mount 目錄(data/config/custom_apps)必須在首次
up前手動建立並chown -R 33:33 tmpfs /tmp:exec是必要的(Memories exiftool binary 在 /tmp 執行)- Recognize 首次分類需手動完整執行,後續 cron 自動增量
- go-vod 連 Nextcloud 用
http://web:80(內網),不繞 Cloudflare
實際應用
此部署適用於:
- 完整 Google Photos 替代方案(時間軸、地圖、AI 標籤、人臉辨識)
- Intel NAS(J4125 或類似),利用 VA-API 硬體加速影片轉碼
- 有多個 NAS 共享資料夾(多 volume)需要透過 Nextcloud 管理和 AI 索引
部署設定參考
以下為實際部署時使用的完整設定,供日後查詢與複製使用。
環境參數
| 項目 | 值 |
|---|---|
| NAS 硬體 | Synology NAS(Intel Celeron J4125,UHD 600) |
| Nextcloud 版本 | 32(官方 FPM 映像) |
| 外部 port | 8880:80(Nginx HTTP) |
| Cloudflare Tunnel | HTTP,連入宿主機 IP:8880 |
| External Storage 掃描 | 增量每 15 分鐘 + 全量每日 UTC 19:00 |
docker-compose.yaml(完整)
yaml
services:
# ============================================================
# MariaDB
# ============================================================
db:
image: mariadb:11
restart: unless-stopped
command: >
--transaction-isolation=READ-COMMITTED
--log-bin-trust-function-creators=1
--innodb-buffer-pool-size=512M
--character-set-server=utf8mb4
--collation-server=utf8mb4_unicode_ci
volumes:
- /volume1/docker/nextcloud/mariadb/mysql:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=<YOUR_DB_ROOT_PASSWORD>
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
- MYSQL_PASSWORD=<YOUR_DB_PASSWORD>
- TZ=Asia/Taipei
# ============================================================
# Redis(無密碼)
# ============================================================
redis:
image: redis:8-alpine
restart: unless-stopped
command: redis-server
volumes:
- /volume1/docker/nextcloud/redis/data:/data
environment:
- ALLOW_EMPTY_PASSWORD=yes
# ============================================================
# Nextcloud (FPM) — 自訂 image 含 ffmpeg + supervisord
# ============================================================
app:
# 預建 image(基於 Dockerfile.nextcloud)。如需本地 build,改用:
# build:
# context: .
# dockerfile: Dockerfile.nextcloud
image: <your-registry>/nextcloud-fpm:<tag>
restart: unless-stopped
depends_on:
- db
- redis
command: ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]
volumes:
- nextcloud_html:/var/www/html
- /volume1/docker/nextcloud/data:/var/www/html/data
- /volume1/docker/nextcloud/config:/var/www/html/config
- /volume1/docker/nextcloud/custom_apps:/var/www/html/custom_apps
# External Storage(NAS 共享資料夾)
- /volume1/data:/mnt/data
- /volume2/data2:/mnt/data2
# 設定檔 mount(修改後重啟容器即生效,不需 rebuild)
- ./supervisord.conf:/etc/supervisor/supervisord.conf:ro
- ./scan-external-storages.sh:/opt/scan-external-storages.sh:ro
- ./scan-crontab:/opt/scan-crontab:ro
environment:
- MYSQL_HOST=db
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
- MYSQL_PASSWORD=<YOUR_DB_PASSWORD>
- REDIS_HOST=redis
- REDIS_HOST_PORT=6379
- OVERWRITEPROTOCOL=https
- OVERWRITECLIURL=https://cloud.yourdomain.com
# TRUSTED_PROXIES 以空格分隔,僅首次初始化生效。後續用 occ 設定。
- TRUSTED_PROXIES=172.16.0.0/12 10.0.0.0/8
- PHP_MEMORY_LIMIT=1G
- PHP_UPLOAD_LIMIT=16G
tmpfs:
- /tmp:exec # Memories exiftool 需要在 /tmp 執行 binary
# ============================================================
# Nginx (Reverse Proxy for FPM)
# ============================================================
web:
image: nginx:alpine
restart: unless-stopped
depends_on:
- app
volumes:
- nextcloud_html:/var/www/html:ro
# custom_apps 是獨立目錄,不在 nextcloud_html 中。
# Nginx 必須直接存取其靜態資源(JS/CSS),否則 App 前端全部 404。
- /volume1/docker/nextcloud/custom_apps:/var/www/html/custom_apps:ro
- ./nginx.conf:/etc/nginx/nginx.conf:ro
ports:
- "8880:80"
# ============================================================
# go-vod (External Transcoder for Memories)
# ============================================================
go-vod:
image: radialapps/go-vod
restart: unless-stopped
init: true
depends_on:
- app
environment:
# Docker 內部連接 Nginx,不繞 Cloudflare
- NEXTCLOUD_HOST=http://web:80
- NEXTCLOUD_ALLOW_INSECURE=1
devices:
- /dev/dri:/dev/dri # VA-API 硬體加速
volumes:
- /volume1/docker/nextcloud/data:/var/www/html/data:ro
volumes:
nextcloud_html:Dockerfile.nextcloud
dockerfile
FROM nextcloud:32-fpm
# 替換主 repo 為 NCHC 鏡像(加速台灣下載),保留 security repo 官方 CDN
RUN sed -i '/debian-security/!s|deb.debian.org|opensource.nchc.org.tw|g' \
/etc/apt/sources.list.d/*.sources 2>/dev/null; \
sed -i '/debian-security/!s|deb.debian.org|opensource.nchc.org.tw|g' \
/etc/apt/sources.list 2>/dev/null; \
apt-get update && \
apt-get install -y --no-install-recommends \
ffmpeg \
procps \
supervisor \
&& rm -rf /var/lib/apt/lists/* \
&& mkdir -p /var/log/supervisord /var/run/supervisord \
&& mkdir -p /var/spool/cron/scan-crontabs
# Imagick 已內建 | Perl 通常不需要(Memories 自帶 exiftool binary)
# VA-API 由 go-vod 容器負責,此容器的 ffmpeg 僅用於縮圖擷取
# 覆蓋 CMD 後仍需此變數觸發 Nextcloud 自動安裝/更新
ENV NEXTCLOUD_UPDATE=1
# CMD 和 supervisord.conf 由 docker-compose.yaml 提供,不寫在 image 內需要 VA-API 於 app 容器內(internal transcoder 模式)時,額外安裝
libva2、intel-media-va-driver-non-free、libva-drm2。若 apt 找不到 non-free driver,先加入來源:dockerfileRUN echo "deb http://deb.debian.org/debian bookworm non-free non-free-firmware" >> /etc/apt/sources.list.d/non-free.list
supervisord.conf
ini
[unix_http_server]
file=/var/run/supervisord/supervisor.sock
[supervisorctl]
serverurl=unix:///var/run/supervisord/supervisor.sock
[supervisord]
nodaemon=true
logfile=/var/log/supervisord/supervisord.log
pidfile=/var/run/supervisord/supervisord.pid
childlogdir=/var/log/supervisord/
logfile_maxbytes=50MB
logfile_backups=10
loglevel=error
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[program:php-fpm]
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
command=php-fpm
[program:cron]
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
command=/cron.sh
[program:scan-cron]
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
command=/bin/bash -c "cp /opt/scan-crontab /var/spool/cron/scan-crontabs/www-data && chmod 600 /var/spool/cron/scan-crontabs/www-data && exec busybox crond -f -c /var/spool/cron/scan-crontabs -L /dev/stdout"
/cron.sh是官方 image 內建腳本,用 busybox crond 每 5 分鐘執行cron.php。scan-cron是第二個獨立 crond 實例,使用/var/spool/cron/scan-crontabs/作為 crontab 目錄,啟動時將 mount 的/opt/scan-crontab複製並設權限 600(busybox crond 要求),以www-data身份執行排程。
scan-external-storages.sh
bash
#!/bin/bash
# 參數:--full 全量掃描(不帶則為增量掃描)
# === External Storage 掛載路徑 ===
# 格式:<Nextcloud 使用者>/files/<掛載點名稱>
# ⚠ 誤填檔案系統路徑(如 /mnt/data)會導致 occ 報 "Unknown user" 錯誤。
# 可用 occ files_external:list 確認掛載點名稱。
PATHS=(
"admin/files/data"
"admin/files/data2"
)
cd /var/www/html || exit 1
SCAN_FLAGS="--unscanned"
if [[ "$1" == "--full" ]]; then
SCAN_FLAGS=""
fi
for p in "${PATHS[@]}"; do
echo "[scan] Scanning: $p (flags: ${SCAN_FLAGS:-full})"
php occ files:scan $SCAN_FLAGS --path="$p"
done
php occ memories:indexscan-crontab
crontab
# 增量掃描(每 15 分鐘,只處理新檔案)
*/15 * * * * /opt/scan-external-storages.sh
# 全量掃描(每日 UTC 19:00 = 台灣凌晨 3 點,處理刪除/修改的檔案)
0 19 * * * /opt/scan-external-storages.sh --full容器內 crond 使用 UTC 時區。若容器設了
TZ=Asia/Taipei,則改用台灣時間0 3。
nginx.conf(完整)
nginx
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
types {
text/javascript mjs;
application/wasm wasm;
}
sendfile on;
keepalive_timeout 65;
client_max_body_size 16G;
client_body_timeout 300s;
client_body_buffer_size 512k;
fastcgi_buffers 64 4K;
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;
# 帶 ?v=xxx 的靜態資源標記為 immutable
map $arg_v $asset_immutable {
"" "";
default ", immutable";
}
upstream php-handler {
server app:9000;
}
server {
listen 80;
server_name _;
server_tokens off;
root /var/www/html;
index index.php index.html /index.php$request_uri;
# --- Microsoft WebDAV client (DavClnt) ---
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;
}
# --- .well-known ---
location ^~ /.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; }
return 301 /index.php$request_uri;
}
# --- 安全 headers ---
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;
# HSTS 由 Cloudflare 管理,不在 Nginx 設定
fastcgi_hide_header X-Powered-By;
# --- 封鎖敏感路徑 ---
location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/) { return 404; }
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) { return 404; }
# --- PHP 處理(必須在靜態資源之前)---
location ~ \.php(?:$|/) {
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;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $path_info;
fastcgi_param HTTPS on;
fastcgi_param modHeadersAvailable true;
fastcgi_param front_controller_active true;
fastcgi_pass php-handler;
fastcgi_intercept_errors on;
# 串流轉發,不暫存到磁碟
# ⚠ DAVx⁵ 等 HTTP Transfer-Encoding: chunked 客戶端須改為 on
fastcgi_request_buffering off;
fastcgi_max_temp_file_size 0;
fastcgi_connect_timeout 10s;
fastcgi_send_timeout 3600s;
fastcgi_read_timeout 3600s;
}
# --- 靜態資源 ---
location ~ \.(?:css|js|mjs|svg|gif|ico|jpg|png|webp|wasm|tflite|map|ogg|flac|mp4|webm)$ {
try_files $uri /index.php$request_uri;
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;
}
# --- 字型(7 天快取)---
location ~ \.(otf|ttf|woff2?)$ {
try_files $uri /index.php$request_uri;
expires 7d;
access_log off;
}
# --- WebDAV 相容性 ---
location /remote {
return 301 /remote.php$request_uri;
}
location / {
try_files $uri $uri/ /index.php$request_uri;
}
}
}部署流程
Step 1:建立 Bind Mount 目錄
bash
mkdir -p /volume1/docker/nextcloud/{data,config,custom_apps}
# Docker bind mount 若路徑不存在會自動建立為 root 擁有,www-data(UID 33)無法寫入
# 必須在首次 docker compose up 前手動建立並設定 owner
chown -R 33:33 /volume1/docker/nextcloud/{data,config,custom_apps}
# nextcloud_html 是具名 volume,Docker 自動管理,不需處理
# External Storage(/volume1/data 等)由 Synology ACL 管理,不需 chownStep 2:啟動
bash
cd /volume1/docker/nextcloud
docker compose up -d --build # 首次含 build
docker compose logs -f app # 等待初始化(約 1-3 分鐘)
docker compose ps # 確認所有服務 runningStep 3:初始化設定
安裝精靈:訪問 https://cloud.yourdomain.com,填入管理員帳密、MariaDB 資訊(使用者 nextcloud、DB nextcloud、主機 db:3306)。
bash
# 所有 occ 必須以 www-data 執行
docker compose exec --user www-data -it app bashTrusted Domains & Reverse Proxy
bash
php occ config:system:set trusted_domains 0 --value='cloud.yourdomain.com'
php occ config:system:set trusted_domains 1 --value='web' # go-vod 內部存取必須!
php occ config:system:set trusted_domains 2 --value='localhost'
php occ config:system:set overwriteprotocol --value='https'
php occ config:system:set overwrite.cli.url --value='https://cloud.yourdomain.com'
php occ config:system:set trusted_proxies 0 --value='172.16.0.0/12'
php occ config:system:set trusted_proxies 1 --value='10.0.0.0/8'Redis(無密碼,官方映像直接使用單反斜線)
bash
php occ config:system:set memcache.local --value='\OC\Memcache\APCu'
php occ config:system:set memcache.distributed --value='\OC\Memcache\Redis'
php occ config:system:set memcache.locking --value='\OC\Memcache\Redis'
php occ config:system:set redis host --value='redis'
php occ config:system:set redis port --value=6379 --type=integerCloudflare chunk 限制
bash
php occ config:system:set files.chunked_upload.max_size --value=99614720 --type=integer預覽設定
bash
php occ config:system:set enable_previews --value=true --type=boolean
php occ config:system:set preview_max_x --value=4096 --type=integer
php occ config:system:set preview_max_y --value=4096 --type=integer
php occ config:system:set jpeg_quality --value=60 --type=integer
php occ config:app:set preview jpeg_quality --value=60
php occ config:system:set preview_ffmpeg_path --value='/usr/bin/ffmpeg'
# OC\Preview\Movie 涵蓋所有 ffmpeg 支援的影片格式(MKV/MP4/AVI 非有效 class)
php occ config:system:set enabledPreviewProviders 0 --value='OC\Preview\PNG'
php occ config:system:set enabledPreviewProviders 1 --value='OC\Preview\JPEG'
php occ config:system:set enabledPreviewProviders 2 --value='OC\Preview\GIF'
php occ config:system:set enabledPreviewProviders 3 --value='OC\Preview\BMP'
php occ config:system:set enabledPreviewProviders 4 --value='OC\Preview\HEIC'
php occ config:system:set enabledPreviewProviders 5 --value='OC\Preview\TIFF'
php occ config:system:set enabledPreviewProviders 6 --value='OC\Preview\Movie'
php occ config:system:set enabledPreviewProviders 7 --value='OC\Preview\MP3'地區、時區、Cron、維護
bash
php occ config:system:set default_phone_region --value='TW'
php occ config:system:set default_timezone --value='Asia/Taipei'
php occ background:cron
php occ config:system:set maintenance_window_start --value=19 --type=integer
php occ db:add-missing-indices
php occ maintenance:repair --include-expensiveStep 4:Memories App 設置
bash
# 安裝 App
docker compose exec --user www-data -it app php occ app:install memories
docker compose exec --user www-data -it app php occ app:enable memories
docker compose exec --user www-data -it app php occ app:install previewgenerator # 注意:無底線
docker compose exec --user www-data -it app php occ app:install recognize
docker compose exec --user www-data -it app php occ app:enable photos
# Reverse Geocoding(下載 ~500MB 地理資料,需數分鐘)
docker compose exec --user www-data -it app php occ memories:places-setup
# 若 crash 可加 --transaction-size=5
# 首次掃描 + 索引
docker compose exec --user www-data -it app php occ files:scan --all
docker compose exec --user www-data -it app php occ memories:indexMemories Admin 設定(Settings → Administration → Memories):
| 設定項 | 值 |
|---|---|
| Enable transcoding | ✅ |
| Enable external transcoder | ✅ |
| Connection address | go-vod:47788 |
| Enable VA-API | ✅ |
| Quality Factor (CRF) | 24 |
Step 5:Recognize AI 設定
bash
docker compose exec --user www-data -it app php occ recognize:download-models
# 限制 1 核心 + nice 15,避免拖垮 NAS
docker compose exec --user www-data -it app php occ config:app:set recognize tensorflow.cores --value="1"
docker compose exec --user www-data -it app php occ config:app:set recognize nice_binary --value="/usr/bin/nice"
docker compose exec --user www-data -it app php occ config:app:set recognize nice_value --value="15"
# 首次完整分類(背景執行,斷點續跑)
docker compose exec --user www-data -T app bash -c \
'nohup php -d memory_limit=2G /var/www/html/occ recognize:classify > /tmp/recognize.log 2>&1 &'
# 查看進度
docker compose exec app tail -f /tmp/recognize.logStep 6:Recognize Ignore Markers(排除目錄)
對 External Storage 的目錄,標記檔必須存在於 Nextcloud filecache 中才生效:
bash
# Step 1:在 NAS 檔案系統建立空檔案
touch /volume1/data/.nomedia
touch /volume2/data2/.nomedia
# Step 2:讓 Nextcloud 掃描發現標記檔(--shallow 只掃該層)
docker compose exec --user www-data -T app php occ files:scan --path="admin/files/data" --shallow
docker compose exec --user www-data -T app php occ files:scan --path="admin/files/data2" --shallow| 標記檔 | 排除範圍 |
|---|---|
.noimage | 圖片辨識(物件標籤、人臉、地標) |
.nomusic | 音樂類型辨識 |
.novideo | 影片動作辨識 |
.nomedia | 以上全部 |
config.php 範例
php
<?php
$CONFIG = array (
'trusted_domains' => array (
0 => 'cloud.yourdomain.com',
1 => 'web', // go-vod 透過 http://web:80 存取,必須包含
2 => 'localhost',
),
'trusted_proxies' => array (
0 => '172.16.0.0/12',
1 => '10.0.0.0/8',
),
'overwriteprotocol' => 'https',
'overwrite.cli.url' => 'https://cloud.yourdomain.com',
'default_phone_region' => 'TW',
'default_timezone' => 'Asia/Taipei',
'maintenance_window_start' => 19, // UTC 19 = 台灣 03:00
'files.chunked_upload.max_size' => 99614720, // 95 MiB(Cloudflare Free 100MB 限制)
'memcache.local' => '\\OC\\Memcache\\APCu',
'memcache.distributed' => '\\OC\\Memcache\\Redis',
'memcache.locking' => '\\OC\\Memcache\\Redis',
'redis' => array (
'host' => 'redis',
'port' => 6379,
),
'enable_previews' => true,
'enabledPreviewProviders' => array (
0 => 'OC\\Preview\\PNG',
1 => 'OC\\Preview\\JPEG',
2 => 'OC\\Preview\\GIF',
3 => 'OC\\Preview\\BMP',
4 => 'OC\\Preview\\HEIC',
5 => 'OC\\Preview\\TIFF',
6 => 'OC\\Preview\\Movie',
),
'preview_max_x' => 4096,
'preview_max_y' => 4096,
'preview_ffmpeg_path' => '/usr/bin/ffmpeg',
'memories.vod.disable' => false, // 官方預設 true,刻意啟用
'memories.vod.vaapi' => true,
'memories.vod.external' => true,
'memories.vod.connect' => 'go-vod:47788',
'memories.vod.qf' => 24,
);常用維運指令
bash
# 服務狀態
docker compose ps
docker compose exec app supervisorctl status # 確認 php-fpm/cron/scan-cron 均 RUNNING
# Logs
docker compose logs -f app
docker compose logs -f go-vod
# 重啟單一程序
docker compose exec app supervisorctl restart cron
docker compose exec app supervisorctl restart scan-cron
# occ(必須 --user www-data)
docker compose exec --user www-data -it app php occ [command]
# 掃描 + 索引
docker compose exec --user www-data -it app php occ files:scan --all
docker compose exec --user www-data -it app php occ memories:index
# Preview Generator(初始化)
docker compose exec --user www-data -it app php occ preview:generate-all
# 宿主機 crontab 定期預生成(加 -T 避免 TTY 問題):
# */10 * * * * docker compose -f /volume1/docker/nextcloud/docker-compose.yaml exec --user www-data -T app php occ preview:pre-generate
# 備份
docker compose exec --user www-data -T app php occ maintenance:mode --on
docker compose exec -T db bash -c 'mysqldump -u root -p"$MYSQL_ROOT_PASSWORD" nextcloud' | gzip > backup_$(date +%Y%m%d).sql.gz
docker compose cp app:/var/www/html/config/config.php ./config-backup.php
docker compose exec --user www-data -T app php occ maintenance:mode --off常見問題排查
| 問題 | 原因 | 解法 |
|---|---|---|
go-vod 持續 Failed to fetch... Retrying | trusted_domains 未包含 web | 加入 php occ config:system:set trusted_domains 1 --value='web' |
Access through untrusted domain | trusted_domains 缺少域名 | 加入外部域名 + web + localhost |
| 大檔上傳失敗 | Nginx client_max_body_size 或 Cloudflare limit | 確認 Nginx 設 16G,PHP env 設 PHP_UPLOAD_LIMIT=16G,chunk ≤ 95 MiB |
Incorrect string value(Geocoding) | MariaDB 字元集非 utf8mb4 | MariaDB command 已設,若仍報錯執行 ALTER DATABASE nextcloud CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci |
| Recognize 管理面板持續顯示 Checking | J4125 無 AVX,WASM 模式 UI bug | 已知問題 #1123,不影響功能 |
相關概念
- Cloudflare Tunnel x Synology NAS 架構指南 — 外部安全存取的基礎架構
- NAS 自建 Nextcloud (LinuxServer.io) — 不需 Memories 時的簡化部署方案
- MariaDB Helm 部署(Kubernetes) — MariaDB 在 K8s 環境的部署參考
- Redis on Kubernetes 部署與維運 — Redis 在 K8s 環境的部署參考