You have a NAS with terabytes of storage, RAID protection, and automatic backups. Your Docker containers keep losing data when you recreate them. The solution is obvious: mount your NAS storage directly into your containers. Docker Compose makes this surprisingly easy once you understand the volume driver options.
This guide covers mounting NFS, SMB/CIFS, and local NAS shares in Docker Compose, along with production tips for permissions, performance, and reliability.
Quick Protocol Comparison
| Protocol | Best For | Strengths | Watch-outs |
|---|---|---|---|
| NFS | Linux hosts, shared access across containers | Simple setup, low overhead, native Docker support | No built-in encryption, UID/GID mapping can be tricky |
| SMB/CIFS | Windows hosts, mixed environments, AD integration | Works everywhere, credential-based auth | Higher overhead, requires cifs-utils on host |
| Local mount + bind | Maximum performance, simple setups | No network latency, works offline | Requires NAS mounted on host first |
Prerequisites
Before starting:
- Your NAS is reachable from your Docker host (same network or routed)
- You have created a share/export on your NAS
- Your Docker host has the required client packages
Installing Required Packages
For NFS on Debian/Ubuntu:
For NFS on RHEL/Rocky/AlmaLinux:
For SMB/CIFS:
Method 1: NFS Volumes (Recommended for Linux)
Docker has built-in support for NFS volumes. No plugins required.
Step 1: Create an NFS Export on Your NAS
On Synology DSM:
- Control Panel → Shared Folder → Create folder (e.g.,
docker-data) - Control Panel → File Services → NFS → Enable NFS
- Edit the shared folder → NFS Permissions → Add rule:
- Hostname/IP: Your Docker host IP or
* - Privilege: Read/Write
- Squash: Map all users to admin
- Hostname/IP: Your Docker host IP or
On TrueNAS:
- Sharing → Unix Shares (NFS) → Add share
- Set path and authorized networks
Step 2: Use NFS in Docker Compose
| |
NFS Mount Options Explained
| Option | Purpose |
|---|---|
addr= | NAS IP address |
nfsvers=4.1 | NFS version (use 4.1 or 4.2 for best compatibility) |
soft | Return errors on timeout instead of hanging (use hard for databases) |
nolock | Disable file locking (faster, but don’t use for databases) |
rw | Read-write mount (default) |
noatime | Don’t update access times (better performance) |
Production-Ready NFS Example
| |
Method 2: SMB/CIFS Volumes
For Windows environments or when your NAS only offers SMB shares.
Basic SMB Volume
| |
SMB with Credentials File (More Secure)
Storing passwords in docker-compose.yml is a bad idea. Use a credentials file instead:
| |
| |
SMB Mount Options
| Option | Purpose |
|---|---|
credentials= | Path to credentials file |
uid= / gid= | Map files to specific user/group ID |
file_mode= / dir_mode= | Set permissions for files/directories |
vers=3.0 | SMB protocol version (try 2.1 or 3.0) |
seal | Enable encryption (SMB 3.0+) |
Method 3: Bind Mounts (NAS Mounted on Host)
Sometimes the simplest approach is mounting the NAS share on your Docker host first, then using bind mounts in Docker Compose.
Step 1: Mount NAS on Host
Add to /etc/fstab:
Mount it:
Step 2: Use Bind Mounts in Docker Compose
| |
Pros:
- Simple to understand
- Works with any file system the host can mount
- Better debugging (you can browse files directly on host)
Cons:
- Extra setup step on host
- If mount fails, containers fail too
- Not portable across hosts
Handling Permissions
Permission issues are the #1 problem when using NAS storage with Docker. Here’s how to solve them.
Understanding the Problem
Docker containers often run as specific users (e.g., postgres runs as UID 999, nginx as UID 101). Your NAS might squash all writes to a different UID. Result: permission denied.
Solution 1: Match Container UID to NAS UID
Find out what UID your container uses:
Configure your NAS to allow that UID, or use NFS squashing to map all access to a UID that the container can use.
Solution 2: Run Container as Specific User
Solution 3: Use an Init Container to Fix Permissions
| |
Solution 4: NAS-Side Squashing
On Synology NFS settings, set “Squash” to “Map all users to admin” - this makes all access appear as the admin user, bypassing permission issues.
Common Pitfalls and Solutions
Pitfall 1: “mount.nfs: access denied by server”
Cause: NFS export doesn’t allow your Docker host IP.
Fix: Check NAS export settings and add your Docker host’s IP.
Pitfall 2: Container can’t write to volume
Cause: UID/GID mismatch.
Fix: Use user: directive in compose file or configure NAS squashing.
Pitfall 3: Volume not mounting on docker-compose up
Cause: NAS not reachable or DNS not resolved yet at boot.
Fix: Add _netdev to fstab for bind mounts, or use a healthcheck/restart policy.
Pitfall 4: “Host is down” after NAS reboot
Cause: Docker cached the mount, NFS handle is stale.
Fix: Restart the containers: docker-compose down && docker-compose up -d
Pitfall 5: Slow write performance
Cause: Synchronous NFS writes.
Fix: Use async export on NAS (with UPS/battery backup) or accept the latency.
Pitfall 6: SMB mounts fail silently
Cause: Wrong SMB version or missing cifs-utils.
Fix: Try adding vers=3.0 or vers=2.1 to mount options. Ensure cifs-utils is installed.
Performance Tips
1. Use Dedicated Storage Network
If your NAS has multiple NICs, dedicate one to Docker storage traffic:
2. Tune NFS Options for Your Workload
For large files (media, backups):
| |
For many small files (web apps):
| |
3. Use Local Volumes for High-IOPS Workloads
For databases that need maximum performance, consider keeping them on local SSD and only using NAS for backups:
| |
Verifying Your Setup
After configuring, verify everything works:
| |
Environment Variables for Flexibility
Make your compose file portable across environments:
Create a .env file:
TL;DR Quick Start
For most setups:
- Install NFS client on Docker host:
apt install nfs-common - Create NFS export on your NAS
- Add NFS volume to your
docker-compose.yml:
- Use the volume in your service:
volumes: - my-data:/app/data - Fix permissions by matching UIDs or using NAS squashing
Your containers now persist data to your NAS. Set up NAS snapshots and backups, and you have a reliable, centralized storage solution for your Docker environment.