14 KiB
Synology NAS Deployment Guide
This guide covers migrating the terra-view stack from a generic Linux host
(currently the home server at 10.0.0.44) to an always-on Synology NAS in
the office, including data migration and the minimal external-access
networking layer.
Table of Contents
- Architecture overview
- Pre-requisites
- Phase 1 — Pre-stage on the NAS (no downtime)
- Phase 2 — Data migration (~10 min window)
- Phase 3 — Repoint the watcher (download2-PC)
- Phase 4 — External access for remote operators
- Phase 5 — Decommission home server
- Verification checklist
- Rollback plan
- Gotchas
Architecture overview
The terra-view stack is three containers:
| Service | Port | What writes to it | Where it lives |
|---|---|---|---|
| terra-view | 8001 | Operators (UI), watchers (heartbeat) | Synology NAS |
| SFM | 8200 | Watchers (Blastware ACH forwards) | Synology NAS |
| SLMM | 8100 | terra-view (proxied), SLMs on LAN | Synology NAS |
Everything that writes to the stack lives inside the office LAN:
- download2-PC is the series3-watcher host. It has a static office IP and POSTs to terra-view's heartbeat endpoint plus SFM's Blastware import endpoint. Both flows are LAN-internal.
- Sound level meters (NL-43) sit on the office LAN; SLMM reaches them
via
network_mode: host.
The only thing that needs to cross the office firewall is operator UI access from outside the office (laptops, phones, working from home). That makes the external networking layer trivial — see Phase 4.
Pre-requisites
On the Synology side:
- DSM 7.2+ with Container Manager installed (Package Center). Older "Docker" package works too — same engine, different menu names.
- x86_64 model (Plus / Value / XS series). ARM j-series will build but expect a slower first build.
- Static LAN IP reserved for the NAS in the office router's DHCP table. Devices on the LAN must have a stable target.
- SSH enabled — Control Panel → Terminal & SNMP → Enable SSH service.
- Shared folder for the stack — e.g.
/volume1/docker/.
On the home server side:
- Working terra-view / SFM / SLMM stack you want to migrate.
rsyncavailable (it almost certainly is).
You will also need:
- An admin account on the Synology with sudo privileges.
- Network access between the home server and the NAS during the migration window (or USB-drive shuttle if not).
Phase 1 — Pre-stage on the NAS (no downtime)
Goal: get the NAS booting an empty stack so you can validate the build and networking before touching any production data.
1.1 Clone the repos
SSH to the NAS as admin:
sudo mkdir -p /volume1/docker
cd /volume1/docker
sudo git clone <your-terra-view-remote> terra-view
sudo git clone <your-slmm-remote> slmm
sudo git clone <your-seismo-relay-remote> seismo-relay
cd terra-view
sudo git checkout main # or whichever branch you ship from
1.2 Build images
cd /volume1/docker/terra-view
sudo docker compose build
First build takes 5–15 min depending on model.
1.3 Boot the empty stack
sudo docker compose up -d
Hit http://<nas-lan-ip>:1001 (dev profile) or :8001 (prod profile) from
another office machine. You should see an empty fleet roster. If that
works, the NAS can run the stack — proven before any production data is
at risk.
1.4 Stop the NAS stack again
sudo docker compose stop
We're ready for the data migration.
Phase 2 — Data migration (~10 min window)
The terra-view stack is stateful in three places. All three must be moved together for consistency.
| Service | Data location (home server) |
|---|---|
| terra-view | /home/serversdown/terra-view/data/ |
| SLMM | /home/serversdown/slmm/data/ |
| SFM | /home/serversdown/seismo-relay/data/ |
2.1 Stop writes on both sides
On the NAS:
cd /volume1/docker/terra-view
sudo docker compose stop
On the home server:
cd /home/serversdown/terra-view
docker compose stop terra-view slmm sfm
2.2 rsync the data dirs
From the home server (or anywhere with SSH access to both):
rsync -avh /home/serversdown/terra-view/data/ admin@<nas-lan-ip>:/volume1/docker/terra-view/data/
rsync -avh /home/serversdown/slmm/data/ admin@<nas-lan-ip>:/volume1/docker/slmm/data/
rsync -avh /home/serversdown/seismo-relay/data/ admin@<nas-lan-ip>:/volume1/docker/seismo-relay/data/
2.3 Fix ownership on the NAS
Synology admin is usually UID 1026, GID 100. Inside containers running
as root, this doesn't matter — but if you've configured user: in any
compose file it will. Safe default:
ssh admin@<nas-lan-ip> "sudo chown -R 1026:100 \
/volume1/docker/terra-view/data \
/volume1/docker/slmm/data \
/volume1/docker/seismo-relay/data"
2.4 Run any pending migrations
Some earlier feature work added migration scripts that need to run once per database. After the rsync, before starting the stack, check what's pending:
ssh admin@<nas-lan-ip>
cd /volume1/docker/terra-view
ls backend/migrate_*.py
Run each one inside the container (after starting it temporarily) or apply them on the host with the same Python environment. Idempotent migrations re-run safely.
2.5 Start the NAS stack
ssh admin@<nas-lan-ip> \
"cd /volume1/docker/terra-view && sudo docker compose up -d"
2.6 Spot-check
- Dashboard loads with real units
/sfmpage lists historical events- A photo loads on a unit detail page
- SFM/HB badge mix on the active table matches what you saw on the home server
If anything's off, see Rollback plan.
Phase 3 — Repoint the watcher (download2-PC)
The download2-PC is the one client we have to reconfigure. It currently POSTs to the home server. Two endpoints to change:
-
terra-view heartbeat URL —
http://<old-home-ip>:8001/api/series3/heartbeat→http://<new-nas-lan-ip>:8001/api/series3/heartbeat -
SFM Blastware import URL —
http://<old-home-ip>:8200/db/import/blastware_file→http://<new-nas-lan-ip>:8200/db/import/blastware_fileOr, if you want to keep SFM container-internal and not publish 8200 on the LAN at all, point it through terra-view's existing SFM proxy: →
http://<new-nas-lan-ip>:8001/api/sfm/db/import/blastware_file
Update the config, restart the watcher service, and confirm the next heartbeat lands in the NAS DB (check the Recent Call-Ins card on the dashboard).
Tip: keep the home server running in parallel for 1–2 days. If you forget to repoint something, it'll still flow into the old DB and you can resync.
Phase 4 — External access for remote operators
Only the terra-view UI needs to be reachable from outside the office. Two clean options — pick one.
Option A — Tailscale (recommended for small teams)
Zero port forwards, zero certs, zero public DNS, zero reverse proxy.
- Install Tailscale from Synology Package Center, sign in.
- Install Tailscale on each operator's laptop/phone, sign in to the same tailnet.
- Operators access
http://<nas-tailscale-ip>:8001from anywhere.
That's the whole setup. The office network has no external exposure at all.
Option B — Reverse proxy with Let's Encrypt
If you want a https://terraview.yourdomain.com URL that any browser can
reach:
B.1 Port forward on the office router
WAN 443 → <nas-lan-ip>:443
WAN 80 → <nas-lan-ip>:80 (only needed for Let's Encrypt HTTP-01;
skip if you use DNS-01 challenge)
Do not forward 1001, 8001, 8100, or 8200.
B.2 Public DNS
- Free: Synology DDNS (Control Panel → External Access → DDNS) — gives
you
something.synology.me. - Better: your own domain with an A record → office WAN IP, or a CNAME → Synology DDNS hostname (handles dynamic IPs automatically).
B.3 Let's Encrypt certificate
Control Panel → Security → Certificate → Add → "Get a certificate from Let's Encrypt." DSM handles renewal.
B.4 Synology reverse proxy
Control Panel → Login Portal → Advanced → Reverse Proxy → Create:
Source: Hostname terraview.yourdomain.com
Protocol HTTPS
Port 443
Destination: Hostname localhost
Protocol HTTP
Port 8001
Under "Custom Header", add:
| Header | Value |
|---|---|
X-Forwarded-For |
$proxy_add_x_forwarded_for |
X-Forwarded-Proto |
$scheme |
Host |
$host |
Tick the WebSocket support checkbox.
B.5 DSM firewall
Control Panel → Security → Firewall → enable:
- 443/TCP from
Anywhere— allow - 80/TCP from
Anywhere— allow (cert renewal only) - Everything else from WAN — deny
- All from LAN — allow
Optional: geo-block to your country if your operators are domestic only. Big reduction in scanning noise.
Phase 5 — Decommission home server
After 1–2 weeks of stable NAS operation:
- Take a final
docker compose downon the home server. - Archive
/home/serversdown/{terra-view,slmm,seismo-relay}/data/to a backup volume. - Free the home server hardware.
Verification checklist
After Phase 2 (data migration):
http://<nas-lan-ip>:8001/loads dashboard with real units- Recent Alerts, Call-Ins (2 cols), Fleet Summary across the top
- SFM/HB badge mix on the active table looks sane
/sfmpage lists historical events (the same count as before)- A unit detail page loads with photos rendering
/api/recent-event-callinsreturns 200 with real data/api/status-snapshotreturns 200,sfm_reachable: true
After Phase 3 (watcher cutover):
- Next heartbeat from download2-PC lands in NAS DB
- A new event arrives in
/sfmpage on the NAS within the next Blastware ACH cycle - No errors in
docker logs terra-view-terra-view-1
After Phase 4 (external access):
- (Option A) Operator laptop on tailnet can reach
http://<nas-tailscale-ip>:8001 - (Option B)
https://terraview.yourdomain.comresolves, cert is valid, dashboard loads - (Option B) Office DSM admin (5001) is not reachable from outside
Rollback plan
The home server stays alive in parallel through Phases 2–3 as a safety net. If anything goes wrong on the NAS:
- On the home server:
cd /home/serversdown/terra-view docker compose up -d - Point download2-PC back at the home server IP.
- NAS data isn't lost — it's just sitting idle. Investigate, fix, retry.
The "irreversible" point is when you decommission the home server in Phase 5. Until then, you can always fall back.
Gotchas
-
Synology UID/GID quirks. Synology admin is usually
1026:100. Containers running as root inside don't care, but if your compose files setuser:, mismatched UIDs cause SQLite "readonly database" errors. Easiest fix: omituser:and let containers run as root. -
network_mode: hostfor SLMM. Required for LAN-direct comms with sound level meters. On Synology this binds to the NAS's interface — confirm nothing else on the NAS uses ports 8100 or 21 (FTP). -
Auto-start on boot. Container Manager → Project → Settings → enable "Auto-restart". Otherwise a DSM update or NAS reboot drops the stack.
-
restart: unless-stoppedin compose. Verify every service has it. DSM occasionally restarts Docker during DSM updates — this flag ensures everything comes back. -
Hyper Backup. Schedule a daily snapshot of
/volume1/docker/terra-view/data/to a USB drive or off-site. SQLite- small photo dir = trivially small backups. The DB-Management UI's built-in snapshots are an additional layer but not a replacement.
-
NAT loopback (Option B only). If your office router doesn't support hairpinning, machines INSIDE the office can't reach the NAS by its public hostname — they have to use the LAN IP. Most modern routers handle this; some ISP-provided ones don't. Test from a laptop on the office Wi-Fi.
-
Let's Encrypt rate limits (Option B only). 5 issuances per domain per week. Don't fat-finger DNS or you'll be locked out. Test with the staging endpoint first if unsure.
-
host.docker.internalresolution. terra-view'sSFM_BASE_URL=http://host.docker.internal:8200relies on Docker's internal DNS. Works on DSM 7.2+ in bridge mode. If you see "name not resolved" errors, fall back to explicit container names with a custom network in compose. -
SFM stale rows. The SFM SQLite has a few rows in
monitor_logandach_sessionsfrom earlier Python-ACH experiments. Harmless to bring over — invisible to terra-view's UI under the watcher-forward pipeline.
Suggested timeline
For a low-risk migration:
- Week 1: Phase 1. Get the NAS booting an empty stack. No production touch.
- Week 2, day 1: Phase 2. Migrate data. 10-min window. Keep home server alive in parallel.
- Week 2, day 1: Phase 3. Repoint download2-PC. Watch heartbeats land on the NAS for the rest of the day.
- Week 3: Phase 4. Add Tailscale or reverse-proxy access for remote operators.
- Week 4–5: Monitor. Confirm everything's stable. Then Phase 5 (decommission home server).
Splitting "make it work on LAN" from "expose it remotely" means you debug one thing at a time.