5.6 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. Port: 18787.
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=18787. 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.
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:18787; 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=0 /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.