6.3 KiB
Synology NAS deployment / 群晖部署
Target: DS923+, DSM 7.4.1 (verified on target; originally specified 7.3.1), AMD64, Container Manager. Directory: /volume2/docker/pigway-frame-flow. Default published port: 6005. The container still listens on 18787; Compose maps host 6005 to container 18787. Existing installations must update their own WEB_PORT and recreate the service to activate the change.
1. Package and upload / 打包上传
From the repository:
python3 scripts/package-nas.py
scp -O dist/pigway-frame-flow-nas.tar.gz nas-example:/tmp/pigway-frame-flow-nas.tar.gz
ssh nas-example
The package contains tracked source/config examples/scripts only, never .env,
actual keys, data or .git. It is a deployment artifact, not a Git backup.
Use git bundle create frame-flow.bundle --all separately for history backups.
2. Prepare project directory / 准备目录
Verify target is empty or explicitly backed up before extracting. Do not extract onto another existing installation or other services. If the directory exists, inspect it first; preserve its .env and data.
mkdir -p /volume2/docker/pigway-frame-flow
# Only for an empty/new target:
tar -xzf /tmp/pigway-frame-flow-nas.tar.gz -C /volume2/docker
cd /volume2/docker/pigway-frame-flow
mkdir -p data/images data/generated data/cache config
sudo chown -R 10333:10333 data config
cp .env.example .env
chmod 600 .env
Edit .env on the NAS. Set FRAME_FLOW_TOKEN to a strong random token. API key
stays empty. KOKONNA_MOCK=true and KOKONNA_WRITE_ENABLED=false.
Set WEB_BIND to the actual NAS LAN address (recommended) or explicitly 0.0.0.0;
WEB_PORT=6005. The latter binds every NAS interface, so use the LAN IP when
strict LAN-only binding is required. No firewall/router/proxy edits are performed.
Copy deploy/devices.yml.example to config/devices.yml if multiple devices are desired.
Set FRAME_FLOW_STORAGE_NAME to the NAS display name. The gallery tab shows
this name followed by “本地图库” (or “Local gallery”). For Actions deployment,
set the repository Variable of the same name. It is a display label, not a path.
3. Validate, build, start / 验证构建启动
Synology Docker is /usr/local/bin/docker. The SSH user may require sudo. Do not change socket ownership, groups, Docker network settings or other containers.
sudo /usr/local/bin/docker compose config --quiet
sudo /usr/local/bin/docker compose build --pull
sudo /usr/local/bin/docker compose up -d
sudo /usr/local/bin/docker compose ps
sudo /usr/local/bin/docker compose logs --tail 100
sudo /usr/local/bin/docker inspect pigway-frame-flow \
--format 'id={{.Id}} started={{.State.StartedAt}} health={{.State.Health.Status}} restart={{.HostConfig.RestartPolicy.Name}} ports={{json .NetworkSettings.Ports}} mounts={{json .Mounts}} network={{json .NetworkSettings.Networks}}'
sudo /usr/local/bin/docker image inspect pigway-frame-flow:latest \
--format 'id={{.Id}} size={{.Size}} architecture={{.Architecture}}'
Do not print unfiltered docker inspect: its environment can contain keys/tokens.
Likewise do not print docker compose config without --quiet on a live installation.
Run the read-only smoke checker from a machine with Python and set FRAME_FLOW_URL
to http://NAS_LAN_IP:6005; FRAME_FLOW_TOKEN stays in private env/file.
Health: GET /health → 200. Readiness: GET /ready → 200 when SQLite, driver config, auth and Scheduler are ready. Missing credentials in live mode → 503. The cloud is not probed by readiness. Cloud loss does not cause healthcheck restart loops.
4. Verify persistence / 持久化
Create a disabled test schedule in Web UI and locally stage one image (Mock only). Record the job and asset IDs, then:
sudo /usr/local/bin/docker compose restart pigway-frame-flow
Wait for healthy state, check /health and /ready, and verify the same job/asset IDs and history remain. Database is data/frameflow.db with WAL/SHM alongside it; back up while stopped or use SQLite backup API, not a lone live DB file copy. Restart policy must read unless-stopped. Do not reboot the NAS for acceptance.
5. Live read-only verification / 实机只读
After Mock acceptance, fill KOKONNA_API_KEY; set mock=false, keep writes=false. Recreate only this service to apply env. Query device and images metadata only. Verify known model/geometry/timezone/name against real data; missing vendor model fields are not claimed verified. No original downloads are necessary. Stop on mismatch; never try to fix cloud state automatically. Obtain explicit owner permission before any upload/switch/delete. See REAL_DEVICE_TEST.md.
Layout
/volume2/docker/pigway-frame-flow/
docker-compose.yml
Dockerfile
.env
requirements.lock
pigway_frame_flow/
vendor/
scripts/
config/
data/
frameflow.db
images/
generated/
cache/
Container runs UID/GID 10333:10333; only data/config need ownership. Avoid recursive ownership changes outside this project's paths. Standard Compose bridge is private and isolated by project name. Logs use stdout/stderr with Docker's capped json-file. No logs directory or Docker socket mount.
Restricted Docker Hub connectivity
If Docker Hub is unreachable from NAS, import the exact official AMD64 base image from a trusted download machine. Verify its manifest, compressed blob SHA-256 and all uncompressed rootfs diff IDs before loading. With the verified local tag loaded:
sudo env DOCKER_BUILDKIT=1 /usr/local/bin/docker compose build \
--build-arg PYTHON_BASE=python:3.14.5-slim-bookworm
This per-build override does not change global registry or network configuration.
A slow Debian CDN can be overridden only for this build, retaining APT GPG verification and changing no NAS system settings:
sudo /usr/local/bin/docker compose build --build-arg DEBIAN_MIRROR=mirrors.aliyun.com
Optional build-cache/debs/fonts-noto-cjk.deb avoids a slow build-network download.
Dockerfile verifies it against SHA-256 from the GPG-authenticated APT index before use.
The cache is not a runtime mount or committed dependency, and empty-cache builds work.
Optional build-cache/wheels/ can hold all 24 Linux AMD64/Python 3.14 wheels
and SHA256SUMS, verified against official PyPI release metadata. Builds verify
the manifest and use pip --no-index; incomplete caches fail explicitly.
Build commit/time are set after dependency installation to preserve build cache.