Skip to content

How I Mount Synology NFS Shares on Ubuntu for Docker

Most of the Docker containers in my homelab keep their configuration and application data locally under /opt/docker on the server where they run.

Larger files are different.

Things such as media, photos and Docker backups are better suited to my Synology NAS. Rather than trying to manage that storage from inside individual Docker containers, I mount the Synology storage on the Ubuntu host first using NFS and then pass the mounted directories into Docker containers as normal bind mounts.

My setup therefore looks roughly like this:

Synology NAS (sjb2)
        |
        | NFS
        |
Ubuntu Docker server
        |
     /mnt/sjb2
        |
        | Docker bind mount
        |
Docker container

This has turned out to be a simple arrangement and, importantly, keeps Docker separate from the network storage configuration.

The Synology NAS and Drives I Use

Affiliate disclosure: This section contains an Amazon affiliate link. If you make a purchase through it, I may earn a commission at no additional cost to you.

My main storage NAS, sjb2, is a Synology DS223. It is a two-bay NAS sold without hard drives. Mine currently contains two 4 TB WD Red Plus drives, model WD40EFPX.

View the Synology DS223 on Amazon.co.uk (paid link)

The link above is for the exact NAS model I use. Amazon prices and seller availability change, so I would compare the current buying options with other reputable NAS retailers before purchasing.

In my setup, the DS223 provides the storage rather than running the Docker containers itself. My containers run across three Ubuntu servers and access the required files on the NAS over NFS.

Why I Use NFS

I have two Synology NAS devices in my homelab, with sjb2 being my main storage NAS.

My Docker services, however, are spread across three Ubuntu servers. I describe that setup in more detail in How I Manage Docker Across Three Servers.

For normal Docker application data I use directories such as:

/opt/docker/jellyfin
/opt/docker/paperless
/opt/docker/caddy
/opt/docker/ntfy

I don't want large media libraries or backup archives stored there.

Instead, the NAS provides central storage and the Ubuntu servers access it across the local network.

For Linux-to-Linux storage, NFS is a natural fit. Synology DSM can export shared folders over NFS and Ubuntu has built-in support once the NFS client utilities are installed.

The arrangement also means Docker itself doesn't need to know that the files are stored on another machine.

As far as a container is concerned, something such as:

/mnt/sjb2/media

is simply another directory on the Docker host.

Host Mounts Rather Than Docker NFS Volumes

Docker can create volumes that connect directly to an NFS server.

I prefer not to do that.

Instead, I separate the two jobs:

Ubuntu handles NFS
Docker handles containers

Ubuntu mounts the NAS storage under /mnt, and Docker Compose uses that mounted directory as a bind mount.

For example, conceptually I can have:

volumes:
  - /mnt/sjb2/media:/media:ro

Docker does not establish the NFS connection. It simply sees /mnt/sjb2/media.

I find this easier to understand and troubleshoot.

If I cannot see the files from Ubuntu, I know I have an NFS or network problem.

If Ubuntu can see the files but the container cannot, I know I should look at the Docker configuration or permissions.

That separation makes troubleshooting considerably simpler.

Enabling NFS on the Synology

NFS first needs to be enabled on the Synology NAS.

In DSM this is under:

Control Panel
    -> File Services
    -> NFS

I enable the NFS service there.

The next step is to give the Docker server permission to access the required shared folder.

In DSM this is done from:

Control Panel
    -> Shared Folder

I select the required shared folder, choose Edit, and then open the NFS Permissions section.

An NFS rule can then be created for the Ubuntu server that needs access.

I prefer to grant access only to machines that actually need the share rather than allowing unrestricted access from everything on the network.

For example, the rule can contain the IP address of the Docker server.

Whether the share should be Read/Write or Read Only depends entirely on what the container needs to do.

A media library being used by Jellyfin may only need read access, whereas a backup destination obviously needs write access.

One useful piece of information appears in the NFS permissions window in DSM: the mount path.

It will look something like:

/volumeX/share-name

The exact value depends on the Synology volume and shared folder.

I use that path when mounting the share from Ubuntu.

Installing the NFS Client on Ubuntu

On each Ubuntu server that needs to access the NAS I install the NFS client tools:

sudo apt update
sudo apt install nfs-common

The nfs-common package provides the utilities Ubuntu needs to mount an NFS filesystem.

I can check that Ubuntu can see the NAS with:

ping sjb2

Because my NAS is available on my local network as sjb2, I can normally use the hostname rather than having to remember its IP address.

Using the NAS IP address works just as well.

Creating the Mount Point

I keep network-mounted storage under /mnt.

For my main Synology this is:

/mnt/sjb2

If the directory does not already exist, it can be created with:

sudo mkdir -p /mnt/sjb2

It is worth remembering that a mount point should normally be empty before a filesystem is mounted onto it.

Anything already stored inside /mnt/sjb2 would be hidden while the NFS filesystem is mounted there.

Testing the NFS Mount Manually

Before making the mount permanent, I prefer to test it manually.

The general command is:

sudo mount -t nfs sjb2:/volumeX/share-name /mnt/sjb2

Here:

sjb2

is my Synology NAS.

/volumeX/share-name

is the NFS mount path shown by Synology DSM.

And:

/mnt/sjb2

is where I want the storage to appear on the Ubuntu server.

I can then check the mount with:

df -h

or:

mount | grep sjb2

I can also simply look at the directory:

ls -la /mnt/sjb2

If the mount has worked, I should now see the contents of the Synology share.

At this point Docker isn't involved at all.

That is useful because it proves the NAS, NFS permissions, network connection and Ubuntu mount are working before I introduce another layer.

Making the Mount Persistent

A manual mount disappears when the server reboots.

For storage that Docker depends on, I need Ubuntu to recreate the mount automatically.

One way of doing this is through /etc/fstab.

I edit it with:

sudo nano /etc/fstab

The basic NFS entry looks like this:

sjb2:/volumeX/share-name /mnt/sjb2 nfs defaults 0 0

Again, /volumeX/share-name needs to be replaced with the actual NFS path displayed by Synology DSM.

After editing /etc/fstab, I reload the systemd configuration:

sudo systemctl daemon-reload

I can then test the entry without rebooting.

First, if I mounted the share manually earlier:

sudo umount /mnt/sjb2

Then:

sudo mount -a

If there are no errors, I check the directory again:

ls -la /mnt/sjb2

This is a useful test to perform before rebooting the server. A typo in /etc/fstab is much easier to fix while I already have a working SSH session.

Avoiding Problems if the NAS Is Offline

There is one extra consideration with network storage.

A local disk is normally available as soon as Ubuntu boots. A NAS might not be.

For example, after a power failure my Docker server and Synology could both be starting at roughly the same time.

I don't want the Ubuntu server to fail or spend a long time waiting simply because sjb2 has not finished starting.

For that reason, network-aware mount options can be useful.

An example is:

sjb2:/volumeX/share-name /mnt/sjb2 nfs defaults,_netdev,nofail,x-systemd.automount 0 0

The important options here are:

_netdev

which tells the system that the filesystem depends on the network,

nofail

which allows the server to continue booting even if the NAS cannot be mounted,

and:

x-systemd.automount

which allows systemd to mount the filesystem when it is accessed.

I would always test mount behaviour carefully before depending on it for an important service, particularly if Docker containers start automatically after a reboot.

There is an important Docker-related reason for this.

If an NFS mount is supposed to be available at:

/mnt/sjb2

but isn't actually mounted, that directory still exists on the Ubuntu server.

Docker could therefore start a container and see an empty local directory instead of the expected NAS storage.

For something such as a media server this may simply mean the library appears empty.

For an application that writes data, it could be worse: data could accidentally be written to the Docker server's local filesystem instead of the NAS.

So I always want to know that the mount is really present before relying on it.

A quick check is:

mountpoint /mnt/sjb2

A successful result should report:

/mnt/sjb2 is a mountpoint

Using the NFS Storage in Docker Compose

Once Ubuntu has mounted the NFS share, there is nothing particularly special about using it from Docker.

It is just a bind mount.

For example:

services:
  example:
    image: example/image:latest
    volumes:
      - /mnt/sjb2/media:/media:ro

The left-hand side:

/mnt/sjb2/media

is the directory on the Ubuntu host.

The right-hand side:

/media

is where the files appear inside the container.

The :ro makes the mount read-only inside the container.

I use read-only mounts where an application only needs to read files. There is little reason to give a container permission to modify a library when it doesn't need to.

For applications that need to write to the NAS, the :ro would obviously be removed.

How This Fits With My /opt/docker Layout

This is one of the reasons I like separating network storage from Docker configuration.

My Docker application files remain under:

/opt/docker

while externally stored data appears under:

/mnt

Conceptually, that gives me:

/opt/docker
├── caddy
├── jellyfin
├── paperless
├── ntfy
└── ...

/mnt
└── sjb2
    ├── media
    ├── docker-backups
    └── ...

The exact directories on the NAS depend on the services using them, but the distinction is clear:

/opt/docker = Docker application/configuration data
/mnt/sjb2  = storage provided by my Synology NAS

I describe the /opt/docker side of this in more detail in How I Organise My Docker Folders.

Where I Use NAS Storage

Central NAS storage is useful in several parts of my homelab.

One obvious example is Jellyfin.

The Jellyfin container runs on one of my Ubuntu Docker servers, but the media itself does not need to live on that server's internal drive. The media storage can be mounted from the Synology and then made available to Jellyfin.

That keeps the Docker server concerned with running Jellyfin rather than also becoming my main media storage device.

My Jellyfin guide covers how I run Jellyfin itself.

I also use NAS storage as the destination for my Docker backups.

Each of my Docker servers creates backups that ultimately live on the Synology rather than only on the machine being backed up.

That is important because a backup stored only on the server it is protecting is not much help if that server's disk fails.

I describe that system in How I Backup My Homelab and Automated Docker Compose Backups.

NFS Permissions Can Be Confusing

One of the areas that can cause problems with NFS is permissions.

NFS does not work like logging into an SMB share from Windows with a username and password.

With the usual NFS security model, numeric Linux user IDs and group IDs are important.

That means a directory can appear correctly mounted but a Docker container may still report errors such as:

Permission denied

or it may be able to read files but not create or modify them.

If that happens, I check the permissions from the Ubuntu host before blaming Docker.

For example:

ls -ln /mnt/sjb2

The -n option is useful because it displays numeric UID and GID values.

I can also check which user a process or container is running as and compare that with the ownership of the files on the NAS.

Synology's Squash setting in the NFS permissions also affects how users are mapped when they access the share.

I don't think there is one NFS permission setting that should blindly be copied for every Docker container. The correct choice depends on whether the application needs read-only access, read/write access, and which user the container runs as.

If something cannot write to an NFS share, changing everything to 777 is not my preferred solution.

It is better to work out which user actually needs access and why.

Useful Troubleshooting Commands

Most NFS problems can be narrowed down quite quickly from the Ubuntu host.

Check whether the NAS responds:

ping sjb2

Check whether the mount exists:

mountpoint /mnt/sjb2

Check mounted NFS filesystems:

mount | grep nfs

Check disk usage and mounted filesystems:

df -h

Look at the files:

ls -la /mnt/sjb2

Check numeric ownership:

ls -ln /mnt/sjb2

Test all entries in /etc/fstab:

sudo mount -a

Look for recent mount-related messages:

journalctl -b | grep -i nfs

If Ubuntu cannot mount the share manually, there is little point troubleshooting the Docker Compose file yet.

I first get this working:

Synology -> Ubuntu

and only then troubleshoot:

Ubuntu -> Docker

Checking the Mount After a Reboot

After configuring a new NFS mount, I also like to test it after an actual reboot.

Once the Docker server has restarted:

mountpoint /mnt/sjb2

and:

df -h

confirm that the NAS storage is really mounted.

Then I check whichever container depends on it.

For example:

docker ps

followed, if necessary, by:

docker logs container-name

This is particularly worthwhile in a homelab where the NAS and Docker hosts may all be connected to the same UPS and may therefore shut down and restart together after an extended power failure.

A mount that works perfectly when configured manually is not necessarily the same thing as a mount that reliably returns after every reboot.

Why This Arrangement Works Well for Me

The main benefit of this setup is that each part has a clear responsibility.

My Synology handles storage.

Ubuntu handles mounting that storage.

Docker handles the applications.

The containers don't need NFS credentials or special Docker volume definitions, and my Compose files simply refer to normal paths on the host.

It also fits neatly with the way I already organise the rest of my homelab:

Docker configuration     -> /opt/docker
NAS storage              -> /mnt/sjb2
Docker backups           -> Synology
Large media files        -> Synology
Containers               -> spread across three Ubuntu servers

It is not a particularly complicated architecture, but that is one of the reasons I like it.

When something goes wrong I can test each layer independently, and I don't need Docker to manage the connection to my NAS as well as the containers that depend on it.

For a small multi-server homelab, that simplicity has been useful.