Introduction β The 60 Mpx Problem
A single Sony a7r IV RAW β 120 MB. A typical shoot: 50β200 shots β 2β5 GB of RAW + 500 MBβ1 GB of exports (DNG masters, web versions, social cuts).
My goal: zero friction between capture, development, backup, and publishing. No manual copies, no βwhereβs that file?β, no full disk at the worst moment.
Global Architecture β Big Picture

Legend:
π’ Local (NVMe SSD) β π΅ Master Storage (NAS ZFS) β π Delivery (Cloudinary CDN) β π©· Off-site Backup (pCloud encrypted) β π£ Search (Immich)
Step 1 β Ingestion & Culling (Lightroom)
- Import directly to local NVMe SSD (max speed, zero network latency)
- Cull & reject before copying to NAS β only keep whatβs worth keeping
- Standardized naming:
YYYYMMDD_Collection_XXXX.ARW
Example: 20260315_Murmurs_0042.ARW
- IPTC metadata at import (via Lightroom import preset):
- Copyright:
Β© FrΓ©dΓ©ric Le Monnier
- Location (GPS if available, else city/region)
- Hierarchical keywords:
Landscape > Forest, Technique > Long Exposure, etc.
π‘ Tip: a βPortfolioβ import preset applies naming + IPTC + base keywords in one click.
Step 2 β Development & Exports (Photoshop)
Direct follow-up to my Photoshop Recipe #1 (dominant color cast fix) and future recipes.
From the developed master (PSD/TIFF 16-bit), three automated exports via a Photoshop βExport Trioβ action:
| Use Case |
Format |
Max Dimension |
Compression |
Destination |
| Archive Master |
TIFF 16-bit / PSD |
9504 Γ 6336 (full 60 Mpx) |
Lossless (ZIP) |
01_DEVELOPPEES/ |
| Web Portfolio |
AVIF / WebP |
2400 px (long edge) |
q_auto:eco |
02_EXPORTS_WEB/ |
| Social Media |
JPEG |
1600 px (long edge) |
q=80 |
03_EXPORTS_SOCIAL/ |
βExport Trioβ Photoshop Action (one keyboard shortcut):
- Duplicate master β convert to 8-bit sRGB
- Resize + output sharpening (bicubic, 0.3 px)
- Save 3 versions to correct NAS folders (via mounted SMB path)
- Write clean EXIF/IPTC (no Photoshop history bloat)
Step 3 β NAS: Master Storage & Network Access
Hardware (details in Why a NAS?):
- TrueNAS Scale (native ZFS)
- AMD Ryzen 7 + 64 GB DDR5 ECC
- 7 Γ Seagate IronWolf 4 TB β RAID-Z2 β 10 TB usable, 2-drive failure tolerance
Folder Structure (mounted as Z: / /Volumes/Photos):
/mnt/pool/photos/
βββ 00_RAW_MASTERS/ # Original RAW (read-only after validated import)
β βββ 2026/03_Murmurs/
βββ 01_DEVELOPPEES/ # Developed TIFF/PSD masters
β βββ 2026/03_Murmurs/
βββ 02_EXPORTS_WEB/ # AVIF/WebP for Cloudinary
β βββ 2026/03_Murmurs/
βββ 03_EXPORTS_SOCIAL/ # JPEG for IG/Bluesky
βββ 2026/03_Murmurs/
ZFS Protections:
- Daily snapshots (kept 30 days) β instant recovery of deleted/modified files
- Monthly scrub (1st of month, 03:00) β silent error detection/correction
- Local replication: 2nd USB pool for monthly cold copy (drive powered off)
Step 4 β Off-site Backup: pCloud (Client-side Encrypted)
TrueNAS β pCloud via rclone + crypt (nightly cron 02:00):
# /root/scripts/rclone_crypt_sync.sh
rclone sync /mnt/pool/photos/ pcloud_crypt:photos \
--transfers 4 --checkers 8 --bwlimit 20M \
--log-file /var/log/rclone_pcloud.log \
--progress
- Client-side encryption:
rclone config β pcloud_crypt (crypt on top of pcloud:) β pCloud only sees encrypted filenames + encrypted blobs
- Retention: 90-day versioning + 1-year trash (pCloud settings)
- Cost: ~β¬180 β2 TB lifetimeβ (promo) β β¬0/year recurring
β οΈ Annual restore test: I pull a 2023 RAW from pCloud β verify integrity (SHA256 hash) β OK.
Auto-upload: Python watchdog script watches 02_EXPORTS_WEB/ β Cloudinary API upload on new file detection.
Cloudinary Presets (configured in dashboard):
| Preset |
Transformation |
Use Case |
portfolio_large |
w_2400,f_auto,q_auto:eco |
Main article image / lightbox |
portfolio_thumb |
w_600,c_fill,g_auto,f_auto,q_auto |
Grids, thumbnails, search |
og_image |
w_1200,h_630,c_fill,g_auto,f_auto |
Open Graph / Twitter Card |
Jekyll Integration: each collection photo has a cloudinary_id in front-matter β custom Liquid tag:
{ % cloudinary portfolio_large { { page.cloudinary_id } } alt:"{ { page.title } }" % }
Generates automatically:
<picture> with source type="image/avif", source type="image/webp", fallback <img>
- Responsive
srcset (400w, 800w, 1200w, 2400w)
loading="lazy" + decoding="async"
Signed URLs (1-year expiry) β cache control + hotlink protection.
Step 6 β Indexing & Search: Immich (Self-hosted)
Docker Compose on TrueNAS (same box, isolated network):
# docker-compose.immich.yml
services:
immich-server:
image: ghcr.io/immich-app/immich-server:latest
volumes:
- /mnt/pool/photos/01_DEVELOPPEES:/usr/src/app/upload:ro
environment:
- DB_HOST=postgres
- REDIS_HOST=redis
postgres: ...
redis: ...
Features I use:
- Text search (IPTC keywords read from TIFFs)
- Geolocation (interactive map)
- Visual similarity (βfind photos like this oneβ)
- Private shared albums (link-only, no RAW/NAS exposure)
Costs & Reality Check β 2026 Budget
| Item |
Upfront |
Yearly |
Est. Lifespan |
| TrueNAS DIY (CPU/RAM/6 HDD) |
~β¬1,200 |
β¬0 |
5β7 years |
| pCloud 2 TB lifetime (promo) |
~β¬180 |
β¬0 |
βLifetimeβ |
| Cloudinary (free 25 GB) |
β¬0 |
β¬0 |
β |
| NAS Electricity (15 W average) |
β |
~β¬60 |
β |
| Total |
~β¬1,380 |
~β¬60/yr |
< β¬0.15/GB/yr |
Comparison: Adobe CC 1 TB = β¬120/yr, Google Photos 2 TB = β¬100/yr β no control, no RAW, no local indexing.
Automation β Key Scripts (Excerpts)
All versioned in my private GitHub repo photo-workflow (easy clone on new machine).
ingest.sh β SD β Local β Lightroom β NAS
#!/bin/bash
set -euo pipefail
SRC="/media/fred/SD_CARD/DCIM"
DST_LOCAL="/home/fred/Photos/Ingest/$(date +%Y%m%d)_$1"
mkdir -p "$DST_LOCAL"
rsync -av --progress "$SRC"/ "$DST_LOCAL"/
# Lightroom auto-imports from watched folder
# Post-import script renames + moves to NAS
export_trio.jsx β Photoshop Action (JavaScript)
// Export Trio - shortcut F12
var doc = app.activeDocument;
var baseName = doc.name.replace(/\.[^\.]+$/, '');
var exportPath = "/Volumes/Photos/02_EXPORTS_WEB/2026/03_Murmurs/";
// 1. Archive TIFF 16-bit
saveTIFF(doc, "/Volumes/Photos/01_DEVELOPPEES/2026/03_Murmurs/" + baseName + ".tif");
// 2. Web AVIF 2400px
resizeExport(doc, 2400, exportPath + baseName + ".avif", "AVIF", 75);
// 3. Social JPEG 1600px
resizeExport(doc, 1600, "/Volumes/Photos/03_EXPORTS_SOCIAL/2026/03_Murmurs/" + baseName + ".jpg", "JPEG", 80);
cloudinary_sync.py β Watchdog β Cloudinary
#!/usr/bin/env python3
import cloudinary.uploader
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import os, time
WATCH_DIR = "/mnt/pool/photos/02_EXPORTS_WEB"
CLOUDINARY_FOLDER = "portfolio/2026"
class Handler(FileSystemEventHandler):
def on_created(self, event):
if event.is_directory: return
if not event.src_path.lower().endswith(('.avif', '.webp')): return
time.sleep(2) # wait for write to finish
public_id = os.path.relpath(event.src_path, WATCH_DIR).replace(os.sep, '/')
cloudinary.uploader.upload(
event.src_path,
folder=CLOUDINARY_FOLDER,
public_id=public_id,
use_filename=True,
unique_filename=False,
tags=["portfolio", "2026"],
eager=[{"transformation": "portfolio_large"}, {"transformation": "portfolio_thumb"}]
)
observer = Observer()
observer.schedule(Handler(), WATCH_DIR, recursive=True)
observer.start()
try:
while True: time.sleep(60)
except KeyboardInterrupt:
observer.stop()
observer.join()
rclone_crypt_sync.sh β Cron 02:00
#!/bin/bash
# /etc/cron.d/nas-pcloud-sync
0 2 * * * root /root/scripts/rclone_crypt_sync.sh >> /var/log/rclone_pcloud_cron.log 2>&1
Key Takeaways
π Separate concerns: master storage (NAS ZFS) β web delivery (Cloudinary CDN) β off-site backup (pCloud encrypted)
π Automate on the 2nd repetition: a script > 100 manual clicks, and it never forgets a step
π Encrypt client-side before any cloud: zero trust β the provider sees only noise
π Test restore 1Γ/year: an untested backup isnβt a backup
π Own your metadata: IPTC in-file = portable, readable by any tool (Immich, Lightroom, ExifTool, my Jekyll)
Conclusion
This workflow lets me:
- Shoot without worrying about space (16 TB usable, expandable)
- Develop at local NVMe speed
- Publish instantly worldwide (Cloudinary global CDN)
- Sleep soundly (ZFS snapshots + pCloud encrypted off-site)
- Find any image in seconds (Immich + IPTC)
Next step: Lightroom Classic β NAS via Smart Previews (offline editing, deferred sync) β dedicated article coming.
What are your thoughts on this? Share your experience in the comments below!