Skip to content

Commit 204385c

Browse files
committed
fix: harden NAS storage setup and compatibility flow
1 parent 55f6289 commit 204385c

7 files changed

Lines changed: 125 additions & 44 deletions

File tree

.github/workflows/validate.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,5 @@ jobs:
6464
echo 'RCLONE_DIR_CACHE_TIME=30s' >> .env
6565
docker compose -f docker-compose.yml -f docker-compose.cloud-storage.yml --profile cloud-storage config >/dev/null
6666
docker compose -f docker-compose.yml -f docker-compose.cloud-storage.yml --profile cloud-storage --profile jellyfin config >/dev/null
67+
docker compose -f docker-compose.yml -f docker-compose.cloud-storage.yml --profile cloud-storage --profile tdarr-docker config >/dev/null
6768
rm .env

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ If you also use optional profiles, append them to the same command (example: `--
120120
Or include it in bootstrap:
121121

122122
```bash
123-
bash bootstrap.sh --cloud-storage
123+
bash bootstrap.sh --jellyfin --cloud-storage
124124
```
125125

126126
### NAS Quick Start
@@ -133,7 +133,7 @@ docker compose -f docker-compose.yml -f docker-compose.cloud-storage.yml --profi
133133
Or include it in bootstrap:
134134

135135
```bash
136-
bash bootstrap.sh --nas-storage
136+
bash bootstrap.sh --jellyfin --nas-storage
137137
```
138138

139139
### How It Works
@@ -176,7 +176,7 @@ cloud-upload --> periodically moves stable files to remote (6h/24h for cloud,
176176

177177
**Platform notes:**
178178
- **TrueNAS:** Media path is typically `/mnt/pool/dataset/media`
179-
- **Synology:** Path is `/volume1/media`. The setup wizard auto-adds `--sftp-path-override` for SFTP chroot compatibility.
179+
- **Synology:** Path is `/volume1/media`. The setup wizard auto-adds a `path_override` config (equivalent to `--sftp-path-override`) for SFTP chroot compatibility.
180180
- **Unraid:** Path is `/mnt/user/media`
181181

182182
**Performance:** rclone SFTP delivers ~100MB/s on Gigabit LAN, adequate for multiple concurrent 4K streams.
@@ -275,7 +275,7 @@ bash bootstrap.sh --jellyfin
275275
NAS storage via SFTP (TrueNAS, Synology, Unraid):
276276

277277
```bash
278-
bash bootstrap.sh --nas-storage
278+
bash bootstrap.sh --jellyfin --nas-storage
279279
```
280280

281281
## Update Existing Clone

SETUP.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ The setup wizard will:
363363
docker compose -f docker-compose.yml -f docker-compose.cloud-storage.yml --profile cloud-storage --profile jellyfin --profile tdarr-docker up -d
364364
```
365365

366-
Or pass `--cloud-storage` to `bootstrap.sh` for a full install.
366+
Or pass `--jellyfin --cloud-storage` to `bootstrap.sh` for a full install.
367367

368368
### NAS Setup (TrueNAS, Synology, Unraid)
369369

@@ -375,7 +375,7 @@ The wizard will:
375375
1. Ask for your NAS hostname/IP and SSH username
376376
2. Offer to generate an SSH key pair (recommended), use an existing key, or use password auth
377377
3. Ask for your media path with platform-specific examples
378-
4. Auto-detect Synology paths and add `--sftp-path-override` for SFTP chroot compatibility
378+
4. Auto-detect Synology paths and add `path_override` (same effect as `--sftp-path-override`) for SFTP chroot compatibility
379379
5. Test connectivity before writing the config
380380
6. Write NAS-optimized VFS cache settings to `.env`
381381

@@ -390,13 +390,13 @@ The wizard will:
390390
docker compose -f docker-compose.yml -f docker-compose.cloud-storage.yml --profile cloud-storage --profile jellyfin --profile tdarr-docker up -d
391391
```
392392

393-
Or use bootstrap: `bash bootstrap.sh --nas-storage`
393+
Or use bootstrap: `bash bootstrap.sh --jellyfin --nas-storage`
394394

395395
### How it works
396396

397-
- **Write path:** Downloads land on local disk (fast). Every 6 hours, `cloud-upload.sh` moves files older than 24h to the cloud via `rclone move`.
398-
- **Read path:** Docker services (Jellyfin, Arr apps, Tdarr Docker) read from the mergerfs overlay, which transparently serves files from local or cloud storage.
399-
- **VFS cache:** rclone caches recently accessed cloud files locally (default 50GB, 72h max age). Adjust `RCLONE_VFS_CACHE_MAX_SIZE` in `.env` based on your available disk space.
397+
- **Write path:** Downloads land on local disk (fast). `cloud-upload.sh` periodically moves stable files to remote storage (cloud default: every 6h; NAS default: every 2h).
398+
- **Read path:** Docker services (Jellyfin, Arr apps, Tdarr Docker) read from the mergerfs overlay, which transparently serves files from local or remote storage.
399+
- **VFS cache:** rclone caches recently accessed remote files locally. Cloud defaults are larger (50GB/72h), NAS defaults are smaller/faster (10GB/1h).
400400

401401
### Library paths when cloud storage is enabled
402402

bootstrap.sh

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -372,13 +372,21 @@ COMPOSE_PROFILES=()
372372

373373
# Detect cloud storage from .env (may have been set by setup-cloud-storage.sh or flag)
374374
CLOUD_ENABLED_ENV=""
375+
STORAGE_TYPE_ENV=""
375376
if [[ -f .env ]]; then
376377
CLOUD_ENABLED_ENV=$(sed -n 's/^CLOUD_STORAGE_ENABLED=//p' .env | head -1)
378+
STORAGE_TYPE_ENV=$(sed -n 's/^STORAGE_TYPE=//p' .env | head -1)
377379
fi
378380
CLOUD_ACTIVE=false
379381
if [[ "$CLOUD_STORAGE" == true || "$CLOUD_ENABLED_ENV" == "true" ]]; then
380382
CLOUD_ACTIVE=true
381383
fi
384+
STORAGE_TYPE_EFFECTIVE="$STORAGE_TYPE_ENV"
385+
if [[ "$NAS_STORAGE" == true ]]; then
386+
STORAGE_TYPE_EFFECTIVE="nas"
387+
elif [[ -z "$STORAGE_TYPE_EFFECTIVE" && "$CLOUD_ACTIVE" == true ]]; then
388+
STORAGE_TYPE_EFFECTIVE="cloud"
389+
fi
382390

383391
if [[ "$CLOUD_ACTIVE" == true && "$MEDIA_SERVER" == "plex" ]]; then
384392
echo -e "${RED}Cloud storage requires Jellyfin in this stack.${NC}"
@@ -388,7 +396,11 @@ if [[ "$CLOUD_ACTIVE" == true && "$MEDIA_SERVER" == "plex" ]]; then
388396
echo " Native Plex cannot read those merged cloud mount paths on macOS."
389397
echo ""
390398
echo "Use one of these options:"
391-
echo " 1. Re-run with Jellyfin: bash bootstrap.sh --jellyfin --cloud-storage"
399+
if [[ "$STORAGE_TYPE_EFFECTIVE" == "nas" ]]; then
400+
echo " 1. Re-run with Jellyfin: bash bootstrap.sh --jellyfin --nas-storage"
401+
else
402+
echo " 1. Re-run with Jellyfin: bash bootstrap.sh --jellyfin --cloud-storage"
403+
fi
392404
echo " 2. Disable cloud storage and keep Plex: set CLOUD_STORAGE_ENABLED=false in .env"
393405
exit 1
394406
fi
@@ -503,7 +515,7 @@ echo " Remaining manual steps:"
503515
if [[ "$MEDIA_SERVER" == "jellyfin" ]]; then
504516
echo " 1. Complete Jellyfin setup wizard at http://localhost:8096"
505517
if [[ "$CLOUD_ACTIVE" == true ]]; then
506-
echo " Add libraries: Movies = /data/movies, TV Shows = /data/tvshows (merged cloud/local)"
518+
echo " Add libraries: Movies = /data/movies, TV Shows = /data/tvshows (merged remote/local)"
507519
else
508520
echo " Add libraries: Movies = /data/movies, TV Shows = /data/tvshows"
509521
fi
@@ -517,7 +529,7 @@ else
517529
fi
518530
echo ""
519531
if [[ "$CLOUD_ACTIVE" == true ]]; then
520-
if [[ "$NAS_STORAGE" == true ]]; then
532+
if [[ "$STORAGE_TYPE_EFFECTIVE" == "nas" ]]; then
521533
echo " NAS storage: enabled (rclone SFTP + mergerfs)"
522534
echo " Uploads run every 2 hours via launchd"
523535
else

scripts/doctor.sh

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ GREEN='\033[0;32m'
99
YELLOW='\033[1;33m'
1010
CYAN='\033[0;36m'
1111
NC='\033[0m'
12+
RCLONE_IMAGE="${RCLONE_IMAGE:-rclone/rclone@sha256:c08f5e100e1c4fa4deb1315b56a47c0cc0e765222b7c0834bc93305f2e4d85c0}"
1213

1314
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
1415
ENV_FILE="$SCRIPT_DIR/.env"
@@ -243,6 +244,10 @@ if [[ -f "$ENV_FILE" ]]; then
243244
fi
244245

245246
if [[ "$STORAGE_TYPE" == "nas" ]]; then
247+
if [[ "$CLOUD_STORAGE_ENABLED" != "true" ]]; then
248+
warn "STORAGE_TYPE=nas is set but CLOUD_STORAGE_ENABLED is not true"
249+
fi
250+
246251
nas_key="$MEDIA_DIR/config/rclone/nas_key.pem"
247252
if [[ -f "$nas_key" ]]; then
248253
ok "NAS SSH key exists"
@@ -259,10 +264,15 @@ if [[ "$STORAGE_TYPE" == "nas" ]]; then
259264
# Test NAS connectivity
260265
rclone_conf="$MEDIA_DIR/config/rclone/rclone.conf"
261266
env_remote=$(sed -n 's/^RCLONE_REMOTE=//p' "$ENV_FILE" | head -1)
267+
env_remote_path=$(sed -n 's/^RCLONE_REMOTE_PATH=//p' "$ENV_FILE" | head -1)
262268
if [[ -n "$env_remote" && -f "$rclone_conf" ]]; then
269+
nas_target="${env_remote}:"
270+
if [[ -n "$env_remote_path" ]]; then
271+
nas_target="${env_remote}:${env_remote_path}"
272+
fi
263273
if docker run --rm \
264274
-v "$MEDIA_DIR/config/rclone:/config/rclone" \
265-
rclone/rclone lsd "${env_remote}:" \
275+
"$RCLONE_IMAGE" lsd "$nas_target" \
266276
--contimeout 10s 2>/dev/null; then
267277
ok "NAS reachable via rclone SFTP"
268278
else

scripts/install-launchd-jobs.sh

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,6 @@ if [[ "${CLOUD_STORAGE_ENABLED:-}" == "true" ]]; then
115115
upload_interval=21600
116116
if [[ "${STORAGE_TYPE:-}" == "nas" ]]; then
117117
upload_interval=7200
118-
elif [[ -n "${CLOUD_UPLOAD_MIN_AGE_HOURS:-}" ]]; then
119-
upload_interval=$((CLOUD_UPLOAD_MIN_AGE_HOURS * 3600))
120118
fi
121119
install_plist "cloud-upload" "$upload_interval" "$SCRIPT_DIR/cloud-upload.sh" "false"
122120
fi

0 commit comments

Comments
 (0)