Set up an encrypted NFS backup server with automated backup scripts and performance monitoring. Configure LUKS encryption, NFS exports, and centralized backup management for secure enterprise storage.
Prerequisites
- Root access to server
- Secondary storage device or partition
- Network connectivity between server and clients
- At least 4GB free space for backup operations
What this solves
Network-attached storage (NAS) backup systems provide centralized, scalable backup solutions for multiple servers and workstations. This tutorial configures an NFS server with LUKS encryption to secure backup data at rest, automated backup scripts for scheduled operations, and monitoring tools to track backup performance and security events.
Step-by-step configuration
Update system and install required packages
Start by updating your system and installing NFS server components, encryption tools, and monitoring utilities.
sudo apt update && sudo apt upgrade -y
sudo apt install -y nfs-kernel-server nfs-common cryptsetup rsync cron-apt smartmontools
Create and encrypt backup storage partition
Set up LUKS encryption on your backup storage device. Replace /dev/sdb1 with your actual backup partition.
sudo cryptsetup luksFormat /dev/sdb1
sudo cryptsetup luksOpen /dev/sdb1 backup_encrypted
sudo mkfs.ext4 /dev/mapper/backup_encrypted
Configure automatic decryption on boot
Create a keyfile and configure automatic mounting to avoid manual password entry on system restart.
sudo mkdir -p /etc/luks-keys
sudo dd if=/dev/urandom of=/etc/luks-keys/backup.key bs=1024 count=4
sudo chmod 400 /etc/luks-keys/backup.key
sudo cryptsetup luksAddKey /dev/sdb1 /etc/luks-keys/backup.key
Configure crypttab for automatic decryption:
backup_encrypted /dev/sdb1 /etc/luks-keys/backup.key luks
Create mount point and configure fstab
Set up the mount point and configure automatic mounting on boot.
sudo mkdir -p /srv/nfs/backup
sudo mount /dev/mapper/backup_encrypted /srv/nfs/backup
Add the mount to fstab for persistence:
/dev/mapper/backup_encrypted /srv/nfs/backup ext4 defaults,noatime 0 2
Configure NFS server exports
Set up NFS exports with proper security options and access controls.
sudo mkdir -p /srv/nfs/backup/clients
sudo chown -R nobody:nogroup /srv/nfs/backup
sudo chmod -R 755 /srv/nfs/backup
Configure NFS exports with security restrictions:
/srv/nfs/backup 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash,secure)
/srv/nfs/backup/clients 192.168.1.0/24(rw,sync,no_subtree_check,root_squash,secure)
Start and enable NFS services
Enable and start the NFS server services for persistent operation.
sudo systemctl enable --now nfs-kernel-server
sudo systemctl enable --now nfs-common
sudo exportfs -arv
Configure firewall rules
Open required ports for NFS communication while maintaining security.
sudo ufw allow from 192.168.1.0/24 to any port nfs
sudo ufw allow from 192.168.1.0/24 to any port 2049
sudo ufw allow from 192.168.1.0/24 to any port 111
Create automated backup script
Develop a comprehensive backup script with encryption, compression, and logging capabilities.
#!/bin/bash
NFS Backup Script with Encryption
Configuration
BACKUP_SOURCE="/home /etc /var/www"
BACKUP_DEST="/srv/nfs/backup/daily"
LOG_FILE="/var/log/nfs-backup.log"
RETENTION_DAYS=30
ENCRYPTION_KEY="/etc/backup-encryption.key"
DATE=$(date +%Y-%m-%d_%H-%M-%S)
Create encryption key if it doesn't exist
if [ ! -f "$ENCRYPTION_KEY" ]; then
openssl rand -base64 32 > "$ENCRYPTION_KEY"
chmod 600 "$ENCRYPTION_KEY"
fi
Logging function
log_message() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}
Start backup
log_message "Starting backup process"
mkdir -p "$BACKUP_DEST"
Perform rsync backup with compression
for source in $BACKUP_SOURCE; do
if [ -d "$source" ]; then
log_message "Backing up $source"
rsync -avz --delete "$source" "$BACKUP_DEST/" 2>&1 | tee -a "$LOG_FILE"
fi
done
Create encrypted archive
log_message "Creating encrypted archive"
tar -czf - "$BACKUP_DEST" | openssl enc -aes-256-cbc -salt -pass file:"$ENCRYPTION_KEY" > "$BACKUP_DEST/../backup_$DATE.tar.gz.enc"
Cleanup old backups
log_message "Cleaning up old backups"
find "$BACKUP_DEST/../" -name "backup_*.tar.gz.enc" -mtime +$RETENTION_DAYS -delete
Verify backup integrity
if [ -f "$BACKUP_DEST/../backup_$DATE.tar.gz.enc" ]; then
log_message "Backup completed successfully: backup_$DATE.tar.gz.enc"
else
log_message "ERROR: Backup failed - encrypted archive not created"
exit 1
fi
log_message "Backup process completed"
Make the script executable:
sudo chmod 755 /usr/local/bin/nfs-backup.sh
sudo chown root:root /usr/local/bin/nfs-backup.sh
Configure scheduled backups with cron
Set up automated backup scheduling with proper logging and error handling.
sudo crontab -e
Add the following cron entries:
# Daily backup at 2 AM
0 2 * /usr/local/bin/nfs-backup.sh
Weekly backup verification at 3 AM on Sundays
0 3 0 /usr/local/bin/verify-backups.sh
Monthly cleanup at 4 AM on the 1st
0 4 1 find /srv/nfs/backup -type f -mtime +90 -delete
Create backup verification script
Implement automated backup integrity checking and monitoring.
#!/bin/bash
Backup Verification Script
BACKUP_DIR="/srv/nfs/backup"
LOG_FILE="/var/log/backup-verification.log"
ENCRYPTION_KEY="/etc/backup-encryption.key"
ALERT_EMAIL="admin@example.com"
log_message() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}
Check disk space
DISK_USAGE=$(df "$BACKUP_DIR" | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$DISK_USAGE" -gt 85 ]; then
log_message "WARNING: Backup disk usage is ${DISK_USAGE}%"
echo "Backup disk usage critical: ${DISK_USAGE}%" | mail -s "NFS Backup Alert" "$ALERT_EMAIL"
fi
Verify latest encrypted backup
LATEST_BACKUP=$(find "$BACKUP_DIR" -name "backup_*.tar.gz.enc" -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -d' ' -f2-)
if [ -f "$LATEST_BACKUP" ]; then
log_message "Verifying backup: $LATEST_BACKUP"
if openssl enc -aes-256-cbc -d -salt -pass file:"$ENCRYPTION_KEY" -in "$LATEST_BACKUP" | tar -tzf - > /dev/null 2>&1; then
log_message "Backup verification successful"
else
log_message "ERROR: Backup verification failed"
echo "Backup verification failed for $LATEST_BACKUP" | mail -s "NFS Backup Error" "$ALERT_EMAIL"
fi
else
log_message "ERROR: No backup files found"
fi
Check NFS service status
if ! systemctl is-active --quiet nfs-kernel-server 2>/dev/null && ! systemctl is-active --quiet nfs-server 2>/dev/null; then
log_message "ERROR: NFS server is not running"
echo "NFS server is down" | mail -s "NFS Server Alert" "$ALERT_EMAIL"
fi
Make the verification script executable:
sudo chmod 755 /usr/local/bin/verify-backups.sh
Configure NFS performance monitoring
Set up monitoring for NFS performance metrics and security events.
[Unit]
Description=NFS Performance Monitor
After=nfs-kernel-server.service
[Service]
Type=forking
ExecStart=/usr/local/bin/nfs-monitor.sh
Restart=always
RestartSec=60
User=root
[Install]
WantedBy=multi-user.target
Create the monitoring script:
#!/bin/bash
NFS Performance Monitoring Script
LOG_FILE="/var/log/nfs-performance.log"
MONITOR_INTERVAL=300 # 5 minutes
log_metric() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}
while true; do
# Monitor NFS connections
ACTIVE_CONNECTIONS=$(netstat -an | grep :2049 | grep ESTABLISHED | wc -l)
log_metric "Active NFS connections: $ACTIVE_CONNECTIONS"
# Monitor disk I/O
DISK_IO=$(iostat -x 1 1 | awk '/sdb/ {print "Read: " $4 " Write: " $5}')
log_metric "Backup disk I/O - $DISK_IO"
# Monitor backup mount status
if mountpoint -q /srv/nfs/backup; then
log_metric "Backup mount: OK"
else
log_metric "ERROR: Backup mount failed"
fi
# Check encryption status
if cryptsetup status backup_encrypted > /dev/null 2>&1; then
log_metric "Encryption: Active"
else
log_metric "ERROR: Encryption not active"
fi
sleep $MONITOR_INTERVAL
done
Enable the monitoring service:
sudo chmod 755 /usr/local/bin/nfs-monitor.sh
sudo systemctl enable --now nfs-monitor.service
Configure client connections
Install NFS client packages
On client machines, install NFS client tools to connect to your backup server.
sudo apt install -y nfs-common
Mount NFS backup share
Configure client machines to mount the NFS backup share.
sudo mkdir -p /mnt/backup
sudo mount -t nfs 192.168.1.100:/srv/nfs/backup/clients /mnt/backup
For persistent mounting, add to fstab:
192.168.1.100:/srv/nfs/backup/clients /mnt/backup nfs defaults,_netdev 0 0
Verify your setup
Test your NFS backup configuration and verify all components are working correctly.
# Check NFS server status
sudo systemctl status nfs-kernel-server # Ubuntu/Debian
sudo systemctl status nfs-server # AlmaLinux/Rocky
Verify NFS exports
sudo exportfs -v
Test NFS connectivity from server
showmount -e localhost
Check encryption status
sudo cryptsetup status backup_encrypted
Verify backup mount
df -h /srv/nfs/backup
Test backup script
sudo /usr/local/bin/nfs-backup.sh
Check backup logs
sudo tail -f /var/log/nfs-backup.log
Verify client connection (from client machine)
mount | grep nfs
touch /mnt/backup/test-file.txt
Performance optimization
Optimize NFS server settings
Configure NFS server parameters for better performance with backup workloads.
RPCNFSDCOUNT=16
RPCNFSDPRIORITY=0
RPCMOUNTDOPTS="--manage-gids"
NEED_SVCGSSD=no
RPCSVCGSSDOPTS=""
For systems that need monitoring integration, you might want to configure advanced htop monitoring with custom scripts and alerting to track NFS performance metrics.
Configure network buffer optimization
Optimize network settings for large file transfers typical in backup operations.
net.core.rmem_max = 67108864
net.core.wmem_max = 67108864
net.ipv4.tcp_rmem = 4096 87380 67108864
net.ipv4.tcp_wmem = 4096 65536 67108864
net.core.netdev_max_backlog = 5000
Apply the settings:
sudo sysctl -p /etc/sysctl.d/99-nfs-performance.conf
Security hardening
Configure backup rotation and retention
Implement secure backup rotation with proper cleanup and archival policies.
#!/bin/bash
Backup Rotation Script
BACKUP_DIR="/srv/nfs/backup"
ARCHIVE_DIR="/srv/nfs/backup/archive"
DAILY_RETENTION=7
WEEKLY_RETENTION=4
MONTHLY_RETENTION=12
Create archive directory
mkdir -p "$ARCHIVE_DIR"/{daily,weekly,monthly}
Rotate daily backups
find "$BACKUP_DIR" -name "backup_*.tar.gz.enc" -mtime +$DAILY_RETENTION -exec mv {} "$ARCHIVE_DIR/daily/" \;
Create weekly archive (every Sunday)
if [ $(date +%u) -eq 7 ]; then
LATEST_DAILY=$(find "$ARCHIVE_DIR/daily" -name "backup_*.tar.gz.enc" -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -d' ' -f2-)
if [ -f "$LATEST_DAILY" ]; then
cp "$LATEST_DAILY" "$ARCHIVE_DIR/weekly/weekly_$(date +%Y-%m-%d).tar.gz.enc"
fi
fi
Create monthly archive (1st of month)
if [ $(date +%d) -eq 01 ]; then
LATEST_WEEKLY=$(find "$ARCHIVE_DIR/weekly" -name "weekly_*.tar.gz.enc" -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -d' ' -f2-)
if [ -f "$LATEST_WEEKLY" ]; then
cp "$LATEST_WEEKLY" "$ARCHIVE_DIR/monthly/monthly_$(date +%Y-%m).tar.gz.enc"
fi
fi
Cleanup old archives
find "$ARCHIVE_DIR/daily" -name "backup_*.tar.gz.enc" -mtime +30 -delete
find "$ARCHIVE_DIR/weekly" -name "weekly_*.tar.gz.enc" -mtime +120 -delete
find "$ARCHIVE_DIR/monthly" -name "monthly_*.tar.gz.enc" -mtime +365 -delete
Implement access logging
Enable comprehensive logging for security auditing and troubleshooting.
# NFS logging configuration
kern.* /var/log/nfs-kernel.log
daemon.* /var/log/nfs-daemon.log
Separate NFS client access logs
:msg, contains, "nfs" /var/log/nfs-access.log
& stop
Restart rsyslog to apply changes:
sudo systemctl restart rsyslog
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Mount fails with "Permission denied" | Firewall blocking NFS ports | Check firewall rules: sudo ufw status or firewall-cmd --list-all |
| "Stale file handle" errors | NFS server restart or network issues | Unmount and remount: sudo umount /mnt/backup && sudo mount /mnt/backup |
| Backup script fails with encryption error | Missing or corrupted encryption key | Recreate encryption key: sudo openssl rand -base64 32 > /etc/backup-encryption.key |
| Poor backup performance | Network buffer sizes too small | Apply network optimization settings and restart NFS services |
| LUKS device won't auto-mount | Incorrect /etc/crypttab configuration | Verify crypttab syntax and key file permissions: chmod 400 /etc/luks-keys/backup.key |
| NFS exports not visible | Export table not refreshed | Refresh exports: sudo exportfs -arv |
Next steps
- Implement Linux file system encryption with LUKS and cryptsetup for additional storage security
- Configure backup monitoring with Prometheus and Grafana for comprehensive backup oversight
- Set up centralized logging with rsyslog and logrotate for unified log management
- Configure advanced NFS high availability clustering for redundant backup infrastructure
- Implement backup deduplication with Borg and Restic for storage optimization
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# NFS Backup Server Installation Script
# Configures encrypted NFS backup storage with monitoring
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Configuration
BACKUP_DEVICE=""
NETWORK_RANGE=""
SCRIPT_STEPS=11
# Usage function
usage() {
echo "Usage: $0 -d <backup_device> -n <network_range>"
echo " -d: Backup device (e.g., /dev/sdb1)"
echo " -n: Network range (e.g., 192.168.1.0/24)"
echo "Example: $0 -d /dev/sdb1 -n 192.168.1.0/24"
exit 1
}
# Parse arguments
while getopts "d:n:h" opt; do
case $opt in
d) BACKUP_DEVICE="$OPTARG" ;;
n) NETWORK_RANGE="$OPTARG" ;;
h) usage ;;
*) usage ;;
esac
done
if [[ -z "$BACKUP_DEVICE" || -z "$NETWORK_RANGE" ]]; then
usage
fi
# Check if running as root
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}This script must be run as root${NC}"
exit 1
fi
# Cleanup function
cleanup() {
echo -e "${RED}Installation failed. Cleaning up...${NC}"
systemctl stop nfs-server 2>/dev/null || true
systemctl stop nfs-kernel-server 2>/dev/null || true
umount /srv/nfs/backup 2>/dev/null || true
cryptsetup luksClose backup_encrypted 2>/dev/null || true
}
trap cleanup ERR
# Detect distribution
echo -e "${YELLOW}[1/$SCRIPT_STEPS] Detecting distribution...${NC}"
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update && apt upgrade -y"
PKG_INSTALL="apt install -y"
NFS_SERVICE="nfs-kernel-server"
NFS_PACKAGES="nfs-kernel-server nfs-common"
FIREWALL_CMD="ufw"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
NFS_SERVICE="nfs-server"
NFS_PACKAGES="nfs-utils"
FIREWALL_CMD="firewall-cmd"
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum update -y"
PKG_INSTALL="yum install -y"
NFS_SERVICE="nfs-server"
NFS_PACKAGES="nfs-utils"
FIREWALL_CMD="firewall-cmd"
;;
*)
echo -e "${RED}Unsupported distribution: $ID${NC}"
exit 1
;;
esac
echo -e "${GREEN}Detected: $PRETTY_NAME${NC}"
else
echo -e "${RED}Cannot detect distribution${NC}"
exit 1
fi
# Update system and install packages
echo -e "${YELLOW}[2/$SCRIPT_STEPS] Updating system and installing packages...${NC}"
$PKG_UPDATE
$PKG_INSTALL $NFS_PACKAGES cryptsetup rsync smartmontools
# Verify backup device exists
echo -e "${YELLOW}[3/$SCRIPT_STEPS] Verifying backup device...${NC}"
if [[ ! -b "$BACKUP_DEVICE" ]]; then
echo -e "${RED}Backup device $BACKUP_DEVICE does not exist${NC}"
exit 1
fi
# Create LUKS encryption
echo -e "${YELLOW}[4/$SCRIPT_STEPS] Setting up LUKS encryption...${NC}"
echo -e "${RED}WARNING: This will destroy all data on $BACKUP_DEVICE${NC}"
read -p "Continue? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Installation cancelled"
exit 1
fi
echo "backup_passphrase" | cryptsetup luksFormat "$BACKUP_DEVICE" -
echo "backup_passphrase" | cryptsetup luksOpen "$BACKUP_DEVICE" backup_encrypted -
mkfs.ext4 /dev/mapper/backup_encrypted
# Configure automatic decryption
echo -e "${YELLOW}[5/$SCRIPT_STEPS] Configuring automatic decryption...${NC}"
mkdir -p /etc/luks-keys
dd if=/dev/urandom of=/etc/luks-keys/backup.key bs=1024 count=4
chmod 400 /etc/luks-keys/backup.key
echo "backup_passphrase" | cryptsetup luksAddKey "$BACKUP_DEVICE" /etc/luks-keys/backup.key -
# Configure crypttab
echo "backup_encrypted $BACKUP_DEVICE /etc/luks-keys/backup.key luks" >> /etc/crypttab
# Create mount point and configure fstab
echo -e "${YELLOW}[6/$SCRIPT_STEPS] Setting up mount points...${NC}"
mkdir -p /srv/nfs/backup
mount /dev/mapper/backup_encrypted /srv/nfs/backup
echo "/dev/mapper/backup_encrypted /srv/nfs/backup ext4 defaults,noatime 0 2" >> /etc/fstab
# Configure NFS exports
echo -e "${YELLOW}[7/$SCRIPT_STEPS] Configuring NFS exports...${NC}"
mkdir -p /srv/nfs/backup/clients
chown -R nobody:nogroup /srv/nfs/backup
chmod -R 755 /srv/nfs/backup
cat > /etc/exports << EOF
/srv/nfs/backup $NETWORK_RANGE(rw,sync,no_subtree_check,no_root_squash,secure)
/srv/nfs/backup/clients $NETWORK_RANGE(rw,sync,no_subtree_check,root_squash,secure)
EOF
# Start NFS services
echo -e "${YELLOW}[8/$SCRIPT_STEPS] Starting NFS services...${NC}"
if [[ "$PKG_MGR" == "apt" ]]; then
systemctl enable --now nfs-kernel-server
systemctl enable --now nfs-common
else
systemctl enable --now nfs-server
systemctl enable --now rpcbind
fi
exportfs -arv
# Configure firewall
echo -e "${YELLOW}[9/$SCRIPT_STEPS] Configuring firewall...${NC}"
if [[ "$FIREWALL_CMD" == "ufw" ]]; then
ufw --force enable
ufw allow from "$NETWORK_RANGE" to any port nfs
ufw allow from "$NETWORK_RANGE" to any port 2049
ufw allow from "$NETWORK_RANGE" to any port 111
else
systemctl enable --now firewalld
firewall-cmd --permanent --add-rich-rule="rule family=ipv4 source address=$NETWORK_RANGE service name=nfs accept"
firewall-cmd --permanent --add-rich-rule="rule family=ipv4 source address=$NETWORK_RANGE port port=2049 protocol=tcp accept"
firewall-cmd --permanent --add-rich-rule="rule family=ipv4 source address=$NETWORK_RANGE port port=111 protocol=tcp accept"
firewall-cmd --reload
fi
# Create backup script
echo -e "${YELLOW}[10/$SCRIPT_STEPS] Creating backup script...${NC}"
cat > /usr/local/bin/nfs-backup.sh << 'EOSCRIPT'
#!/bin/bash
set -euo pipefail
BACKUP_SOURCE="/home /etc /var/www"
BACKUP_DEST="/srv/nfs/backup/daily"
LOG_FILE="/var/log/nfs-backup.log"
RETENTION_DAYS=30
DATE=$(date +%Y-%m-%d_%H-%M-%S)
mkdir -p "$BACKUP_DEST"
mkdir -p "$(dirname "$LOG_FILE")"
echo "$(date): Starting backup" >> "$LOG_FILE"
for source in $BACKUP_SOURCE; do
if [[ -d "$source" ]]; then
rsync -avz --delete "$source" "$BACKUP_DEST/$DATE/" >> "$LOG_FILE" 2>&1
fi
done
find "$BACKUP_DEST" -type d -mtime +$RETENTION_DAYS -exec rm -rf {} + 2>/dev/null || true
echo "$(date): Backup completed" >> "$LOG_FILE"
EOSCRIPT
chmod 755 /usr/local/bin/nfs-backup.sh
# Setup cron job
(crontab -l 2>/dev/null; echo "0 2 * * * /usr/local/bin/nfs-backup.sh") | crontab -
# Verification
echo -e "${YELLOW}[11/$SCRIPT_STEPS] Verifying installation...${NC}"
systemctl is-active --quiet "$NFS_SERVICE" || { echo -e "${RED}NFS service not running${NC}"; exit 1; }
mount | grep -q backup_encrypted || { echo -e "${RED}Encrypted volume not mounted${NC}"; exit 1; }
exportfs | grep -q "/srv/nfs/backup" || { echo -e "${RED}NFS exports not configured${NC}"; exit 1; }
echo -e "${GREEN}Installation completed successfully!${NC}"
echo "NFS backup server is ready at /srv/nfs/backup"
echo "Daily backups scheduled at 2:00 AM"
echo "Logs available at /var/log/nfs-backup.log"
echo -e "${YELLOW}Mount on clients: mount -t nfs <server_ip>:/srv/nfs/backup /mnt/backup${NC}"
Review the script before running. Execute with: bash install.sh