2026-10-02 00:03:48 +08:00
|
|
|
# Synology NAS deployment / 群晖部署
|
|
|
|
|
|
2026-10-02 00:32:53 +08:00
|
|
|
Target: DS923+, DSM 7.4.1 (verified on target; originally specified 7.3.1), AMD64, Container Manager.
|
2026-10-16 16:25:42 +08:00
|
|
|
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.
|
2026-10-02 00:03:48 +08:00
|
|
|
|
|
|
|
|
## 1. Package and upload / 打包上传
|
|
|
|
|
|
|
|
|
|
From the repository:
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
```sh
|
2026-10-02 00:32:53 +08:00
|
|
|
mkdir -p /volume2/docker/pigway-frame-flow
|
2026-10-02 00:03:48 +08:00
|
|
|
# Only for an empty/new target:
|
2026-10-02 00:32:53 +08:00
|
|
|
tar -xzf /tmp/pigway-frame-flow-nas.tar.gz -C /volume2/docker
|
|
|
|
|
cd /volume2/docker/pigway-frame-flow
|
2026-10-02 00:03:48 +08:00
|
|
|
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;
|
2026-10-16 16:25:42 +08:00
|
|
|
WEB_PORT=6005. The latter binds every NAS interface, so use the LAN IP when
|
2026-10-02 00:03:48 +08:00
|
|
|
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.
|
|
|
|
|
|
2026-10-16 18:58:01 +08:00
|
|
|
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.
|
|
|
|
|
|
2026-10-02 00:03:48 +08:00
|
|
|
## 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.
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
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
|
2026-10-16 16:25:42 +08:00
|
|
|
to http://NAS_LAN_IP:6005; FRAME_FLOW_TOKEN stays in private env/file.
|
2026-10-02 00:03:48 +08:00
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
```
|
2026-10-02 00:32:53 +08:00
|
|
|
/volume2/docker/pigway-frame-flow/
|
2026-10-02 00:03:48 +08:00
|
|
|
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.
|
2026-10-02 00:32:53 +08:00
|
|
|
|
|
|
|
|
## 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:
|
|
|
|
|
|
|
|
|
|
```sh
|
2026-10-02 00:50:59 +08:00
|
|
|
sudo env DOCKER_BUILDKIT=1 /usr/local/bin/docker compose build \
|
2026-10-18 19:10:00 +08:00
|
|
|
--build-arg PYTHON_BASE=python:3.14.8-alpine3.24
|
2026-10-02 00:32:53 +08:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This per-build override does not change global registry or network configuration.
|
2026-10-02 00:41:24 +08:00
|
|
|
|
2026-10-18 19:10:00 +08:00
|
|
|
A slow Alpine CDN can be overridden for this build, retaining APK signature
|
2026-10-02 00:41:24 +08:00
|
|
|
verification and changing no NAS system settings:
|
|
|
|
|
|
|
|
|
|
```sh
|
2026-10-18 19:10:00 +08:00
|
|
|
sudo /usr/local/bin/docker compose build --build-arg ALPINE_MIRROR=mirrors.tuna.tsinghua.edu.cn
|
2026-10-02 00:41:24 +08:00
|
|
|
```
|
2026-10-02 00:49:07 +08:00
|
|
|
|
2026-10-18 19:10:00 +08:00
|
|
|
Optional `build-cache/wheels/` can hold all locked Linux AMD64/Python 3.14 musllinux wheels
|
2026-10-02 01:02:49 +08:00
|
|
|
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.
|