Nextcloud Sync Client for Docker

5 minute read

I’ve found the ‘native’ macOS Nextcloud client to be a bit flakey. It’s all Qt and C++ and times out all the time.

I’m trying to replace Dropbox on my Mac, as they get up to all sorts of scummy behaviour, but it’s a little harder than expected.

I mentioned in an earlier post that I sync my photos to my NAS, then sync to Nextcloud via their command line client, running in a Docker container. I run all my containers on my Synology NAS using their Docker package:

Docker containers Docker containers Docker images

In the last screenshot there you can see my image: stoutyhk/nextcloud-client. It’s based on the image from juanitomint, with some small tweaks to add functionality to exclude certain folders and to set folders for selective sync. I also slightly tweaked how the arguments to the client are constructed and exposed the media and config folders.


The config folder can contain files named:

  • exclude - this contains files/folders/patterns that you don’t want to sync
  • unsyncedfolders - contains the list of un-synced remote folders (selective sync)

For example, I want to exclude syncing the Synology-generated metadata @eaDir directories to my server:

So I have a config directory containing an exclude file:

config folder screenshot


exclude file screenshot

Additionally, on my server there are folders that I do not want to sync down to my NAS. So those folders go in the unsyncedfolders file:

unsyncedfolders file screenshot

So you download the image, launch and set the volumes and environment variables, and start it up:

nc-docker-settings screenshot

From the command line this would be something like:

docker run -it --rm \
  -v some_named_volume:/media/nextcloud \
  -v some_config_volume:/config \
  -e NC_USER=$username -e NC_PASS=$password \
  -e NC_URL=$server_url\

The Dockerfile CMD is, a script that parses the arguments to construct the nextcloudcmd command. For my setup it’s this:

"/bin/su -s /bin/ash $USER -c 'nextcloudcmd --exclude /config/exclude \
--unsyncedfolders /config/unsyncedfolders --max-sync-retries 6 \
--non-interactive -u $NC_USER -p $NC_PASS /media/nextcloud $NC_URL'"

This simply runs in a while true loop, sleeping for 500s after it syncs and then checking if it needs to sync again after 500s.

It’s very stable, hasn’t crashed or timed out yet. I highly recommend this method vs the native macOS client.

Going full No Dropbox would mean syncing further folders and mounting them on my Mac. That’s up next.

BTW, building and tweaking this is very simple. The Dockerfile describes what to do:

Build image based on Alpine (a tiny, 2.7MB, version of Linux):

FROM alpine:latest

Set some ARGS and ENVS:

ARG USER=ncsync

    NC_USER=username \
    NC_PASS=password \
    NC_INTERVAL=500 \
    NC_URL="" \
    NC_TRUST_CERT=false \
    NC_SILENT=false \
    NC_EXIT=false   \
    NC_HIDDEN=false \

Create user/group:

RUN adduser -G $GROUP -D -u $USER_UID $USER

Update repositories and install nextcloud-client:

RUN apk update && apk add nextcloud-client && rm -rf /etc/apk/cache

Add run script and expose volumes:

ADD /usr/bin/
VOLUME [ "/media/nextcloud" ]
VOLUME [ "/config" ]

Finally, execute the command:

CMD /usr/bin/

The build command to build the image is:

docker build \
        --build-arg VCS_REF=$(git rev-parse --short HEAD) \
        --build-arg BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \
        -t stoutyhk/nextcloud-client . | tee build.log
docker images stoutyhk/nextcloud-client

You can see my current image is the one from DockerHub:

docker-ps screenshot

The build output:

root@dusky2: /volume1/homes/james/src/nextcloud-client-docker on master[!?]
$ ./
Sending build context to Docker daemon  28.67kB
Step 1/19 : FROM alpine:latest
 ---> a187dde48cd2
Step 2/19 : LABEL maintainer="stoutyhk" version="0.1" description="nextcloud sync client"
 ---> Running in d7bcd965c0d4
Removing intermediate container d7bcd965c0d4
 ---> b6a1c94582c9
Step 3/19 : LABEL based-on=""
 ---> Running in fba05915652c
Removing intermediate container fba05915652c
 ---> 92e4f3b6aba1
Step 4/19 : LABEL repo=""
 ---> Running in 63258ff88545
Removing intermediate container 63258ff88545
 ---> 07233dc234c6
Step 5/19 : ARG VCS_REF
 ---> Running in f03e352100f4
Removing intermediate container f03e352100f4
 ---> 172784412e92
Step 6/19 : ARG BUILD_DATE
 ---> Running in 80600d7575e4
Removing intermediate container 80600d7575e4
 ---> 77bc2f5d481a
Step 7/19 : LABEL vcs-ref=$VCS_REF
 ---> Running in c58cdd8b58f6
Removing intermediate container c58cdd8b58f6
 ---> 2e598c72729a
Step 8/19 : LABEL build-date=$BUILD_DATE
 ---> Running in d51c564b1354
Removing intermediate container d51c564b1354
 ---> 2cf77c7ccd0f
Step 9/19 : ARG USER=ncsync
 ---> Running in 10c452b72d23
Removing intermediate container 10c452b72d23
 ---> 62d6402dfe64
Step 10/19 : ARG GROUP=users
 ---> Running in 2c7c84cca017
Removing intermediate container 2c7c84cca017
 ---> 32af7f7e31a6
Step 11/19 : ARG USER_UID=1000
 ---> Running in a76b14779d6e
Removing intermediate container a76b14779d6e
 ---> d16b1dc7fda7
Step 12/19 : ARG USER_GID=100
 ---> Running in 16a3579c0537
Removing intermediate container 16a3579c0537
 ---> b06b174ac1cb
Step 13/19 : ENV USER=$USER     GROUP=$GROUP     USER_UID=$USER_UID     USER_GID=$USER_GID     NC_USER=username     NC_PASS=password     NC_INTERVAL=500     NC_URL=""     NC_TRUST_CERT=false     NC_SILENT=false     NC_EXIT=false       NC_HIDDEN=false     NC_MAX_SYNC_RETRIES=3
 ---> Running in 85c579929ce1
Removing intermediate container 85c579929ce1
 ---> 82c168659c42
Step 14/19 : RUN adduser -G $GROUP -D -u $USER_UID $USER
 ---> Running in 84755af084c9
Removing intermediate container 84755af084c9
 ---> 9888ccca2dcc
Step 15/19 : RUN apk update && apk add nextcloud-client && rm -rf /etc/apk/cache
 ---> Running in 702eef96b765
v3.11.6-32-g9ddc349524 []
v3.11.6-28-g4b76c8208f []
OK: 11273 distinct packages available
(1/95) Installing dbus-libs (1.12.16-r2)
(2/95) Installing libgcc (9.2.0-r4)
(3/95) Installing libffi (3.2.1-r6)
(95/95) Installing nextcloud-client (2.6.1-r0)
^@Executing busybox-1.31.1-r9.trigger
OK: 300 MiB in 109 packages
Removing intermediate container 702eef96b765
 ---> 3147165b6612
Step 16/19 : ADD /usr/bin/
 ---> 11a9c630e797
Step 17/19 : VOLUME [ "/media/nextcloud" ]
 ---> Running in 69454b15f6b6
Removing intermediate container 69454b15f6b6
 ---> af98dc485510
Step 18/19 : VOLUME [ "/config" ]
 ---> Running in 94ac262e01a9
Removing intermediate container 94ac262e01a9
 ---> b964f6b7c97c
Step 19/19 : CMD /usr/bin/
 ---> Running in 942c9ddf5ddd
Removing intermediate container 942c9ddf5ddd
 ---> aa064981bb5b
Successfully built aa064981bb5b
Successfully tagged stoutyhk/nextcloud-client:latest

real	5m50.592s
user	0m0.096s
sys	0m0.047s
REPOSITORY                  TAG                 IMAGE ID            CREATED             SIZE
stoutyhk/nextcloud-client   latest              aa064981bb5b        1 second ago        311MB
stoutyhk/nextcloud-client   <none>              6ca8291dece4        3 weeks ago         311MB

You can see the latest tag is now pointing to image aa064981bb5b. To update, you export the settings, stop the container, docker rm stoutyhk-nextcloud-client, then import the settings and launch the new image:

new-image screenshot

Spot of housekeeping:

root@dusky2: /usr/local/bin
$ for image in $(docker images -f "dangling=true" | grep stouty | grep -vE 'IMAGE' | awk '{ print $3 }'); do
    docker rmi "$image";
Untagged: stoutyhk/nextcloud-client@sha256:024ba23cc5e182aedb3d30df1bc4e31127fe21f94b35aed55804989b8e154a98
Deleted: sha256:6ca8291dece42c3dda0cf2b6601e0ff1180cf854909b5f3474710ca84babaf04
Deleted: sha256:be8054c4d017455ca6b4afd7e00fb7da03e58beae4a3705575aabcff8af23fb9
Deleted: sha256:c9eae4086cf6c94c60590bb909fbffa90f166371d9c13a53a4a988d48747cdcc
Deleted: sha256:688a57e452c154219a34c88e18ec695ef3a15ce4a1d4f73875d56ff40a8a6de6

I have a script that runs weekly to automate pulling of new images:

I then need to do the export, rm, import manually as I haven’t figured out how to automate that with Synology Docker package yet.

Dive is a nice tool for examining Docker images and layers.

You can see what each command in the Dockerfile does to the image. It also gives you an image efficiency estimate:

Image Efficiency

The lower left pane shows basic layer info and an experimental metric that will guess how much wasted space your image contains. This might be from duplicating files across layers, moving files across layers, or not fully removing files. Both a percentage “score” and total wasted file space is provided.

Mine is fairly good:

Total Image size: 311MB
Potential wasted space: 179kB
Image efficiency score: 99 %

Here’s a look at dive: