11 Mat Homelab Application Migration Plan
kiyi edited this page 2026-09-21 21:44:53 -05:00

Mat's Homelab Application Migration Plan

Setup-specific disclosure

This is the migration plan for Mat's homelab, not your homelab. It reflects Mat's actual hardware, datasets, domains, dependencies, availability needs, and application decisions. The reusable runbooks elsewhere in this wiki may be adapted to other environments; the workload placement and migration order on this page should not be treated as general recommendations.

Purpose

This page is the source of truth for moving selected workloads from TrueNAS Apps and standalone Docker Compose environments into the RKE2 cluster running UDS Core. It also records which services intentionally remain outside Kubernetes, which applications are being replaced, and which datasets must be retained after their old applications are retired.

The governing issues are:

Migration principles

  • Existing services remain operational until their replacement passes validation.
  • Every migration must be reversible.
  • Source datasets are retained through an agreed observation period after cutover.
  • Large shared data stays on TrueNAS unless there is a specific reason to move it.
  • Node-local data stays with the hardware that requires it when moving it would add risk or unnecessary network traffic.
  • Existing public hostnames remain unchanged when required.
  • Keycloak integration is selected per application. Applications with suitable built-in authentication are not placed behind a second Keycloak login unless there is a specific security requirement.
  • During migration, the Kubernetes instance uses an app.dev.gitchegumi.com hostname while Nginx Proxy Manager continues serving the existing production hostname.
  • Existing datasets are used in place when practical; full dataset copies are not the default migration method.
  • No credentials, private keys, live Secret values, or sensitive file contents belong in this public wiki.

Target placement decisions

Migrate into Kubernetes

Workload Target or constraint Data placement
Actual Budget Initial migration pattern; identical application version during cutover Existing server-files and user-files datasets mounted from TrueNAS
Forgejo Linux Actions runner Kubernetes-native runner with isolated build execution Ephemeral workspace unless a job requires otherwise
Trilium Notes Single stateful application Dynamic RWO storage
Node-RED Preserve flows, projects, and installed dependencies Dynamic RWO storage
Jellyseerr May call media automation services across the LAN Dynamic RWO storage
Audiobookshelf Application and metadata move Audiobook library remains on TrueNAS
Calibre Move application Ebook library remains on TrueNAS
Calibre-Web Move with Calibre Ebook library remains on TrueNAS
Reactive Resume Application moves; PostgreSQL database migrates to the shared TrueNAS PostgreSQL service Existing files remain on TrueNAS or use an application-specific volume
GitchAlerts Production deployed 2026-09-16; external TrueNAS PostgreSQL and app-specific Redis Existing S3 assets; see deployment record
AFFiNE Application moves; PostgreSQL migrates to shared TrueNAS PostgreSQL; Redis remains application-specific Existing uploads remain on TrueNAS
Twenty CRM Application moves; PostgreSQL migrates to shared TrueNAS PostgreSQL; Redis remains application-specific Existing local files remain on TrueNAS where practical
Vaultwarden Conservative migration with verified backup and rollback Dynamic RWO storage
Ghost Preserve both existing public hostnames MySQL and content move into managed volumes
Forgejo Preserve existing HTTPS and SSH access PostgreSQL and MinIO remain external on TrueNAS; existing repository data is retained
ERPNext Migrate database, Redis components, workers, sites, and application data as one system Multiple stateful volumes
Core Keeper Schedule to OptiPlex Game data remains local to OptiPlex
Satisfactory Schedule to OptiPlex Game data remains local to OptiPlex
Windrose Schedule to OptiPlex Game data remains local to OptiPlex
Enshrouded Schedule to OptiPlex Game data remains local to OptiPlex
Palworld Schedule to OptiPlex Game data remains local to OptiPlex
ComfyUI Schedule to RTX 3080 Ti media node Existing data remains local to media node
Jellyfin Schedule to RTX 3080 Ti media node Configuration moves; media remains on TrueNAS
Jellystat Migrate after Jellyfin Dynamic application/database storage

Separate FoundryVTT product track

The four existing FoundryVTT datasets—foundry-data, foundry-14-data, james-12-data, and james-13-data—will be imported into the self-service hosting platform defined by #19.

That platform must support:

  • A dashboard at foundry.gitchegumi.com
  • Player access to individual Foundry instances without a Keycloak gate; Foundry provides player authentication
  • Keycloak-protected DM dashboard and administrative lifecycle controls
  • One licensed Foundry instance per DM
  • Self-service provisioning for users with a valid Foundry license
  • A default 5 GB allocation per instance
  • Administrative quota adjustment
  • Protected license-key handling
  • Per-instance backup, restore, lifecycle, and deletion controls
  • Import of existing instances only after the platform is validated

Remain on TrueNAS or outside Kubernetes

Service or data Decision
MinIO Remains on TrueNAS as external object storage for Forgejo, UDS Velero, UDS Loki, and other retained consumers
NocoDB Remains on TrueNAS, including its database and MinIO objects
Shared external PostgreSQL Remains on TrueNAS at the existing homelab/postresql dataset; Keycloak and compatible applications receive separate databases and roles
qBittorrent Remains on TrueNAS
Gluetun Remains paired with qBittorrent on TrueNAS
Sonarr Remains on TrueNAS with the media stack
Radarr Remains on TrueNAS with the media stack
Prowlarr Remains on TrueNAS with the media stack
ReadMeABook Deployed on TrueNAS; owner confirmed configuration/path validation on 2026-09-21 after setting PUID=3000, PGID=3001. Uses :latest and pull_policy: always; existing Audiobookshelf library remains in place. Operating notes; platform#26 closed at owner request.
FlareSolverr Remains on TrueNAS with Prowlarr
Shared media library Remains on TrueNAS and is mounted read/write only where required
ROM library Remains on TrueNAS for clients such as RetroArch after RomM is retired

ReadMeABook on TrueNAS

The owner confirmed configuration/path validation on 2026-09-21 and requested closure of platform#26. The app is running on TrueNAS with the retained ARR stack. End-to-end acquisition/import/playback, restart persistence, and restore/rollback tests have not been separately reported.

  • Image: ghcr.io/kikootwo/readmeabook:latest, with pull_policy: always; HTTP port 3030. Pull and recreate through TrueNAS to update; a restart alone does not replace the image. Record the running digest before updates.
  • Runtime identity: PUID: "3000", PGID: "3001", UMASK: "002", TZ: "America/Chicago". Both mounted media directories were observed as 3000:3001 with mode 0770. Running the app as 1000:1000 caused both setup path tests to fail; matching the existing ownership resolved validation. Edit the environment values and save/redeploy in TrueNAS; preserve the existing config, database, shared-folder ownership and ACLs.
  • Persistent app storage: /mnt/homelab/apps/readmeabook/config to /app/config, cache to /app/cache, and redis to /var/lib/redis. Keep generated keys in /app/config/.secrets persistent and backed up.
  • Downloads: /mnt/homelab/media/downloads/arr/books/audio-books to /downloads. Library: /mnt/homelab/media/library/books/audiobooks to /media, preserving the existing Audiobookshelf library. Enter /downloads and /media in the setup wizard. Existing ebook downloads/categories stay in place.
  • Database configuration uses a dedicated readmeabook database and owner role on shared PostgreSQL; the external DATABASE_URL disables internal PostgreSQL. Redis stays internal and app-specific. All environment variables are inline in the TrueNAS Compose editor, including the privately entered database URL and actual PUBLIC_URL. Keep real credentials out of Git and this wiki. The owner chose this simple configuration without an OpenBao agent or startup loader.
  • qBittorrent remote path mapping must reflect its actual container path for the new audio-books child, mapped to local /downloads; no additional client subfolder is needed. The exact remote prefix, service endpoints, TrueNAS/PostgreSQL versions and running image digest were not recorded in this task.
  • Before upgrades, back up the dedicated PostgreSQL database together with config/keys and queue state. Stop ReadMeABook before recovery; restore compatible database/config backups and use the previous image digest if needed. Preserve shared library/download data and the other ARR services.

Replace during the transition

Existing service Replacement Retirement gate
Authentik UDS Keycloak Every relying application authenticates successfully through Keycloak
Pi-hole Technitium DNS Server LAN clients receive Technitium through the real DHCP/DNS path; filtering and split DNS are validated
Nginx Proxy Manager Istio gateways Every public and internal hostname has a tested Istio route

Technitium must provide ad and malware blocking plus split DNS for the Istio gateways. Replacing Pi-hole alone will not help unless clients are actually configured to use the replacement resolver.

Retire

The following deployments will not be migrated:

  • Current TradeGumi deployment; the actively developed replacement will be deployed when ready
  • Baserow
  • Old TrueNAS Core Keeper instance
  • Old TrueNAS Satisfactory instance
  • Readarr
  • Standalone Grafana
  • JDownloader
  • RomM application
  • Lidarr
  • n8n
  • Navidrome
  • Tailscale on TrueNAS
  • KitchenOwl
  • Old TrueNAS ComfyUI instance
  • Remark42

Retiring RomM does not delete the ROM library. Retiring the old TrueNAS game and ComfyUI instances does not affect the authoritative OptiPlex or media-server data.

Required hostname continuity

The following migrations must retain their existing hostnames:

Workload Required hostname
Forgejo git.gitchegumi.com
Ghost ghost.gitchegumi.com
Ghost website integration blog.gitchegumi.com
Foundry hosting platform foundry.gitchegumi.com

These names are not required to use the *.app.gitchegumi.com tenant-domain pattern. Istio routes and certificates must be configured for the actual hostnames. Existing application hostnames are retained wherever the application continues; users should not have to adopt new production URLs merely because ingress moved from Nginx Proxy Manager to Istio.

Authentication selection

Authentication is configured per route and per application:

  • Keycloak is used when the application supports OIDC cleanly or when the route needs an external identity gate.
  • Applications with built-in authentication may use it directly to avoid a redundant Keycloak login followed by another application login.
  • Foundry players connect directly to their licensed Foundry instance and authenticate through Foundry.
  • Foundry DMs authenticate to the management dashboard through Keycloak before starting, stopping, provisioning, or administering an instance.
  • Administrative routes may be Keycloak-protected even when the corresponding player or public route is not.

Temporary development hostnames

Every application migration receives a temporary hostname in the form <app>.dev.gitchegumi.com. For example, Actual Budget uses actual.dev.gitchegumi.com.

The development hostname routes through Istio to the Kubernetes deployment. The existing production hostname continues routing through Nginx Proxy Manager during validation. After the Kubernetes deployment, production data, authentication, backup, and restart behavior are verified:

  1. Prepare the native production route and certificate, and record the rollback path while keeping the existing NPM Proxy Host intact.
  2. Perform the coordinated allowlist removal and native Istio activation described below.
  3. Verify DNS, trusted TLS, application behavior, logs and data safety.
  4. Retain the NPM configuration and old deployment for rollback through the observation period; only then retire obsolete NPM and temporary dev routes.

The development hostname is a migration control, not a permanent alternate URL.

Primary edge and production hostname handoff

Homelab/platform#1 replaces public issue 20 as the ingress tracker. The current evidence and remaining checks are recorded in that issue and the architecture status. OpenBao HTTPS is confirmed. As of 2026-09-07, the owner has confirmed Actual login, existing data, sync, persistence and the production hostname on RKE2. See Homelab Operations Status for current evidence and the remaining Actions automation work; other issue acceptance checks are not implied complete.

The target traffic paths are:

Public TCP 80/443 → Xfinity → Sager TCP relay 10.0.0.107 → Istio tenant 10.0.0.241
LAN application DNS → Istio tenant 10.0.0.241
  native host → Istio TLS termination → Kubernetes application
  explicit legacy SNI host → NPM 10.0.0.116:443 → existing application
  unknown host → reject; no wildcard NPM fallback
LAN administration → admin gateway 10.0.0.240; never exposed by public forwarding

The initial operational inventory is the 29-host allowlist in Homelab/platform/infrastructure/istio/npm-legacy. The workload table below is a planning inventory, not an exact deployment allowlist; reconcile it against current NPM inventory before changes rather than regenerating routes from the table.

Before the global edge cutover, capture actual router TCP 80/443 destinations, public DNS, LAN resolver rules, and direct NPM access in the operational change record. The last recorded baseline forwarded directly to NPM; recheck it. Validate through the tenant VIP before switching production forwarding, then test from both LAN and an external network. Keep the old DNS/NAT values ready for restoration. Do not forward the public edge to the admin VIP.

For each application after the primary edge is established:

  1. Validate the app through its dev route, with trusted TLS, appropriate authentication, backups and safe single-writer behavior. Pin and test the native production route/certificate and rollback artifacts before activation.
  2. Coordinate two owners in one change window: platform removes that hostname from every legacy Gateway HTTP/TLS host list, VirtualService TLS SNI match, and validation inventory; the application activates its native production route. Ensure the native route owns HTTP-to-HTTPS redirection too. Do not leave competing TLS termination and passthrough definitions for the same hostname.
  3. Use tested versioned UDS packages/bundles for the handoff. Existing raw-manifest adoption must be explicitly planned; do not delete serving shared gateways to force Helm ownership.
  4. Check trusted certificate/SANs, redirects, expected application response, authentication, data integrity and logs. Revalidate the other legacy hosts and unknown-host rejection. The legacy script uses insecure TLS for response comparison, so perform a separate certificate-trust check without bypassing verification.
  5. Keep the NPM Proxy Host, old deployment definition and recovery data available through the observation period. Coordinate active writers and background jobs; retaining a definition does not authorize concurrent writes.
  6. If rollback is needed, stop conflicting new writers, deactivate the native hostname route, restore its exact legacy allowlist entries and recover the old workload as appropriate. Restore global DNS/NAT only when rolling back the entire edge. Record any data recovery separately; routing rollback does not undo database or filesystem changes.
  7. After the observation period and successful validation, retire obsolete NPM/dev routes. Retire NPM and the passthrough layer only when no retained host or service depends on them and the legacy allowlist reaches zero.

OpenBao/ESO and cert-manager are deployed. Production dev, tenant and admin certificates are issued; OpenBao is accessible over trusted HTTPS at openbao.admin.app.gitchegumi.com/ui/. Leaf renewal observation remains distinct from initial issuance. OpenBao recovery follows the accepted rebuild-first homelab scope; no isolated restore environment is required. See Security and Secrets.

Existing production hostname inventory

Workload Production hostname(s) Planned outcome
Actual Budget actual.gitchegumi.com Migrate; test at actual.dev.gitchegumi.com
AFFiNE affine.gitchegumi.com Migrate
MinIO / AIStor aistor.gitchegumi.com, assets.gitchegumi.com Remain on TrueNAS
GitchAlerts alerts.gitchegumi.com, alerts-webhook.gitchegumi.com Production deployed 2026-09-16; run 64 passed
Audiobookshelf audiobooks.gitchegumi.com Migrate
Authentik auth.gitchegumi.com Replace with Keycloak
Ghost ghost.gitchegumi.com, blog.gitchegumi.com Migrate and preserve both names
Calibre / Calibre-Web calibre.gitchegumi.com Migrate
Twenty CRM crm.gitchegumi.com Migrate
ERPNext erp.gitchegumi.com Migrate
FoundryVTT foundry.gitchegumi.com, james13.gitchegumi.com, james14.gitchegumi.com Import into Foundry platform
Forgejo git.gitchegumi.com Migrate and preserve HTTPS/SSH name
Jellyfin jellyfin.gitchegumi.com Migrate to GPU worker
n8n n8n.gitchegumi.com Retire
NocoDB noco.gitchegumi.com Remain outside Kubernetes
Node-RED node.gitchegumi.com Migrate
Trilium Notes notes.gitchegumi.com Migrate
Jellyseerr requests.gitchegumi.com Migrate
Reactive Resume resume.gitchegumi.com Migrate
TradeGumi tradegumi.gitchegumi.com Retire current deployment; deploy replacement when ready
Vaultwarden vault.gitchegumi.com Migrate

Migration sequence

Phase 0 — Retire unused workloads safely

  1. Export any data worth preserving.
  2. Stop retired applications to reduce TrueNAS resource use.
  3. Keep their datasets until cleanup is explicitly approved.
  4. Confirm no website, automation, or user still depends on the stopped service.

Phase 1 — Establish the repeatable pattern

Before application migration, establish the deployment pipeline in Homelab/workflows. Actual is the first application validation target once the pipeline is ready. Manual infrastructure deployment remains acceptable.

  1. Actual Budget
  2. Forgejo Linux Actions runner — completed on owner acceptance 2026-09-10. Global Kubernetes ci/deploy pools provide four CI slots plus one deployment slot; Windows Hyper-V is unchanged. See Linux Runner Migration.
  3. Trilium Notes
  4. Node-RED
  5. Jellyseerr
  6. Audiobookshelf
  7. Calibre and Calibre-Web

This phase establishes packaging, persistent storage, Istio ingress, Keycloak integration, backup, restore, restart recovery, and reversible cutover practices.

Phase 2 — Conventional stateful applications

  1. Reactive Resume
  2. GitchAlerts — production deployment completed 2026-09-16; evidence
  3. AFFiNE
  4. Twenty CRM
  5. Vaultwarden

Vaultwarden is deliberately last in this phase because its technical shape is simple but its data is more consequential.

Phase 3 — Public and complex services

  1. Ghost
  2. Forgejo
  3. ERPNext

These applications have public consumers, more complex dependencies, or greater operational impact.

Phase 4 — Node-local workloads

  1. Migrate Core Keeper first as the OptiPlex game-server pattern.
  2. Migrate Satisfactory, Windrose, Enshrouded, and Palworld.
  3. Migrate ComfyUI to the Kubernetes workload pinned to the RTX 3080 Ti media node.
  4. Migrate Jellyfin to the same GPU node.
  5. Migrate Jellystat after Jellyfin.

The FoundryVTT hosting platform proceeds separately after Phase 1 proves the standard deployment pattern.

ComfyUI access and updates — planned, 2026-09-17

The owner requires Keycloak SSO restricted to the ComfyUI/Users group (full group path /ComfyUI/Users). Protect the UI, API and WebSocket routes, including group authorization for bearer-token access; a successful login alone is insufficient. Validate member access, nonmember denial and removal of any direct unauthenticated route before cutover.

Use weekly upstream checks initially to build and scan an immutable candidate image/bundle when ComfyUI or declared custom-node inputs change, then email the owner a release link. Deployment and promotion remain manual, with GPU workflow acceptance before production. No scheduled builder, email notification, SSO integration or ComfyUI Kubernetes deployment is enabled by this plan. Implementation and provenance gaps are tracked in Comfy-UI #1, with the design in PR #2 and GPU readiness in platform #3.

Storage conventions implied by this plan

Storage pattern Intended use
Dynamic iSCSI RWO Single-writer application state, SQLite, and database volumes
Dynamic NFS RWX Workloads that actually require shared access across pods or nodes
Static NFS PV Existing TrueNAS-owned media, ROM, Foundry, ebook, audiobook, or other retained shared datasets
Local PV plus node affinity OptiPlex game data and media-node ComfyUI data
External network service MinIO, NocoDB, shared TrueNAS PostgreSQL, and the retained media automation stack

Static PVs do not transfer ownership of the underlying TrueNAS datasets to Kubernetes. Local-PV workloads do not have transparent failover and require explicit backup plans.

External PostgreSQL and Redis conventions

The existing PostgreSQL service on TrueNAS is the shared external PostgreSQL service for Keycloak and compatible applications. PostgreSQL does not move into Kubernetes as part of application migration.

  • Each application receives a separate database, owner role, and credentials.
  • Applications do not share schemas or credentials.
  • Existing PostgreSQL databases are migrated to this service before their application cutover.
  • The old deployment is repointed to the TrueNAS PostgreSQL database and validated before the Kubernetes instance is introduced.
  • The old and Kubernetes deployments use the same application version while both can reach the database.
  • Singleton schedulers, queue consumers, migration jobs, and background workers run in only one deployment until cutover.
  • PostgreSQL receives native backups in addition to TrueNAS dataset snapshots.

A universal shared Redis instance is not the default. Redis and Valkey instances remain application-specific unless the application explicitly supports safe sharing with separate ACL users, key prefixes, compatible persistence, and compatible eviction policies. Redis logical database numbers alone are not treated as application isolation.

Standard application migration procedure

Common preparation

  1. Create or update the issue and implementation branch in the owning application repository, and add the issue to Homelab Roadmap. Runtime secret values come from OpenBao through ESO; commit references only.
  2. Document the source deployment, dataset, ownership, ports, current production hostname, dependencies, authentication method, and backup method.
  3. Pin the Kubernetes deployment to the same application version as the existing deployment.
  4. Create the <app>.dev.gitchegumi.com Istio route.
  5. Take a named pre-migration ZFS snapshot before the new deployment can modify production data.
  6. Keep the Nginx Proxy Manager production route in place during validation.

PostgreSQL-backed applications

  1. Create a dedicated database and owner role on the shared TrueNAS PostgreSQL service.
  2. Import the existing application database.
  3. Reconfigure the old application to use the shared TrueNAS PostgreSQL database and verify production operation.
  4. Deploy the Kubernetes web application against the same database.
  5. Keep schema migrations, schedulers, queues, and other singleton workers active in only one deployment.
  6. Validate the Kubernetes web path through the development hostname.
  7. Move the production hostname from Nginx Proxy Manager to Istio.
  8. Activate Kubernetes workers as required and stop the old deployment.
  9. Retain the old definition and snapshots through the observation period.

This makes the final application cutover a routing and workload handoff rather than a second database transfer.

Existing filesystem or SQLite datasets

  1. Mount the existing authoritative TrueNAS dataset into the Kubernetes workload, normally through a static NFS PV.
  2. Do not create a full dataset copy solely for migration testing.
  3. Use the same application version in both deployments.
  4. Ensure only one deployment is actively used at a time. RWX capability does not make concurrent writers safe.
  5. Close browser tabs, PWAs, API clients, sync clients, and other automatic consumers that might still contact the production URL during development-host validation.
  6. Validate the Kubernetes deployment through the development hostname using the production data.
  7. Move the production hostname from Nginx Proxy Manager to Istio.
  8. Stop the old deployment after cutover.
  9. If validation fails, stop the Kubernetes workload and return to the old route and deployment. Roll back the ZFS snapshot only when the new version changed data incompatibly.

For Actual Budget, the authoritative dataset contains server-files/account.sqlite plus the budget blobs under user-files. Mat is the only user and will not use actual.gitchegumi.com and actual.dev.gitchegumi.com concurrently. The identical-version Kubernetes instance may therefore validate directly against the production dataset after a ZFS snapshot, provided existing tabs, PWAs, and sync clients are closed and only one endpoint is actively used.

Cutover completion

  1. Verify data integrity, login behavior, application-specific authentication, TLS, Istio routing, restart recovery, and backup coverage.
  2. Preserve the original production hostname.
  3. Complete the coordinated passthrough-to-native hostname handoff above, preserving NPM configuration through the rollback window.
  4. Keep the old deployment definition and authoritative pre-migration snapshot through the agreed observation period.
  5. Remove the development hostname after the production observation period.

Completion gates

An application is not considered migrated until:

  • Its intended hostname resolves and presents a valid certificate.
  • Authentication and authorization work as designed.
  • Persistent data survives pod recreation.
  • Required TrueNAS or node-local data is mounted with correct permissions.
  • Backup and restore responsibilities are documented and tested to the agreed extent.
  • Monitoring and logs are available.
  • The old deployment can be restored or restarted during the rollback window.
  • The issue contains completion evidence.