All In One Debian NAS and Application Server Setup
This documents the initial setup of a Debian-based NAS system with SnapRAID, MergerFS, Docker, and Samba.
This documents the initial setup of a Debian-based NAS system. It’s based on my setup and is a work in progress and is therefor not complete. That being said, I have been using it like this for 6+ months and while there is still work to be done everything is working just fine.
System Information
- Terramaster F8 SSD Plus
- WD Black SN850X NVMe SSD (M.2 2280)
- Debian (Bookworm)
- SnapRAID
- MergerFS
- Docker
Important Warning: Initial SnapRAID Sync
The initial SnapRAID sync can be very intensive on NVMe drives. With 14TB of data, the sync took approximately 3 hours and drove temperatures up to around 200°F (93°C), which is dangerously hot for NVMe drives. The specified operating temperature range for these drives is 32°F to 185°F (0°C to 85°C), so it’s crucial to monitor temperatures during the initial sync.
You can monitor drive temperatures using this command:
watch -n 1 'for sensor in /sys/class/nvme/nvme*/hwmon*/temp*_input; do temp=$(cat $sensor); celsius=$(echo "scale=2; $temp/1000" | bc); fahrenheit=$(echo "scale=2; ($celsius * 9/5) + 32" | bc); echo "Sensor $(basename $sensor): $fahrenheit°F"; done'
If temperatures exceed 185°F (85°C) during the sync:
- Press Ctrl+Z to pause the sync process
- Wait for drives to cool down
- Resume the sync with fg command
Disk Configuration
Hardware Layout
- 4x 8TB NVMe drives (nvme0n1, nvme1n1, nvme2n1, nvme3n1)
- System is configured with RAID1 for the root filesystem and separate data drives
Partition Layout
System Drive 1 (nvme0n1):
/dev/nvme0n1p1: 1GB - /boot (ext4)/dev/nvme0n1p2: 1GB - /boot/efi (vfat)/dev/nvme0n1p3: 500GB - RAID1 member/dev/nvme0n1p4: 10GB - Swap/dev/nvme0n1p5: 7.5TB - /mnt/data1 (ext4)
System Drive 2 (nvme1n1):
/dev/nvme1n1p1: 1GB - (reserved for boot) (ext4)/dev/nvme1n1p2: 1GB - (reserved for efi) (vfat)/dev/nvme1n1p3: 500GB - RAID1 member/dev/nvme1n1p4: 10GB - Swap/dev/nvme1n1p5: 7.5TB - /mnt/data2 (ext4)
Data Drives:
/dev/nvme2n1p1: 8TB - /mnt/data3 (ext4)/dev/nvme3n1p1: 8TB - /mnt/parity1 (ext4)
RAID Configuration
- RAID1 array (md0) composed of:
/dev/nvme0n1p3/dev/nvme1n1p3
- Array Size: 500GB
LVM Configuration
- Physical Volume:
/dev/md0(500GB) - Volume Group: root (500GB)
- Logical Volume: root (500GB mounted as /)
Mount Points
/→/dev/mapper/root-root(500GB)/boot→/dev/nvme0n1p1(1GB)/boot/efi→/dev/nvme0n1p2(1GB)/mnt/data1→/dev/nvme0n1p5(7.5TB)/mnt/data2→/dev/nvme1n1p5(7.5TB)/mnt/data3→/dev/nvme2n1p1(8TB)/mnt/parity1→/dev/nvme3n1p1(8TB)[SWAP]→ nvme0n1p4 and nvme1n1p4 (10GB each)
Debian Installation Choices
During the Debian installation process, when you reach the “Software Selection” screen:
Uncheck/Deselect:
- Debian desktop environment
- GNOME
- Print server
Check/Select:
- SSH server
- Standard system utilities
This ensures a minimal server installation without unnecessary desktop components, while enabling remote SSH access.
1. Initial Access and Network Setup
Copy your SSH public key to enable passwordless login:
# If you don't have an SSH key yet, create one
ssh-keygen -t ed25519
# Copy your public key to the server
ssh-copy-id user@nas_ip_address
The system is configured for SSH access. Replace ‘nas_ip_address’ with your NAS’s IP address:
ssh user@nas_ip_address
After copying your key, you should be able to SSH without entering a password.
2. Configure System Path
Configure PATH for both regular user and root. This ensures system administration commands are available:
# Configure PATH for regular user
echo 'export PATH=$PATH:/usr/sbin:/sbin' >> ~/.bashrc
source ~/.bashrc
# Configure PATH for root user
su -
echo 'export PATH=$PATH:/usr/sbin:/sbin' >> ~/.bashrc
source ~/.bashrc
3. Verify RAID Status
Check the status of the RAID1 array to ensure proper synchronization:
sudo mdadm --detail /dev/md0 | grep State
The output should show:
State : clean
If you see any other state (like “resync” or “recovery”), wait for the array to finish synchronizing before proceeding.
If it says anything else you should take a closer look at the array and get it back to a clean state.
4. Initial System Updates (as root)
Initial commands must be run as root until sudo is installed:
su -
Update package repositories and upgrade system:
# Update package lists
apt update
# Perform full system upgrade
apt upgrade -y
5. User Setup and Sudo Installation
Install and configure sudo (as root):
# Install sudo package
apt install sudo
# Add user to sudo group (replace 'username' with your actual username)
usermod -aG sudo username
# Exit root shell to apply changes
exit
Note: After sudo installation, you must log out and log back in for group changes to take effect.
6. MergerFS Setup
MergerFS is a union filesystem that allows combining multiple drives into a single pool. For complete documentation and advanced configuration options, see the official MergerFS documentation.
Install MergerFS:
apt install -y mergerfs
Configure the storage pool by adding this line to /etc/fstab:
/mnt/data* /mnt/storage mergerfs allow_other,cache.files=off,dropcacheonclose=false,category.create=mfs,minfreespace=50G,fsname=storage 0 0
The fstab configuration explained:
/mnt/data*: Sources all directories matching this pattern (data1, data2, data3)/mnt/storage: Mount point where the merged filesystem will be accessiblemergerfs: Filesystem type- Options:
allow_other: Required for MergerFS versions <2.35.0 to allow non-root accesscache.files=off: Disable file caching for better consistencydropcacheonclose=false: Keep cache on file close for better performancecategory.create=mfs: Use most-free-space policy for new filesminfreespace=50G: Minimum 50GB free space required on target drivefsname=storage: Name for the merged filesystem
0 0: No filesystem checks at boot (recommended for mergerfs)
After mounting, the storage pool will be available:
Filesystem Size Used Avail Use% Mounted on
storage 21T 84K 20T 1% /mnt/storage
7. Memory Testing
Before setting up SnapRAID, it’s crucial to verify system memory integrity. RAM issues are a common cause of data corruption and loss when using SnapRAID. Install memtest86+:
apt install memtest86+ -y
Reboot the system and:
- At the GRUB boot menu, select “Memory test (memtest86+)”
- Let the test run for at least one complete pass
- If any errors are found, resolve memory issues before running snapraid sync for the first time
- If the test passes, continue with SnapRAID setup
For more information about hardware requirements and reliability, see the SnapRAID FAQ.
8. SnapRAID Setup
SnapRAID provides file-based snapshot parity protection. Install SnapRAID:
apt install snapraid -y
Create the directory for one of the content files. This is also part of the raid1 array for the OS so we should have plenty of copies of the content file:
mkdir /var/snapraid
Configure SnapRAID by creating /etc/snapraid.conf:
# Parity file location (first level of parity)
parity /mnt/parity1/snapraid.parity
# Content file locations - these store filesystem metadata
# Multiple copies for redundancy
content /mnt/data1/snapraid.content
content /mnt/data2/snapraid.content
content /mnt/data3/snapraid.content
content /var/snapraid/snapraid.content
# Data disk definitions - assign names to the data disks
data d1 /mnt/data1/
data d2 /mnt/data2/
data d3 /mnt/data3/
# Exclude common system directories
exclude /lost+found/
Initialize SnapRAID by running a sync:
snapraid sync
Note: The first sync will create the content files. You may see warnings about missing content files - this is normal for the initial run.
9. SnapRAID Runner Setup
Install snapraid-runner for automated maintenance and email notifications:
# Create directory for snapraid-runner
mkdir /opt/snapraid-runner
# Download configuration and script files
wget https://raw.githubusercontent.com/Chronial/snapraid-runner/refs/heads/master/snapraid-runner.conf.example -O /opt/snapraid-runner/snapraid-runner.conf
wget https://raw.githubusercontent.com/Chronial/snapraid-runner/refs/heads/master/snapraid-runner.py -O /opt/snapraid-runner/snapraid-runner.py
Configure snapraid-runner by editing /opt/snapraid-runner/snapraid-runner.conf:
[snapraid]
# Path to the snapraid executable
executable = /usr/bin/snapraid
# Path to your snapraid config file
config = /etc/snapraid.conf
# Maximum number of files that can be deleted before requiring manual intervention
# Set to -1 to disable the threshold and always sync
deletethreshold = 40
# Whether to update the access time of files. Generally best left as false
# to avoid unnecessary writes to the filesystem
touch = false
[logging]
# Path to the log file
file = /var/log/snapraid.log
# Maximum size of log file in bytes before rotation. Empty or 0 means no limit
# When size is reached, the log is rotated and up to 9 backup files are kept
# Example: maxsize=1024 will rotate at 1MB and keep snapraid.log plus snapraid.log.1 through snapraid.log.9
maxsize =
[email]
# When to send emails: comma-separated list of 'success' and/or 'error'
sendon = success,error
# Use a shorter email format (omits snapraid output)
short = true
subject = [SnapRAID] Status Report:
from = your.email@domain.com
to = your.email@domain.com
# Maximum size of email body in KB. Logs will be truncated if they exceed this size
maxsize = 1024
[smtp]
# Email server settings (example uses Mailgun)
host = smtp.mailgun.com
port = 587
# SSL is older encryption (typically port 465)
ssl = false
# TLS is modern encryption (typically port 587)
tls = true
user = your.email@domain.com
password = your-smtp-password
[scrub]
# Whether to perform periodic data verification (scrubbing)
enabled = true
# Either number of days between scrubs or a custom plan name
# Example: plan=12 will scrub every 12 days
# Example: plan=bad will use the 'bad' plan from snapraid config
plan = 12
# Only scrub files older than this many days
# Prevents frequent re-verification of recently changed files
# Only applies when plan is a number
older-than = 10
Configuration explained:
deletethreshold: Maximum number of files that can be deleted before requiring manual interventionsendon: When to send email notifications (success and/or error)smtp: Email server settings (example uses Mailgun)scrub: Settings for periodic data verification (currently disabled)
Test the configuration:
/usr/bin/python3 /opt/snapraid-runner/snapraid-runner.py -c /opt/snapraid-runner/snapraid-runner.conf
Set up automated daily sync by adding to root’s crontab:
sudo crontab -e
Add this line to run daily at 3 AM:
00 03 * * * /usr/bin/python3 /opt/snapraid-runner/snapraid-runner.py -c /opt/snapraid-runner/snapraid-runner.conf
Verify the cron job was added:
sudo crontab -l
10. Docker Setup
First, remove any conflicting Docker packages that might be installed:
for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove $pkg; done
Add Docker’s official GPG key and set up the repository:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
Add the Docker repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
Update the package database and install Docker packages:
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Set up user permissions by creating the docker group and adding your user to it. This allows running Docker commands without sudo:
sudo groupadd docker
sudo usermod -aG docker $USER
Note: After adding your user to the docker group, you may need to log out and back in for the changes to take effect.
Activate the group changes in current shell:
newgrp docker
Verify Docker service status and enable it to start on boot:
sudo systemctl status docker
You should see output similar to this:
● docker.service - Docker Application Container Engine
Loaded: loaded (/lib/systemd/system/docker.service; enabled; preset: enabled)
Active: active (running) since Sat 2025-01-18 18:29:01 PST; 3h 30min ago
TriggeredBy: ● docker.socket
Docs: https://docs.docker.com
Main PID: 6324 (dockerd)
Tasks: 13
Memory: 46.4M
CPU: 1.932s
CGroup: /system.slice/docker.service
└─6324 /usr/sbin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
Look for these indicators:
Loaded: loaded (...; enabled; preset: enabled)- Shows Docker will start on bootActive: active (running)- Shows Docker is currently running
If Docker is not enabled or running:
# Start Docker service
sudo systemctl start docker
# Enable Docker to start on boot
sudo systemctl enable docker
11. Docker Compose Setup
Docker Compose configurations for specific services and applications will be documented in a separate guide focused on container deployment and management.
12. Samba Setup
Install Samba for file sharing across the network:
sudo apt install samba
Configure Samba by editing /etc/samba/smb.conf. This setup includes a personal storage share and a special folder for document scanning. The configuration prioritizes security over convenience by requiring user authentication and disabling guest access:
Note: Samba has two equivalent parameters for controlling write access: writable = yes and read only = no. They do exactly the same thing. We use writable here as it’s more intuitive, though you might see read only in other configurations.
[global]
# Basic Samba configuration
# Windows workgroup name
workgroup = WORKGROUP
# Server description (%v shows version)
server string = Samba Server %v
# Require user authentication
security = user
# Don't allow guest access if user/pass fails
map to guest = Bad User
# %m is replaced by client machine name
log file = /var/log/samba/log.%m
# Rotate logs after 1000KB
max log size = 1000
# Disable printer sharing for security and simplicity
load printers = no
printcap name = /dev/null
# Personal storage share
[jbrennan]
comment = My Storage Folder
path = /mnt/storage/jbrennan
# Share is visible in network browsing
browseable = yes
# Allow write access (if user has permission)
writable = yes
# Require authentication (no guest access)
guest ok = no
# New files get rw-rw---- permissions
create mask = 0660
# New directories get rwxrwx--- permissions
directory mask = 0770
# Paperless-ngx scanning folder
[consumption]
comment = Consumption Folder
path = /home/jbrennan/apps/paperless/consumption
# Share is visible in network browsing
browseable = yes
# Allow write access (if user has permission)
writable = yes
# Require authentication
guest ok = no
# New files get rw-rw---- permissions
create mask = 0660
# New directories get rwxrwx--- permissions
directory mask = 0770
Security considerations:
- Each share requires valid user credentials
- Guest access is completely disabled
- New files and directories get restrictive permissions by default
- Printer sharing is disabled to reduce attack surface
- Separate user for scanner access limits potential security impact
The consumption folder is used by paperless-ngx for document scanning. When the paperless-ngx container starts, it will set ownership of the consumption folder to jbrennan:jbrennan. We’ll create a dedicated user for scanner access and ensure it has the right permissions through group membership:
# Create user without home directory (-M prevents home directory creation) and don't allow shell access, but user can still access network services like Samba
sudo useradd -M -s /usr/sbin/nologin consumption
# Set Samba password for the user (this is separate from system password)
sudo smbpasswd -a consumption
# Add consumption user to jbrennan group for folder access
sudo usermod -aG jbrennan consumption
# Set appropriate permissions (774 = owner/group read-write, others read-only)
# This allows both jbrennan and the consumption user (via group) to read/write
chmod 774 /home/jbrennan/apps/paperless/consumption
Start the Samba services to apply the configuration:
sudo systemctl restart smbd nmbd
To verify the Samba configuration and check for potential issues:
# Test configuration file syntax
testparm
# View current Samba user list
sudo pdbedit -L -v