smartHome
Post image
smartHome · 22 February 2026

Building a Practical Home NAS on Ubuntu Server (LVM + Samba + Transmission + Custom Automation)

This post walks through how I migrated from independent HDD shares to a structured LVM-based storage pool on Ubuntu Server, integrated Samba, Transmission, and a custom C# automation layer — all on repurposed hardware.

For years, my home storage setup was functional — but architecturally messy. You can check this out here -> iamJohnnySam | Media Box

Each hard drive was mounted independently, and each disk had its own Samba share. It worked, but it wasn’t scalable, resilient, or clean from a systems perspective. As someone working in systems engineering and automation, that bothered me. The system functioned, but it wasn’t designed.

This post walks through how I migrated from independent HDD shares to a structured LVM-based storage pool on Ubuntu Server, integrated Samba, Transmission, and a custom C# automation layer — all running on repurposed hardware that most people would have retired.

Phase 1 — The Original Setup (The “It Works” Stage)

Initially, I had:

  • Multiple 1TB HDDs
  • Ubuntu Server
  • Individual mount points
  • Separate Samba shares per drive

Each had its own Samba block in smb.conf.

Problems With This Architecture

  • No unified storage abstraction
  • Manual allocation of data per disk
  • Uneven disk utilization
  • Difficult future expansion
  • No clean way to re-balance data

It was storage at the filesystem level, not at the volume-management level. Every time a disk filled up, I had to manually decide where new data should go. There was no logical aggregation layer between physical disks and network access.

Phase 2 — The Goal: A Single Logical Pool

What I wanted:

  • One logical storage pool
  • Single mount point
  • Clean folder hierarchy
  • Proper permission model
  • Easy future expansion
  • Ability to automate file orchestration

Naturally, I evaluated ZFS and TrueNAS.

Why Not TrueNAS?

I attempted installing TrueNAS (CORE and SCALE), but the older hardware presented compatibility and installation issues.

I am running a very old HP Compaq 8100 Elite PC with an early-generation Intel Core i5 processor. This machine has served reliably for years, but it was never designed for modern storage-heavy operating systems.

Post Image

Instead of fighting the hardware, I stepped back and reconsidered:

Do I actually need full ZFS-level enterprise features for a home NAS?

For my use case — media storage, automation workflows, and torrent orchestration — the honest answer was no.

Why I Settled on LVM

While reinstalling Ubuntu Server, I revisited Logical Volume Manager (LVM). LVM provides a clean abstraction layer between physical disks and filesystems without imposing heavy system requirements.

LVM gave me:

  • A unified volume group
  • Logical abstraction over physical disks
  • Expandability (add disks later)
  • Snapshot capability
  • Simplicity
  • Native Linux integration

And most importantly — it worked immediately on my hardware with zero compatibility friction.

Current Storage Architecture

Physical disks → Volume Group → Logical Volume → Single mount

/dev/sdarn/dev/sdbrn/dev/sdcrn/dev/sddrn/dev/sde rn↓ rnVolume Group: molecule rn↓ rnLogical Volume: media rn↓ rnMounted at: /molecule 

This transformed five independent disks into one logical storage pool.

Samba Configuration

I structured Samba into two logical shares to reflect system architecture rather than just folders.

Public Media Share (Open Access)

[Media]rn    path = /molecule/Mediarn    writeable = Yesrn    create mask = 0777rn    directory mask = 0777rn    public = Yes 

Purpose:

  • Accessible by all LAN devices
  • Centralized media repository
  • No authentication friction for TVs, tablets, and laptops

Private SSD Application Share

[atom] rn    path = /home/atomrn    browseable = yesrn    read only = norn    valid users = userrn    create mask = 0775rn    directory mask = 0775 

Purpose:

  • Lives on SSD for performance
  • Hosts my custom C# automation service
  • Restricted access
  • Cleaner permission control

This separation enforces architectural intent:

  • HDD pool = Storage layer
  • SSD = Compute and orchestration layer

The system is now layered instead of improvised.

Transmission Integration

Transmission is configured with:

'download-dir': '/molecule/Media/Downloads', rn'incomplete-dir': '/molecule/Media/IncompleteDownloads'

Key detail:

Transmission runs as debian-transmission, not my primary user account. That means Linux permissions must be intentionally structured.

I created a shared group and applied proper ownership:

groupadd media rnusermod -aG media debian-transmission rnusermod -aG media user rnchown -R root:media /molecule/Media rnchmod -R 2775 /molecule/Media

This ensures:

  • Transmission can write
  • Samba users can write
  • New files inherit correct group
  • No permission deadlocks

This is critical in multi-service Linux storage systems where daemons and users interact on the same filesystem.

Custom C# Automation Layer (Atom → MediaBox)

This is where the system becomes more than just a NAS.

On the SSD share (/home/atom), I deployed a background C# service that:

  • Monitors Transmission via RPC
  • Organizes downloaded content
  • Moves files into structured directories
  • Deletes torrents post-processing
  • Interfaces with me via Telegram
  • Maintains folder taxonomy rules

This module started life under the broader Atom project as the MediaBox engine, and has since become a dedicated .NET 10 service. I cover the full journey and the system that hosts it here -> iamJohnnySam | MediaBox 2026.

This effectively acts as a lightweight orchestration engine for home media lifecycle management. It separates raw ingestion (torrent downloads) from structured storage, keeping the media library clean and deterministic.

Why Not ZFS?

ZFS is powerful and feature-rich:

  • End-to-end checksumming
  • Self-healing data
  • RAID-Z configurations
  • Snapshots
  • Compression

But for this hardware and workload:

  • LVM is lighter
  • Easier to recover on standard Linux installs
  • No significant RAM overhead
  • No kernel compatibility concerns

If I upgrade to newer hardware with ECC memory and higher RAM capacity, I may migrate to ZFS. For now, LVM provides the correct abstraction-to-complexity ratio.

Post Image

This NAS is not enterprise-grade. It is not redundant RAID with hot spares and ECC memory. But it is structured, expandable, automated, and intentionally engineered.

And that makes all the difference.

sudo apt updaternsudo apt upgradernsudo apt updaternrn# Sudo permissionsrnsudo usermod -aG sudo atomrnrn# Wipe Drivesrnsudo wipefs -a /dev/sdbrnsudo wipefs -a /dev/sdcrnsudo wipefs -a /dev/sddrnsudo wipefs -a /dev/sdernrn# Create physical volumernsudo pvcreate /dev/sdbrnsudo pvcreate /dev/sdcrnsudo pvcreate /dev/sddrnsudo pvcreate /dev/sdernrn# Create logical volumernsudo vgcreate molecule /dev/sdb /dev/sdc /dev/sdd /dev/sdernsudo lvcreate -l 100%FREE -n media moleculernrn# Formatrnsudo mkfs.ext4 /dev/molecule/mediarnrn# Create mount point and mountrnsudo mkdir /moleculernsudo mount /dev/molecule/media /moleculernrn# Make the mounting permanentrnsudo blkidrnsudo nano /etc/fstabrn→ UUID=6b3b1323-87fb-4842-8e67-7e75737a8c2b  /molecule  ext4  defaults,noatime  0  2rnrnsudo tune2fs -m 0 /dev/molecule/mediarnrn# Set Static IPrnls /etc/netplan/rnsudo nano /etc/netplan/01-netcfg.yamlrnrn→ # This is the network config written by 'subiquity'rnnetwork:rn  version: 2rn  ethernets:rn    enp0s25:rn      dhcp4: norn      addresses:rn        - 192.168.1.30/24rn      routes:rn        - to: defaultrn          via: 192.168.1.1rn      nameservers:rn        addresses: [1.1.1.1]rnrn# Samba Set uprnsudo apt install sambarnsudo systemctl status smbdrnrnsudo adduser userrnsudo usermod -aG molecule userrnrnsudo nano /etc/samba/smb.confrnrn→ [Media]rn   path = /molecule/Mediarn   writeable=Yesrn   create mask=0777rn   directory mask=0777rn   public=Yesrn   rn→ [atom]rn    path = /home/atomrn    browseable = yesrn    read only = norn    valid users = userrn    create mask = 0775rn    directory mask = 0775rn	rnsudo smbpasswd -a userrnrn→ .NETrnsudo curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel STSrnrnsudo apt-get update && rn  sudo apt-get install -y dotnet-sdk-9.0rnrnsudo apt-get update && rn  sudo apt-get install -y aspnetcore-runtime-10.0rnrnsudo apt updaternsudo apt install dotnet-runtime-8.0rnrnecho 'export DOTNET_ROOT=$HOME/.dotnet' >> ~/.bashrcrnecho 'export PATH=$PATH:$HOME/.dotnet' >> ~/.bashrcrnsource ~/.bashrcrnrndotnet --versionrnrn→ Transmissionrnsudo chmod -R 775 /molecule/Mediarnsudo apt install transmission-daemonrnrnsudo systemctl stop transmission-daemonrnrnsudo chown -R debian-transmission:debian-transmission /molecule/Media/Downloadsrnsudo chown -R debian-transmission:debian-transmission /molecule/Media/IncompleteDownloadsrnrn/etc/transmission-daemon/settings.jsonrnrn→ {rn  'alt-speed-down': 0,rn  'alt-speed-enabled': false,rn  'alt-speed-time-begin': 465,rn  'alt-speed-time-day': 127,rn  'alt-speed-time-enabled': true,rn  'alt-speed-time-end': 15,rn  'alt-speed-up': 0,rn  'bind-address-ipv4': '0.0.0.0',rn  'bind-address-ipv6': '::',rn  'blocklist-enabled': false,rn  'blocklist-url': 'http://www.example.com/blocklist',rn  'cache-size-mb': 4,rn  'dht-enabled': true,rn  'download-dir': '/molecule/Media/Downloads',rn  'download-limit': 100,rn  'download-limit-enabled': 0,rn  'download-queue-enabled': true,rn  'download-queue-size': 5,rn  'encryption': 1,rn  'idle-seeding-limit': 30,rn  'idle-seeding-limit-enabled': false,rn  'incomplete-dir': '/molecule/Media/IncompleteDownloads',rn  'incomplete-dir-enabled': true,rn  'lpd-enabled': false,rn  'max-peers-global': 200,rn  'message-level': 1,rn  'peer-congestion-algorithm': '',rn  'peer-id-ttl-hours': 6,rn  'peer-limit-global': 200,rn  'peer-limit-per-torrent': 50,rn  'peer-port': 51413,rn  'peer-port-random-high': 65535,rn  'peer-port-random-low': 49152,rn  'peer-port-random-on-start': false,rn  'peer-socket-tos': 'default',rn  'pex-enabled': true,rn  'port-forwarding-enabled': false,rn  'preallocation': 1,rn  'prefetch-enabled': true,rn  'queue-stalled-enabled': true,rn  'queue-stalled-minutes': 30,rn  'ratio-limit': 0,rn  'ratio-limit-enabled': true,rn  'rename-partial-files': true,rn  'rpc-authentication-required': false,rn  'rpc-bind-address': '0.0.0.0',rn  'rpc-enabled': true,rn  'rpc-host-whitelist': '192.168.1.*',rn  'rpc-host-whitelist-enabled': true,rn  'rpc-password': '{828b6db2a9fc2115177ab572cce421d5d89d5e74UtkMgp.E',rn  'rpc-port': 9091,rn  'rpc-url': '/transmission/',rn  'rpc-username': 'transmission',rn  'rpc-whitelist': '127.0.0.1, 192.168.1.*',rn  'rpc-whitelist-enabled': true,rn  'scrape-paused-torrents-enabled': true,rn  'script-torrent-done-enabled': false,rn  'script-torrent-done-filename': '',rn  'seed-queue-enabled': false,rn  'seed-queue-size': 10,rn  'speed-limit-down': 100,rn  'speed-limit-down-enabled': false,rn  'speed-limit-up': 100,rn  'speed-limit-up-enabled': false,rn  'start-added-torrents': true,rn  'trash-original-torrent-files': false,rn  'umask': 2,rn  'upload-limit': 100,rn  'upload-limit-enabled': 0,rn  'upload-slots-per-torrent': 14,rn  'utp-enabled': truern}rn

Setting up YouTube downloads

sudo apt updaternsudo apt install -y python3 python3-pip ffmpegrnrnpip install -U yt-dlprnpip install ytdl-subrnrnmkdir -p /molecule/Media/YouTubernrnconfiguration:rn  working_directory: /home/atom/.cache/ytdl-subrnrnyt-dlp rn  --download-archive /home/atom/.config/ytdl-archive.txt rn  --no-overwrites rn  -o '/molecule/Media/YouTube/%(uploader)s/%(upload_date)s - %(title)s.%(ext)s' rn  -f 'best[height<=720]' rn  https://www.youtube.com/@BlokandDino

Similar Posts

Other projects and posts you might find interesting.

0 Comments