Categories: SystemsInfrastructureSelf-hosting

TrueNAS Home Lab

I repurposed an older desktop into a self-hosted storage and application server using TrueNAS SCALE, ZFS RAIDZ1, Immich and Nextcloud, and remote access that works without inbound port forwarding.

Role
Personal infrastructure project
Status
Ongoing
Storage
3 × 1 TB HDD · ZFS RAIDZ1
Services
Immich · Nextcloud
Platform
TrueNAS SCALE on a reused desktop
William's home server tower and monitor, with an inset showing the components and wiring inside the case.

Motivation

A cheap place for photos became a home lab

It started as a cheap place to keep photos and videos. I had an older desktop PC, and instead of buying a commercial NAS straight away, I wanted to see whether I could build my own storage server from hardware I already owned.

Once the storage layer was working, it became a way to learn how the rest of a home-lab stack fits together: datasets, applications, databases, DNS, tunnels, private networking and recovering from failure.

  • Reuse hardware I already had instead of buying a NAS appliance first.
  • Keep photos and videos on storage I run myself.
  • Add general file storage and sync alongside the photo library.
  • Learn storage, networking and server administration by operating a real system.
  • Leave room to expand into later home-lab experiments.

Hardware · Real limits

Constraints shaped the build

I built the first useful version with the hardware I already had: an older desktop, three hard drives, no dedicated GPU and no spare SSD for application workloads. Every SATA port on the motherboard was already in use, and the whole system sits on an ordinary home network.

Waiting for ideal hardware would have meant not starting. I accepted HDD-backed applications, CPU-based photo processing and limited room for more drives, and let those constraints decide what to upgrade later.

Build the useful version first.

Machine
Reused desktopAn older PC, not purpose-built server hardware
Drives
3 × 1 TB HDDCapacity first, on the drives I had
GPU
None dedicatedImmich’s machine learning runs on the CPU
App storage
No SSD poolApplications share the hard-drive pool
Expansion
SATA ports fullNo free onboard ports for more drives
Network
Home networkAn ordinary residential setup

Platform decision

A storage-first platform

I considered building on Ubuntu Server, which would have meant assembling more of the storage and application layer by hand. Because this project was storage-first, I chose TrueNAS SCALE: it brings ZFS pool and dataset management, disk and storage health, snapshots, scrubs and application deployment together behind a browser interface.

The interface didn’t make the work automatic. Several problems still meant working in the shell, in configuration files and on the network.

Considered
Ubuntu Server: a more manual build of storage, health monitoring and apps.
Chosen
TrueNAS SCALE: ZFS management, health visibility, snapshots, scrubs and apps in one platform.
Still required
Shell access, configuration edits and network troubleshooting when things went wrong.

I chose a storage-focused platform first, then used its application layer to grow the NAS into a broader home-lab server.

ZFS · RAIDZ1

Three drives, one redundant pool

Storage came first. The three 1 TB drives form a single RAIDZ1 vdev inside a ZFS pool, so the pool can keep running if any one drive fails. TrueNAS showed about 1.58 TiB available at the time.

RAIDZ1 suited the goal of affordable, redundant bulk storage from the drives I already had. Datasets inside the pool then hold the applications and the data they serve.

Storage layout · conceptual
  1. 3 × 1 TB HDDThe drives already in the machine
  2. RAIDZ1 vdevOne-disk redundancy across the three drives
  3. ZFS poolAbout 1.58 TiB shown available at the time
  4. DatasetsOrganised storage inside the pool
  • ApplicationsImmich, Nextcloud and their databases
  • User dataPhotos, videos and files

Redundancy ≠ backup

RAIDZ1 is not a backup

RAIDZ1 protects the pool from a single drive failure, but it does not protect against accidental deletion, malware, catastrophic hardware loss or every form of pool failure. A separate backup remains part of the future reliability work.

Trade-off

Capacity now, speed later

Hard drives came first because the capacity was cheaper, the drives already existed and the server could be useful immediately.

The compromise is that application workloads live on the same spinning disks as the bulk data: databases, metadata, thumbnails and caches all share HDD-backed storage. A dedicated SSD tier for applications is the natural next step, and TrueNAS’s own guidance recommends SSD storage for the apps pool. It is planned, not installed.

Performance optimisation didn’t have to block the first useful version.

Where workloads live · current vs planned
Current

One HDD pool

RAIDZ1 HDD pool

  • Bulk files
  • Photos and videos
  • Application data
  • Databases
  • Metadata, thumbnails and cache
Planned · not installed

Two storage tiers

HDD tier

  • Bulk files
  • Photos and videos
  • Large user data

SSD application tier

  • Databases
  • Application configuration
  • Metadata and thumbnails
  • Cache

Immich · Nextcloud

From NAS to application platform

With storage in place, TrueNAS Apps turned the NAS into an application platform. Two containerised, self-hosted services put the storage to everyday use.

Immich handles photo and video backup and browsing, with a real library stored on the server. There was no dedicated GPU, so it used the CPU-based machine-learning path rather than GPU acceleration. I configured it for the hardware I actually had instead of delaying the project until GPU acceleration was possible.

Nextcloud provides general file storage and sync through the browser and its apps. Its app stack runs with PostgreSQL as the database and Redis as the cache, which mattered later when it failed.

Immich
Photo and video backup and browsing · CPU machine learning · remote access through Cloudflare Tunnel
Nextcloud
General file storage and sync · PostgreSQL · Redis · browser access through Cloudflare
TrueNAS Apps
Containerised deployment on the same ZFS pool
Composition of the home server's services: the Nextcloud dashboard, the Immich photo library with personal photos blurred, and the TrueNAS dashboard showing one online pool of three disks with no backup task configured.
Sanitised view of the services running on the home server. Personal media and identifying system details are obscured.

Cloudflare Tunnel · Tailscale

Remote access without port forwarding

The services stay on the home server while Cloudflare Tunnel provides the public HTTPS path. The tunnel is an outbound connection from the server to Cloudflare’s edge, so the application ports did not need to be opened to the Internet through inbound port forwarding.

That makes it self-hosted storage and applications with managed edge access, not a system independent of third parties: public access relies on Cloudflare. Remote access to Immich and browser access to Nextcloud worked through the tunnel, with one mobile-client gap left open.

I also tested Tailscale as a private access layer. It was useful for private network reachability, while Cloudflare Tunnel became the working browser-access path for the hosted web services.

Public web access · conceptual
  1. Internet clientA browser, away from home
  2. Link: HTTPS
    Cloudflare edgePublic hostname and DNS
  3. Link: Routed to the tunnel
    Cloudflare TunnelConnection opened outbound by the server
  4. Link: No inbound port forwarding
    TrueNAS SCALEThe services stay on the home server
  • ImmichPhotos and videos
  • NextcloudFiles

Addresses, ports, hostnames and tunnel identifiers are deliberately left out.

Two access layers, two purposes
Working path

Cloudflare Tunnel

  • Public web-service access
  • HTTPS hostnames for the hosted services
  • Remote access to Immich; Nextcloud in the browser
Experiment

Tailscale

  • Private network reachability
  • Administration and LAN-style testing
  • Not the public path for the web services

Debugging a multi-service failure

The database wasn’t the problem

After I edited Nextcloud’s configuration, the service broke: the browser showed an internal server error and the main Nextcloud container stayed in Starting / Deploying.

Instead of treating the whole stack as broken, I looked at each workload. PostgreSQL and Redis were still healthy; only the application container was not. That narrowed the failure to the application and configuration layer, so the change I had just made was the first thing to check. Re-reading the edit exposed a typo, and restoring valid configuration brought Nextcloud back.

How the fault was narrowed

SymptomInternal server error · Nextcloud stuck in Starting / Deploying after a configuration edit

Workload health

  • PostgreSQLHealthyDatabase container healthy
  • RedisHealthyCache container healthy
  • NextcloudFailingApplication container never finished starting

Reasoning

  1. Narrow the layerHealthy dependencies point at the app or its configuration
  2. Check the latest changeThe configuration edit made just before the failure
  3. Find the causeA typo in the edited configuration
  4. Restore and re-testValid configuration back, service checked again

ResolvedNextcloud started and responded again

What it shows

  • Isolate the healthy components first.
  • Narrow the failing layer before changing anything else.
  • Check the most recent change.
  • Don’t blame services that are still healthy.
  • Restore, then re-test.

Lessons from running it

What operating it taught me

The most useful lessons came from operating the server rather than installing it: something changed unexpectedly, broke after an edit or didn’t quite work from outside the house.

I built it while learning the technologies involved. Documentation, online research, YouTube tutorials and AI-assisted troubleshooting helped me understand each unfamiliar concept, implement it on the real hardware, watch what happened and work back through whatever broke.

Redundancy is not backup
RAIDZ1 survives a failed drive, not deletion, malware or losing the whole machine.
Health narrows the fault
Checking each workload turned “Nextcloud is broken” into one failing layer.
Stable addressing matters
The server’s address changed unexpectedly during setup. Services others depend on need deliberate addressing.
Ports are not endpoints
An app’s internal port is not its public HTTPS address; the tunnel maps a public hostname to a private service.
Constraints set the order
HDDs before SSDs and CPU before GPU: real bottlenecks decide the upgrades.

Current system · next iteration

Where the first version is limited

This is a personal home lab on modest hardware. Its limits are specific, and each one points to the next change rather than being hidden.

Current limits and the direction each points to
AreaCurrent systemNext iterationState
BackupRAIDZ1 redundancy only; no independent backup confirmedA separate backup target outside the poolPlanned
App storageApps, databases, metadata and caches run on the HDD poolA dedicated SSD application tierPlanned
ExpansionEvery onboard SATA port is in useAn HBA or other added SATA connectivityPlanned
AccelerationNo dedicated GPU; Immich machine learning runs on the CPUA GPU, only if real workloads justify itPossible
Remote clientsBrowser access worked; the Nextcloud mobile app’s remote path was unresolvedInvestigate and fix the mobile-client pathPlanned

Open issue

Nextcloud’s mobile app through the tunnel

Browser access through the reverse tunnel worked, and the mobile app worked on the local network. Remotely, the mobile client still exposed a compatibility or configuration issue. Reverse-proxy deployments can require additional client and proxy configuration, but the exact cause was never proven. I left it as a next-iteration item rather than pretending the remote-access path was finished.

Planned · possible

The next iteration

The upgrade path follows the real bottlenecks rather than a wish list. Beyond the limits above, a few reliability steps are planned, and some application ideas remain possibilities, not commitments.

Upgrade path
  1. First version · builtThree HDDs in RAIDZ1, apps on the same pool, public access through a tunnel
  2. Next · plannedSSD application tier, more SATA connectivity and an independent backup
  3. Later · possibleMore services and compute once real use justifies them
Also on the list

Reliability

  • Verify snapshot, scrub and SMART schedulesPlanned
  • Monitoring and alertsPlanned
  • A UPSPossible

Applications

  • JellyfinPossible
  • VaultwardenPossible
  • Local AI and LLM experimentsPossible

Networking

  • Refine the private administration pathPlanned

Outcome · reflection

What it became

What began as a cheap place for photos became a working self-hosted platform on a reused desktop: redundant ZFS storage, a photo and video library in Immich, a browser-accessible personal file cloud in Nextcloud and remote access to both through Cloudflare Tunnel.

It is deliberately a personal home lab rather than a finished appliance: it holds real data, runs services I actually use and gives me a platform I can keep improving as I learn more. An independent backup is not in place yet, and the upgrades above stay planned until they are built.

I also learned that I don’t need to know every part of a system before I start. Documentation, tutorials, AI-assisted troubleshooting and testing helped me understand each layer as I reached it. If I rebuilt the server today I would have to look some details up again, but I know how the architecture fits together and how to work my way back through it.

Storage
A redundant ZFS RAIDZ1 pool
Photos
An Immich photo and video library
Files
A Nextcloud file cloud in the browser
Access
HTTPS through Cloudflare Tunnel
Next
An expandable base for future home-lab work

The biggest lesson was that infrastructure is mostly trade-offs. I could wait for the ideal hardware, or build a useful first version and let real bottlenecks tell me what to upgrade next.