System Administration

External Storage Mount Guide for XNAT Docker Deployment

Overview

This guide provides instructions for mounting external SFTP/SSH-based storage to serve as the archive directory for an XNAT Docker deployment. This approach enables cost-effective long-term storage while maintaining local VPS resources for temporary processing operations.

Prerequisites

  • Root/sudo access to your XNAT host server
  • External storage service with SSH/SFTP access enabled
  • XNAT deployed via Docker Compose
  • Required packages: sshfs, cifs-utils

Architecture Decision

Storage Location Strategy

Mount to External Storage:

  • archive/ – Permanent DICOM storage (high volume, infrequent access)

Keep on Local VPS:

  • prearchive/ – Temporary staging during upload
  • build/ – Processing workspace
  • cache/ – Temporary files during operations
  • logs/ – Application logs

Rationale: External network storage introduces latency. Keeping high-frequency read/write operations local maximizes performance while offloading long-term storage to external infrastructure.

Installation Steps

1. Install Required Packages

apt-get update
apt-get install -y sshfs cifs-utils

2. Create Mount Point

mkdir -p /mnt/external-storage

3. Establish Initial Mount

Replace placeholders with your actual credentials:

sudo sshfs [email protected]:/ /mnt/external-storage 
  -o allow_other,default_permissions,uid=0,gid=0

Parameters explained:

  • allow_other – Allows non-root users (including Docker containers) to access the mount
  • default_permissions – Enables kernel permission checking
  • uid=0,gid=0 – Sets ownership to root for consistency

Authentication: You will be prompted for the storage password.

4. Verify Mount

df -h | grep external-storage
ls -la /mnt/external-storage/

Expected output should show the mount point with available storage capacity.

5. Create XNAT Directory Structure

sudo mkdir -p /mnt/external-storage/xnat-archive
sudo mkdir -p /mnt/external-storage/xnat-archive/.catalog

The .catalog directory is required by XNAT for metadata indexing.

6. Set Permissions

sudo chown -R root:root /mnt/external-storage/xnat-archive
sudo chmod -R 755 /mnt/external-storage/xnat-archive

7. Update XNAT Environment Configuration

Edit your .env file:

nano .env

Modify the archive path variable:

# Before:
XNAT_ARCHIVE_PATH=./xnat/data/archive

# After:
XNAT_ARCHIVE_PATH=/mnt/external-storage/xnat-archive

# Keep these local for performance:
XNAT_PREARCHIVE_PATH=./xnat/data/prearchive
XNAT_BUILD_PATH=./xnat/data/build
XNAT_CACHE_PATH=./xnat/data/cache
XNAT_LOGS_PATH=./xnat/data/home/logs

8. Restart XNAT Services

docker-compose down
docker-compose up -d

9. Verification

# Verify Docker container can access the mount
docker exec xnat-web ls -la /data/xnat/archive

# Test write permissions
docker exec xnat-web touch /data/xnat/archive/connectivity-test.txt

# Verify file appears on external storage
ls -la /mnt/external-storage/xnat-archive/connectivity-test.txt

# Monitor XNAT logs for errors
docker logs xnat-web --tail 50

Persistence Configuration

Create Remount Script

Since password-based SSHFS mounts don’t persist across reboots, create an automation script:

sudo nano /usr/local/bin/mount-external-storage.sh

Add the following content (replace with your credentials):

#!/bin/bash
if ! mountpoint -q /mnt/external-storage; then
    echo "your-password-here" | sshfs [email protected]:/ /mnt/external-storage 
      -o allow_other,default_permissions,uid=0,gid=0,password_stdin
    echo "External storage mounted"
else
    echo "External storage already mounted"
fi

Make executable:

sudo chmod +x /usr/local/bin/mount-external-storage.sh

Add to System Startup

sudo nano /etc/rc.local

Add before the exit 0 line:

/usr/local/bin/mount-external-storage.sh

If /etc/rc.local doesn’t exist, create it:

#!/bin/bash
/usr/local/bin/mount-external-storage.sh
exit 0

Make it executable:

sudo chmod +x /etc/rc.local

Post-Upload Verification

After uploading DICOM data to XNAT:

# List project directories on external storage
ls -lah /mnt/external-storage/xnat-archive/

# View DICOM files for a specific project
find /mnt/external-storage/xnat-archive/PROJECT_NAME/ -name "*.dcm" | head -10

# Check storage utilization
du -sh /mnt/external-storage/xnat-archive/PROJECT_NAME/

# Verify growth is on external storage, not local VPS
df -h | grep external-storage

Troubleshooting

Mount Not Accessible from Docker Container

Symptoms: XNAT displays “Archive files do not exist” error.

Solution:

docker-compose restart xnat-web
docker exec xnat-web ls -la /data/xnat/archive

Permission Denied Errors

Cause: Incorrect mount options or ownership.

Solution:

sudo umount /mnt/external-storage
sudo sshfs [email protected]:/ /mnt/external-storage 
  -o allow_other,default_permissions,uid=0,gid=0
sudo chown -R root:root /mnt/external-storage/xnat-archive

Mount Disappears After Reboot

Cause: SSHFS mounts with password authentication don’t auto-mount.

Solution: Verify remount script is properly configured in /etc/rc.local or set up SSH key authentication (see Advanced Configuration).

Slow Upload Performance

Cause: Network latency between VPS and external storage.

Mitigation:

  • Ensure prearchive, build, and cache remain on local storage
  • Check network bandwidth between host and storage provider
  • Consider SSH compression: add -o compression=yes to mount options

Advanced Configuration

For automatic mounting without password exposure:

# Generate SSH key pair
sudo ssh-keygen -t rsa -b 4096 -f /root/.ssh/external_storage -N ""

# Display public key
sudo cat /root/.ssh/external_storage.pub

Add the public key to your external storage service’s authorized keys (method varies by provider).

Update mount command:

sudo sshfs [email protected]:/ /mnt/external-storage 
  -o allow_other,default_permissions,uid=0,gid=0,IdentityFile=/root/.ssh/external_storage,reconnect,ServerAliveInterval=15,ServerAliveCountMax=3

Add to /etc/fstab for automatic mounting:

[email protected]:/ /mnt/external-storage fuse.sshfs noauto,x-systemd.automount,_netdev,allow_other,default_permissions,uid=0,gid=0,IdentityFile=/root/.ssh/external_storage,reconnect,ServerAliveInterval=15,ServerAliveCountMax=3 0 0

Test fstab entry:

sudo umount /mnt/external-storage
sudo mount /mnt/external-storage

Security Considerations

  1. Credential Storage: If using password authentication, ensure the remount script has restricted permissions (chmod 700)
  2. Network Security: Use SSH key authentication when possible
  3. Backup Strategy: Verify external storage provider offers snapshots or backup mechanisms
  4. Access Control: Limit SSH access to the storage service to your XNAT host IP if possible

Maintenance

Regular Health Checks

# Verify mount status
df -h | grep external-storage

# Check for connection issues
dmesg | grep -i fuse | tail -20

# Monitor storage capacity
du -sh /mnt/external-storage/xnat-archive/

Data Migration (Optional)

To migrate existing local archive data to external storage:

sudo rsync -av ./xnat/data/archive/ /mnt/external-storage/xnat-archive/

Verify integrity before removing local copies:

# Compare file counts
find ./xnat/data/archive/ -type f | wc -l
find /mnt/external-storage/xnat-archive/ -type f | wc -l

Performance Expectations

  • Initial Upload: Speed limited by network bandwidth between VPS and storage provider
  • Archive Access: Minimal impact as archived data is accessed infrequently
  • Processing Operations: No degradation since prearchive/build/cache remain local

Conclusion

This configuration provides a scalable storage solution for XNAT deployments, separating high-performance temporary operations from cost-effective long-term archive storage. Regular monitoring of mount health and storage capacity ensures reliable operation.