Red Hat Enterprise Linux 9 — File Sharing & Advanced Storage
Companion skill to rhel-server-admin (parent). Covers NFS, Samba/CIFS, Stratis, LVM-VDO, XFS administration, disk quotas, POSIX ACLs, and storage monitoring on RHEL 9.x (and compatible: AlmaLinux 9, Rocky Linux 9, Oracle Linux 9).
1. NFS (nfs-utils)
sudo dnf install nfs-utils
sudo systemctl enable --now nfs-server
cat /proc/fs/nfsd/versions # expect: +4 +4.1 +4.2
/etc/exports
/srv/nfs/data 10.0.1.0/24(rw,sync,no_subtree_check,no_root_squash)
/srv/nfs/iso 10.0.1.50(ro,sync,no_subtree_check)
# NFSv4 pseudo-root
/srv/nfs4 10.0.1.0/24(rw,sync,fsid=0,crossmnt,no_subtree_check)
/srv/nfs4/data 10.0.1.0/24(rw,sync,no_subtree_check)
Key options: sync (safe) vs async (faster, risk on crash) | no_subtree_check (recommended) | root_squash (default) vs no_root_squash | all_squash,anonuid=1000,anongid=1000 (guest-like).
sudo exportfs -ra && sudo exportfs -v # apply and verify
# NFSv4 bind mount
sudo mkdir -p /srv/nfs4/data && sudo mount --bind /srv/nfs/data /srv/nfs4/data
# Persist: /etc/fstab — /srv/nfs/data /srv/nfs4/data none bind 0 0
SELinux for NFS
sudo setsebool -P nfs_export_all_rw 1
sudo setsebool -P nfs_export_all_ro 1
sudo setsebool -P use_nfs_home_dirs 1 # if sharing home dirs
sudo semanage fcontext -a -t nfs_t "/srv/nfs(/.*)?"
sudo restorecon -Rv /srv/nfs
getsebool -a | grep nfs # verify
Client (fstab and autofs)
sudo dnf install nfs-utils
# /etc/fstab
10.0.1.10:/data /mnt/nfs/data nfs4 defaults,_netdev,rsize=1048576,wsize=1048576 0 0
# Autofs on-demand mounting
sudo dnf install autofs
# /etc/auto.master.d/nfs.autofs: /mnt/nfs /etc/auto.nfs --timeout=300
# /etc/auto.nfs: data -rw,rsize=1048576,wsize=1048576 10.0.1.10:/data
sudo systemctl enable --now autofs
Firewalld and Performance
# NFSv4 only needs TCP 2049
sudo firewall-cmd --permanent --add-service=nfs
sudo firewall-cmd --reload
# NFSv3 also needs: --add-service=rpc-bind --add-service=mountd
# Restrict to zone
sudo firewall-cmd --permanent --zone=internal --add-source=10.0.1.0/24
sudo firewall-cmd --permanent --zone=internal --add-service=nfs
sudo firewall-cmd --reload
# Tuning: /etc/nfs.conf [nfsd] threads = 16 (default 8; scale with cores)
# Stats: nfsstat -s (server) | nfsstat -c (client) | nfsiostat 5
2. Samba / CIFS
sudo dnf install samba samba-common samba-client
sudo systemctl enable --now smb nmb
smb.conf (/etc/samba/smb.conf)
[global]
workgroup = WORKGROUP
server string = RHEL File Server
security = user
map to guest = Never
passdb backend = tdbsam
log file = /var/log/samba/log.%m
max log size = 1000
use sendfile = yes
[data]
path = /srv/samba/data
browseable = yes
read
valid users = @smbgroup
create mask = 0664
directory mask = 0775
force group = smbgroup
[projects]
path = /srv/samba/projects
read
valid users = @smbgroup
vfs objects = acl_xattr
map acl inherit = yes
store dos attributes = yes
inherit acls = yes
Users and Directory Setup
sudo useradd -M -s /sbin/nologin smbuser1
sudo smbpasswd -a smbuser1 # set Samba password
sudo pdbedit -L -v # list users
sudo mkdir -p /srv/samba/{data,public,projects}
sudo groupadd smbgroup
sudo chgrp -R smbgroup /srv/samba/data /srv/samba/projects
sudo chmod 2775 /srv/samba/data /srv/samba/projects # setgid
testparm && sudo systemctl reload smb
SELinux for Samba
sudo semanage fcontext -a -t samba_share_t "/srv/samba(/.*)?"
sudo restorecon -Rv /srv/samba
sudo setsebool -P samba_enable_home_dirs on
sudo setsebool -P samba_share_nfs on # if re-exporting NFS mounts
getsebool -a | grep samba # verify
ls -lZ /srv/samba/ # confirm samba_share_t
Firewalld, Client, and Windows
sudo firewall-cmd --permanent --add-service=samba && sudo firewall-cmd --reload
# Linux CIFS client
sudo dnf install cifs-utils
# /etc/fstab (credentials in /root/.smbcredentials, mode 600)
//10.0.1.10/data /mnt/samba cifs credentials=/root/.smbcredentials,uid=1000,gid=1000,_netdev 0 0
# Windows: net use Z: \\10.0.1.10\data /user:smbuser1
Audit Logging
# Add to share or [global] in smb.conf
vfs objects = full_audit
full_audit:prefix = %u|%I|%S
full_audit:success = connect disconnect mkdir rmdir open rename unlink write
full_audit:failure = connect
full_audit:facility = local5
full_audit:priority = notice
echo 'local5.* /var/log/samba/audit.log' | sudo tee /etc/rsyslog.d/samba-audit.conf
sudo systemctl restart rsyslog smb
3. Stratis Storage Management
sudo dnf install stratisd stratis-cli
sudo systemctl enable --now stratisd
Pool and Filesystem Operations
sudo stratis pool create mypool /dev/sdb
sudo stratis pool create bigpool /dev/sdc /dev/sdd # multi-device
sudo stratis pool add-data mypool /dev/sde # expand pool
# Create thin-provisioned XFS filesystems
sudo stratis filesystem create mypool appdata
sudo stratis filesystem create mypool backups
# Mount
sudo mkdir -p /mnt/appdata
sudo mount /dev/stratis/mypool/appdata /mnt/appdata
# fstab (must wait for stratisd)
UUID=<stratis-uuid> /mnt/appdata xfs defaults,x-systemd.requires=stratisd.service 0 0
# Status
stratis pool list && stratis filesystem list && stratis blockdev list
Snapshots
sudo stratis filesystem snapshot mypool appdata appdata-snap-$(date +%F)
# Mount snapshot (read-write COW clone)
sudo mount /dev/stratis/mypool/appdata-snap-2026-03-23 /mnt/snap
# Restore via rename
sudo umount /mnt/appdata
sudo stratis filesystem rename mypool appdata appdata-broken
sudo stratis filesystem rename mypool appdata-snap-2026-03-23 appdata
sudo mount /dev/stratis/mypool/appdata /mnt/appdata
sudo stratis filesystem destroy mypool appdata-broken # cleanup
Tiered Storage (Cache)
sudo stratis pool init-cache mypool /dev/nvme0n1 # add SSD/NVMe cache
sudo stratis pool add-cache mypool /dev/nvme1n1 # add more cache devices
stratis blockdev list mypool # Tier column: Data vs Cache
Destruction (must unmount and destroy filesystems first)
sudo umount /mnt/appdata
sudo stratis filesystem destroy mypool appdata
sudo stratis pool destroy mypool
4. VDO / LVM-VDO (Deduplication & Compression)
In RHEL 9, VDO is integrated into LVM as lvmvdo. Standalone VDO is deprecated.
sudo dnf install lvm2 kmod-kvdo vdo
# Create VG and VDO logical volume
sudo pvcreate /dev/sdb && sudo vgcreate vdo-vg /dev/sdb
sudo lvcreate --type vdo --name vdo-lv --size 100G --virtualsize 300G vdo-vg
# Format and mount
sudo mkfs.xfs -K /dev/vdo-vg/vdo-lv # -K skips discard (faster)
sudo mkdir -p /mnt/vdo && sudo mount /dev/vdo-vg/vdo-lv /mnt/vdo
# fstab
/dev/vdo-vg/vdo-lv /mnt/vdo xfs defaults,x-systemd.requires=vdo.service,_netdev 0 0
Statistics and Tuning
sudo vdostats --human-readable
sudo lvs -o+vdo_compression,vdo_deduplication,vdo_compression_state
sudo lvs -o name,size,data_percent,vdo_saving_percent vdo-vg
# Enable/disable per LV
sudo lvchange --compression y /dev/vdo-vg/vdo-lv
sudo lvchange --deduplication y /dev/vdo-vg/vdo-lv
Growing VDO
sudo lvextend --size +50G /dev/vdo-vg/vpool0 # physical pool
sudo lvextend --virtualsize +100G /dev/vdo-vg/vdo-lv # virtual size
sudo xfs_growfs /mnt/vdo # grow filesystem
5. XFS Administration
XFS is the default filesystem on RHEL 9. Supports online growth but NOT shrink.
xfs_info /mnt/data # block size, log, AG details
sudo xfs_growfs /mnt/data # online growth (after lvextend)
# Repair (must unmount first)
sudo umount /mnt/data
sudo xfs_repair /dev/data-vg/data-lv
sudo xfs_repair -L /dev/data-vg/data-lv # force log zeroing (last resort)
# Fragmentation
sudo xfs_db -c frag -r /dev/data-vg/data-lv # check
sudo xfs_fsr -v /mnt/data # defragment (online)
# Freeze/thaw for consistent snapshots
sudo xfs_freeze -f /mnt/data # freeze
# ... take LVM snapshot or backup ...
sudo xfs_freeze -u /mnt/data # unfreeze
xfsdump / xfsrestore
sudo dnf install xfsdump
sudo xfsdump -l 0 -L "full-backup" -M "tape1" -f /backup/data-full.dump /mnt/data
sudo xfsdump -l 1 -L "incr-backup" -M "tape1" -f /backup/data-incr.dump /mnt/data
sudo xfsrestore -f /backup/data-full.dump /mnt/restore
sudo xfsrestore -i -f /backup/data-full.dump /mnt/restore # interactive
xfsdump -I # dump inventory
6. Disk Quotas
XFS Quotas (Preferred on RHEL)
# Enable in /etc/fstab mount options
UUID=xxxx /data xfs defaults,usrquota,grpquota,prjquota 0 0
sudo mount -o remount /data
# User and group quotas
sudo xfs_quota -x -c "limit bsoft=400m bhard=500m username" /data
sudo xfs_quota -x -c "limit -g bsoft=2g bhard=2500m devteam" /data
# Project quota (directory-level)
# 1. /etc/projects: 10:/data/projectA
# 2. /etc/projid: projectA:10
sudo xfs_quota -x -c "project -s projectA" /data
sudo xfs_quota -x -c "limit -p bsoft=5g bhard=6g projectA" /data
# Reports
sudo xfs_quota -x -c "report -ugh" /data # all quotas
sudo xfs_quota -x -c "report -p" /data # project quotas
sudo xfs_quota -x -c "free" /data # free space
ext4 Quotas
sudo dnf install quota
# /etc/fstab: UUID=xxxx /data ext4 defaults,usrquota,grpquota 0 2
sudo mount -o remount /data && sudo quotacheck -cugm /data && sudo quotaon /data
sudo setquota -u username 409600 512000 0 0 /data # soft/hard blocks, inodes
sudo setquota -g devteam 2097152 2621440 0 0 /data
sudo repquota -a
7. POSIX ACLs
# XFS and ext4 have built-in ACL support on RHEL 9
# Set ACLs
sudo setfacl -m u:alice:rwx /srv/data/project
sudo setfacl -m g:auditors:r-x /srv/data/project
sudo setfacl -R -m g:devteam:rwx /srv/data/project # recursive
# Default ACLs (inherited by new files/dirs)
sudo setfacl -d -m g:devteam:rwx /srv/data/project
# View / remove
getfacl /srv/data/project
sudo setfacl -x u:alice /srv/data/project # remove entry
sudo setfacl -b /srv/data/project # remove all ACLs
# Backup and restore
getfacl -R /srv/data > /backup/data-acls.txt
setfacl --restore=/backup/data-acls.txt
ACLs with Samba
Use vfs objects = acl_xattr with map acl inherit = yes and store dos attributes = yes in smb.conf (see [projects] in Section 2).
sudo setfacl -R -m g:smbgroup:rwx /srv/samba/projects
sudo setfacl -R -d -m g:smbgroup:rwx /srv/samba/projects
ls -lZ /srv/samba/projects # verify samba_share_t
ACLs with NFS
POSIX ACLs work over NFS when server and client agree on UID/GID mapping. Set matching Domain in /etc/idmapd.conf on all hosts:
# [General]
# Domain = home.lab
sudo systemctl restart nfs-idmapd
8. Storage Monitoring
Stratis and VDO Health
stratis pool list && stratis filesystem list && stratis blockdev list
stratis daemon version
sudo vdostats --human-readable
sudo lvs -o name,size,data_percent,vdo_saving_percent
SMART Monitoring
sudo dnf install smartmontools
sudo systemctl enable --now smartd
sudo smartctl -H /dev/sda # quick health
sudo smartctl -a /dev/sda # full SMART data
sudo smartctl -t short /dev/sda # self-test (~2 min)
sudo smartctl -l selftest /dev/sda # view results
# /etc/smartmontools/smartd.conf:
# DEVICESCAN -a -o on -S on -n standby,q -s (S/../.././02|L/../../6/03) -m admin@example.com
Disk Space and I/O
df -h && df -i # filesystem + inode usage
du -sh /srv/data/* | sort -rh | head -20 # largest dirs
sudo dnf install ncdu && sudo ncdu /srv # interactive browser
sudo dnf install sysstat
iostat -xz 5 # per-device I/O (%util >80% = bottleneck)
sudo iotop -ao # per-process I/O
Alert Scripts
#!/bin/bash
# /usr/local/bin/disk-alert.sh — cron: */15 * * * * root /usr/local/bin/disk-alert.sh
THRESHOLD=85; MAILTO="admin@example.com"; HOST=$(hostname -f)
df -H --output=pcent,target | tail -n+2 | while read pct mount; do
usage=${pct%\%}
[ "$usage" -ge "$THRESHOLD" ] && echo "${mount} is ${pct} full on ${HOST}" \
| mail -s "Disk Alert: ${HOST} ${mount} ${pct}" "$MAILTO"
done
#!/bin/bash
# /usr/local/bin/stratis-health.sh — cron: */30 * * * * root /usr/local/bin/stratis-health.sh
MAILTO="admin@example.com"; HOST=$(hostname -f)
stratis pool list 2>/dev/null | tail -n+2 | while read name total used state alerts; do
[ "$state" != "Ok" ] && echo "Stratis pool ${name} state: ${state}" \
| mail -s "Stratis ALERT: ${HOST} pool ${name}" "$MAILTO"
done
sudo chmod +x /usr/local/bin/disk-alert.sh /usr/local/bin/stratis-health.sh
echo "*/15 * * * * root /usr/local/bin/disk-alert.sh" | sudo tee /etc/cron.d/disk-alert
echo "*/30 * * * * root /usr/local/bin/stratis-health.sh" | sudo tee /etc/cron.d/stratis-health
Ports Quick Reference
| Service | Ports | Protocol |
|---|---|---|
| NFSv4 | 2049 | TCP |
| NFSv3 | 111, 2049, 32765-32767 | TCP/UDP |
| Samba | 139, 445 | TCP |
| Samba NetBIOS | 137, 138 | UDP |
Anti-Patterns
| Anti-Pattern | Why It Fails | Correct Approach |
|---|---|---|
| Exporting NFS shares without restricting client IPs | Any machine on the network can mount the share; data exfiltration; unauthorized writes | Specify client IPs or subnets in /etc/exports; use root_squash (default) and sec=krb5p for authentication |
| Using Samba shares without SELinux context | SELinux blocks Samba access; admins disable SELinux instead of setting correct boolean/context | Set samba_share_t context on shared directories; enable samba_enable_home_dirs boolean if sharing homes |
| No disk quotas on shared storage | One user fills the volume; all users affected; no accountability for storage consumption | Enable XFS project quotas or ext4 quotas; set soft and hard limits per user/group; monitor with repquota |
| Stratis pool with single disk and no monitoring | Single disk failure loses entire pool; no warning before space exhaustion | Use Stratis with multiple disks for redundancy; monitor pool space with stratis pool list; alert at 80% usage |
| Not testing NFS failover or backup restore | Assume storage is always available; actual failure reveals untested recovery procedures take hours | Test NFS server failover quarterly; validate backup restore to alternate server; document recovery time |
Related Skills
| Workload | Skill |
|---|---|
| Core admin (LVM, firewall, SSH, systemd, SELinux) | rhel-server-admin |
| Web servers (Nginx, Apache, Caddy) | rhel-web-servers |
| Databases (PostgreSQL, MySQL, Redis) | rhel-databases |
| Docker / Podman containers | rhel-docker-host |
| DNS, DHCP, NTP | rhel-network-infra |
| Prometheus, Grafana, logging | rhel-monitoring |
| NVIDIA GPU, Ollama, CUDA | rhel-ollama-nvidia |