Michel Media — Migration Plan
Versie 1 · 2026-05-07 · gebaseerd op DISCOVERY.md
Strategische uitgangspunten
- Eigen alles, voor zover praktisch en betaalbaar.
- Bedrijfskritiek = eigen hardware of cloud waar je voor betaalt (Cloudflare, Mac mini). Niet werk-VPS.
- Cloudflare als platform (Pages, Workers, R2, D1) waar mogelijk, paid tier OK.
- Mac mini als 24/7 hub — productiever maken loont meer dan NAS behouden.
- NAS-exit gepland, niet vandaag uitgevoerd. Camera-vraag eerst beantwoord.
- Cashspot wordt afgesloten, Coolify wordt afgebouwd na de twee resterende deploys gemigreerd zijn.
Filosofie per wave
- Eén wave per Claude-sessie — fris contextvenster, gefocust werken
- Elke wave heeft heldere succes-criteria en een rollback-pad
- Niets weggooien voordat de vervanger 7+ dagen stabiel draait
- DNS swap is laatste stap — services parallel laten draaien tot je weet dat het werkt
- Backup vóór destructieve actie, altijd
Volgorde
Wave 0 → Foundations (Syncthing + parity + restic backup) DONE 2026-05-07
Wave 0.5 → R2 secondary repo (off-site backup) ~30 min
Wave 0.7 → Dry-run failover oefening ~1 uur
Wave 1 → SPOF mitigatie (Vaultwarden encrypted export) ~30 min
Wave 2 → Mac mini server prep (Docker + tunnel + Caddy) ~2-3 uur
Wave 3 → Vaultwarden migratie naar Mac mini ~2 uur
Wave 4 → Paperless migratie naar Mac mini ~3 uur
Wave 5 → soulwise-site → Cloudflare Pages ~1 uur
Wave 6 → dekei1 migratie (next-on-pages compat-check + Postgres) ~3-5 uur
Wave 7 → plex VPS schoonmaak (Coolify + Supabase + Happy) ~2 uur
Wave 8 → NAS audit + Time Machine via Mac mini ~3 uur
Wave 9 → Camera transitie (geparkeerd tot beslissing) tbd
Totaal geschat: 19-27 uur, verspreid over 6-9 weken in eigen tempo.
Motivatie (juridische ondergrond)
De migratie is niet alleen pragmatisch maar ook juridisch geboden:
- AVG/GDPR: bestaande klant-data (Lurvink KMS = 420 medewerkers, dekei1 = LTC competitie-data) op werk-VPS = werkgever heeft technisch toegang tot klant-personeelsdata. Onder AVG is dat een verwerkersovereenkomst-mismatch.
- Belastingdienst-bewaarplicht 7 jaar: Paperless = drager voor compliance. Single point of failure = niet alleen "vervelend" maar bij audit problematisch.
- Strategische continuïteit: bij wisseling van werkgever of incident op werk-VPS = bedrijfsstilstand. Voor net-startende ZZP'er die voltijds afhankelijk wordt van zelfstandig inkomen = onacceptabel.
Wave 0 — Foundations · DONE 2026-05-07
Status: uitgevoerd 7 mei 2026 ~23:30. Mac mini is hot standby. Claude Code config + werkmappen syncen via Syncthing. Onvervangbare data nightly naar Mac mini SFTP via restic. Dotfiles op GitHub. R2 secondary repo deferred naar Wave 0.5 (vereist 1-min UI-stap voor S3-keys).
Geïmplementeerd:
| Component | Status |
|---|---|
brew install syncthing restic op MBP + macmini |
✓ |
| Syncthing devices gepaird (S4IXMDC... ↔ JVIW4R2...) | ✓ |
5 Syncthing folders shared two-way met .stignore patterns |
✓ |
- michelmedia (~/Documents/michelmedia) |
✓ |
- dekei1, lurvink, og (~/Documents/...) |
✓ |
- claude-config (~/.claude met aggressive ignore: shell-snapshots, ide, projects, plugin-git) |
✓ |
Restic SFTP repo sftp:macmini:/Users/michelhelsdingen/restic-backups/main |
✓ |
Restic passphrase 40 chars in Vaultwarden item "restic backup passphrase" + ~/.cache/restic-passphrase.txt |
✓ |
restic-backup.sh script in infra/scripts/ |
✓ |
LaunchAgent com.michelmedia.restic-backup.plist (03:30 nightly) |
✓ |
| Eerste backup-run: 16,7 GB raw → 5,4 GB stored, 56k files in 57 sec | ✓ |
| Telegram-notificatie hooks (Spotbot bot 8402924483 chat 467689725) | ✓ |
Dotfiles repo michelhelsdingen/dotfiles private op GitHub met bootstrap-script + Brewfile |
✓ |
claude-mem strategie (afwijking van origineel plan):
- ~/.claude-mem zit NIET in Syncthing (zou SQLite single-writer corruptie veroorzaken)
- ~/.claude-mem zit WEL in restic nightly snapshot naar Mac mini (en straks ook R2)
- MacBook = primary, recovery via restic restore latest --include /Users/michelhelsdingen/.claude-mem
Rollback Wave 0:
- Syncthing pauzeren via UI of brew services stop syncthing op een/beide machines
- LaunchAgent uitzetten: launchctl unload ~/Library/LaunchAgents/com.michelmedia.restic-backup.plist
- Restic repo op Mac mini blijft staan; geen impact op MBP data
Wave 0.5 — R2 secondary backup repo
Doel: off-site backup naast lokale Mac mini, beschermt tegen "huis brandt af".
Prerequisites (1-min UI-stap):
1. Login op https://dash.cloudflare.com → R2 → Manage R2 API tokens → Create API token
2. Permissions: Object Read & Write voor bucket noaber-backups
3. Kopieer Access Key ID + Secret Access Key + endpoint URL
4. Plak in Vaultwarden item "restic R2 backup credentials"
Stappen (door Claude na UI):
- Init R2 als tweede restic repo:
restic -r s3:https://<account>.r2.cloudflarestorage.com/noaber-backups init - Update
restic-backup.sh: backup naar BEIDE repos (SFTP primary + R2 secondary) - Test eerste R2-backup en verify met
restic snapshots -r s3:... - Update FAILOVER-PLAYBOOK met R2 restore-procedure
Verificatie: - ✓ Beide repos zichtbaar in nightly run-log - ✓ R2 dashboard toont bucket-content (~5-7 GB)
Wave 0.7 — Dry-run failover oefening
Doel: voor het eerst echt testen dat recovery werkt, vóór het urgent nodig is.
Stappen:
- Op Mac mini: open Claude Code-sessie. Verifieer dat skills/plugins werken (claude-mem write, custom skills callable).
- Simuleer "MBP stuk": stop Syncthing op MBP, sluit alle Claude Code sessies.
- Doe een task op Mac mini, valideer dat claude-mem-write werkt op Mac mini's lokale DB.
- Restart Syncthing op MBP, verifieer two-way conflict-resolution werkt netjes.
- Test restic restore: in /tmp directory, restore latest snapshot van een specifieke folder, verifieer file-integriteit.
- Documenteer alle verschillen tussen MBP en Mac mini-omgeving die opvallen (font-rendering, app-paden, etc.)
Verificatie: documenteren in infra/FAILOVER-DRYRUN-RESULTS.md. Pas dán Wave 1+ starten.
Wave 1 — Vaultwarden SPOF mitigatie
Doel: Het ergste single-point-of-failure dichten zonder dat we Vaultwarden verhuizen. Maximaal 30 minuten werk, voorzichtigheidsnet voordat Wave 2/3 plaatsvinden.
Stappen:
- CLI-export:
bw login → bw unlock → bw export --format encrypted_json --password "<sterkte-passphrase>"→ uploaden naar R2 alsvaultwarden/encrypted-export-YYYYMMDD.json - Encrypted file ook kopiëren naar offline USB stick. Stick fysiek opbergen op extern adres (kluis of vertrouwd familieadres).
- Encryptie-passphrase noteren op papier in dezelfde kluis (of in Vaultwarden zelf — paradox).
- Master password van Vaultwarden noteren op papier in kluis.
- Maandelijkse herhaling automatiseren via LaunchAgent + telegram-bericht "vault-export YYYY-MM uploaded".
Verificatie:
- ✓ Test-restore op een tijdelijke Vaultwarden-instance (Docker lokaal) werkt
- ✓ R2 snapshot zichtbaar via aws s3 ls (rclone of wrangler r2)
Rollback: geen — additief, niets te rollbacken.
Wave 2 — Mac mini server prep
Doel: Mac mini klaar maken om Docker-services te hosten op productie-niveau. Reverse proxy + Cloudflare Tunnel + persistent storage.
Stappen:
- Docker Desktop opstarten en in PATH brengen - Auto-start bij login configureren - Resources: 8 GB RAM toewijzen, 4 cores
- Tailscale running zetten (huidige error-state oplossen)
- Folder layout vaststellen:
/Users/michelhelsdingen/Docker/{vaultwarden,paperless,...}voor service-volumes - Cloudflare Tunnel container installeren met eigen tunnel-ID
- Maak nieuwe tunnel "macmini-services" via CF dashboard of
cloudflared tunnel create macmini-services- Token in macOS Keychain - Container start via Docker Compose, auto-restart unless-stopped - Caddy als reverse proxy in Docker (eenvoudig automatisch HTTPS via tunnel) — of route alles via cloudflared tunnel ingress (geen Caddy nodig)
- Test met dummy-service:
nginx-testcontainer achternginx-test.helsdingen.comroute
Verificatie: - ✓ Cloudflare dashboard toont actieve tunnel - ✓ Test-route bereikt nginx-test container vanaf publiek internet via CF Access - ✓ Mac mini reboot test: alle services starten weer automatisch
Rollback: Docker stoppen + tunnel verwijderen. Geen impact op Plex VPS.
Wave 3 — Vaultwarden migratie
Doel: vault.helsdingen.com draait op Mac mini, plex Vaultwarden uitgezet maar containers nog aanwezig (1 week buffer).
Stappen:
- Snapshot maken op plex VPS:
docker stop vaultwarden && tar czf /mnt/docker/backups/vaultwarden-pre-migration-YYYYMMDD.tgz /mnt/docker/vaultwarden/ - Sync naar Mac mini:
rsync -avz plex:/mnt/docker/vaultwarden/ ~/Docker/vaultwarden/ - Mac mini Vaultwarden Docker compose:
- Image
vaultwarden/server:1.35.6(zelfde versie als plex) - Volume mount op~/Docker/vaultwarden- Environment variables overnemen (admin token, signups disabled, etc.) - Tunnel-ingress route:vault.helsdingen.com→http://vaultwarden:80 - Start Mac mini container met aparte test-host eerst:
vault-test.helsdingen.com - Login-test, vault decrypt, browser sync, mobile sync (iOS Bitwarden app)
- Stop plex Vaultwarden container (niet rm)
- DNS swap:
vault.helsdingen.comCNAME wijzigen van plex-tunnel naar mac mini-tunnel - Test live na propagation
- Wachttijd 7 dagen voordat plex Vaultwarden container verwijderd wordt
- Encrypted export pipeline (Wave 1) updaten om Mac mini's volume te exporteren
Verificatie: - ✓ Login succes op vault.helsdingen.com via desktop browser, mobiele app, en bw CLI - ✓ Recent items zichtbaar (geen lege vault) - ✓ Sync van een nieuw item werkt op alle devices - ✓ Mac mini reboot test: Vaultwarden komt weer up
Rollback: - DNS-swap terug naar plex (TTL 5 min) → plex container start opnieuw - Volume op Mac mini blijft als referentie, geen data-verlies
Wave 4 — Paperless migratie
Urgentie: laag (SPOF is gemitigeerd 2026-05-11 via 3-laags backup, Wave 0.8). Verhuizen is nu een nice-to-have voor ownership/CF-consolidatie, niet voor risico-mitigatie.
Doel: docs.helsdingen.com draait op Mac mini incl. mail-fetcher en gotenberg/tika. NAS-replicatie + R2 encrypted blijven als backup-lagen.
Stappen:
- Snapshot op plex:
docker compose -f /mnt/docker/paperless/docker-compose.yml stop && tar czf paperless-pre-migration.tgz /mnt/docker/paperless/ - Rsync volume naar Mac mini:
rsync -avz plex:/mnt/docker/paperless/ ~/Docker/paperless/ - Mac mini Docker Compose met identieke services (paperless, redis, gotenberg, tika, mail-fetcher)
- Mail-fetcher:
.env-mailfetcheroverzetten,mail-fetcher.pyook (incl. recente subject-skip update) - Tunnel route:
docs.helsdingen.com→http://paperless:8000 - Test-host eerst:
docs-test.helsdingen.com - Verifieer nieuwe scan@ mail wordt verwerkt door Mac mini-mail-fetcher
- NAS-replicatie inrichten:
- rsync van
~/Docker/paperless/naarterror:/volume1/docker/paperless-mirror/om 04:00 nightly - Plus encrypted snapshot naar R2 (restic) - DNS swap docs.helsdingen.com
- Buffer 7 dagen, dan plex containers stoppen
Verificatie: - ✓ Web-UI laadt op docs.helsdingen.com - ✓ Bestaande documenten zichtbaar - ✓ Nieuwe mail naar scan@ wordt geclassificeerd binnen 5 minuten - ✓ Cron-backup dump naar terror succesvol (eerste run handmatig triggeren) - ✓ R2 snapshot zichtbaar
Rollback: - DNS terug naar plex - Mail-fetcher op plex weer starten
Wave 5 — soulwise-site naar Cloudflare Pages
Doel: Coolify-deploy soulwise-site (nginx static op soulwiseapp.com) verhuizen naar CF Pages. Eerste warm-up voor dekei1.
Stappen:
- Clone
michelhelsdingen/soulwiserepo lokaal (als nog niet aanwezig) - Bouw lokaal als nginx serveerbare output (
dist/ofpublic/) - CF Pages project aanmaken:
soulwise-site - Custom domain
soulwiseapp.com+www.soulwiseapp.com - DNS records swappen
- Verifieer site online
- Coolify deployment uitzetten en verwijderen
- GitHub Actions of CF Git-integratie inrichten voor auto-deploy bij push naar main
Verificatie: - ✓ soulwiseapp.com responds 200 OK - ✓ Visuele check: site identiek aan voor migratie - ✓ Auto-deploy test: push naar main triggert nieuwe deploy
Rollback: - DNS terug naar plex (Coolify deployment niet verwijderen tot stabiel)
Wave 6 — dekei1 migratie
Doel: dekei1.ltcdekei.nl van Coolify Next.js + self-hosted Supabase af.
Eerst beslissing nemen voor de Postgres-laag. Drie opties:
| Optie | Hosting | Migratie-werk | Maandelijkse kosten | Geschikt voor |
|---|---|---|---|---|
| A. CF Pages + Supabase hosted (Frankfurt) | CF + Supabase | medium (DB-export/import) | gratis tier mogelijk | meest pragmatisch, snelst |
| B. CF Pages + D1 | volledig CF | hoog (schema-rewrite, geen pgvector etc.) | gratis ruim | als app simpel genoeg is |
| C. Mac mini Docker (Next + Postgres beide lokaal) | eigen | laag, lift-and-shift | stroom | als je self-host wilt blijven |
Aanbeveling: A mits app niet veel Postgres-extensies gebruikt. Eerst auditeren.
Stappen (uitgaande van A):
- Repo audit:
michelhelsdingen/dekei1. Lijst Postgres extensies, RLS policies, Storage gebruik - Supabase Frankfurt hosted project: bestaande LTC project gebruiken of nieuw aanmaken
- Schema dump van self-hosted:
pg_dumpvan Supabase-db container, restore in hosted project via Studio ofpsql - Storage migratie (indien gebruikt): rsync MinIO bucket → Supabase Storage of R2
- App rebuild met nieuwe SUPABASE_URL en keys, deploy naar CF Pages (Next.js SSR via @cloudflare/next-on-pages)
- Custom domain dekei1.ltcdekei.nl
- Acceptatietest met klant Theo
- Coolify deployment uit, self-hosted Supabase containers nog niet aanraken (Wave 7)
Verificatie: - ✓ Site live op CF Pages - ✓ DB queries werken (login, lijst tonen, schrijven) - ✓ Klant heeft minimaal 7 dagen kunnen testen - ✓ Geen errors in Sentry/CF logs
Rollback: - DNS terug naar plex - Self-hosted Supabase blijft in originele staat tot Wave 7
Wave 7 — plex VPS schoonmaak
Doel: Coolify uitzetten, self-hosted Supabase weg, Happy stack uit, irrelevante containers opruimen. plex VPS draait alleen wat daar logisch hoort.
Eindstaat plex VPS containers: - Immich (server, ML, postgres, redis) - Jellyfin (zit niet in huidige container-list — checken of het via Coolify draaide) - Uptime Kuma - Watchtower - Glances - Portainer (nog overwegen of nodig) - Cloudflared tunnel (alleen voor Immich + Jellyfin) - Optioneel: Homepage, change-detection, AI-news-bot, MEXC bot
Stappen:
- Coolify uitzetten:
docker compose -f /coolify/source/docker-compose.yml down - Self-hosted Supabase 12 containers down + volumes archiveren naar tar in /mnt/docker/backups/
- Happy server stack down (4 containers)
- n8n: blijft draaien op plex of migreren naar Mac mini? Onderdeel van beslissing in Wave 6 (gerelateerd aan dekei1?)
- Cloudflared config updaten — alleen routes overhouden voor diensten die nog op plex draaien
- Check disk usage: zou ~50 GB moeten vrijkomen door Supabase + Coolify cleanup
- Watchtower configuratie verifiëren
Verificatie:
- ✓ Immich + Jellyfin werken op respectievelijke routes
- ✓ Geen 404 of broken routes via cloudflared
- ✓ disk usage /mnt/docker ligt onder 100 GB
Rollback: containers weer starten (volumes blijven), of restore tar.
Wave 8 — NAS audit + Time Machine via Mac mini
Doel: Beslissen of NAS blijft of weggaat. Time Machine van externe SSD af + naar Mac mini SMB share als secondaire bestemming.
Stappen:
- Audit
/volume1/homes(2.7 TB): - Inhoud per top-level folder bepalen - Klasificeer: weggooien / R2 archive / Mac mini lokaal / NAS behouden - Dit kan dagen aan triage zijn — niet één sessie - Audit Plex media op NAS (5.8 GB): verhuizen naar plex VPS Jellyfin of weggooien
- Surveillance migratie afhankelijk van Wave 9 (camera). Tot dan blijft NAS leven.
- Time Machine secondary destination:
- Op Mac mini: enable File Sharing + maak
TimeMachineshare aan (SMB) - Configureer als TM-bestemming in Mac mini's/etc/backup-config- Op MacBook: Systeeminstellingen → Time Machine → Voeg Mac mini toe als 2e doel naast externe SSD - NAS-beslissing: - Als surveillance migreert (Wave 9): NAS leeg en verkopen - Als surveillance blijft: NAS behouden alleen voor camera, andere shares uit
Verificatie: - ✓ Time Machine schrijft naar zowel SSD als Mac mini bij wekelijkse backup - ✓ Geen andere services dependen op NAS shares meer (audit)
Rollback: NAS niet wegdoen voordat 30 dagen volledig draait zonder NAS-toegang.
Wave 9 — Camera transitie (geparkeerd)
Status: Wacht op beslissing. Vrouw gebruikt DS Cam (Synology mobile app). Beelden moeten ergens.
Opties bij beslissing:
- A. NAS behouden voor camera alleen. Andere shares afsluiten. Synology blijft als single-purpose device.
- B. NUC Frigate LXC. Frigate doet object detection + lokaal RTSP. Camerabeelden naar lokale schijf op NUC. DS Cam vervangen door Frigate-app of NVR-alternatief.
- C. Hybride. Cam-streams naar NUC voor detection + opnames, maar DS Cam blijft live-view via Synology als die beelden ontvangt via NUC.
Bij C heeft NUC voldoende capaciteit (cams-lxc gebruikt al 1 GB RAM), maar disk-IO voor opnames is een vraag.
Geen actie tot Michel beslist.
Cross-cutting verwijzing
- FAILOVER-PLAYBOOK.md beschrijft hoe je tussen MacBook ↔ Mac mini schakelt en wat te doen bij elke type uitval
- DISCOVERY.md is de feiten-snapshot waarop dit plan rust. Bij significante infra-wijzigingen herrun
- infra-overview.html / infra.michelmedia.nl is de live dashboard. Ververst niet automatisch — handmatig deploy bij significante wijzigingen