Remote docker context on a QNAP QTS container host
QTS supports Docker with the installation of the app ‘Container Station’.
Container Station offers a GUI way to control containers, but interfacing with Docker is still possible via CLI.
In a most advantageous way, you can interface with Docker on QTS via remote context, just as you would expect from any Docker host.
However, only with some light modification.
Typical steps
You already have SSH connectivity to the remote QTS host.
(In this case I have an administrator user on the NAS with my name, and my pub key is defined in QTS Settings.)
- Install Container Station via the App Center. Confirm it is operating via the web UI.
- Ideally also confirm Docker is operating via an admin shell.
seb@nas1 ~ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
seb@nas1 ~ $
- Add the remote server as a context in
docker context.
docker context create nas1 --description="nas1" --docker="host=ssh://nas1"
Challenge
Usually at this point you can access the Docker daemon from your own local shell successfully.
seb@mac ~ $ docker context use nas1
nas1
Current context is now "nas1"
You can now normally run docker commands like docker ps.
However, out of the box on QTS, running anything will produce an error message instead:
error during connect: Get “http://docker.example.com/v1.51/containers/json": command [ssh -o ConnectTimeout=30 -T – nas1 docker system dial-stdio] has exited with exit status 127, make sure the URL is valid, and Docker 18.09 or later is installed on the remote host: stderr=bash: docker: command not found
This occurs because the SSH session is in an environment whose path doesn’t have the docker binary.
This can be seen when you repeat the SSH call with echo $PATH instead.
seb@mac ~ $ ssh -o ConnectTimeout=30 -T -- nas1 docker system dial-stdio -- echo '$PATH'
/usr/bin:/bin:/usr/sbin:/sbin
The docker binary is installed by QTS App Center to a user data location instead:
seb@nas1 ~ $ which -a docker
/share/CACHEDEV1_DATA/.qpkg/container-station/bin/docker
There is no reference to the docker binary within the 4 paths listed in the above PATH.
Solution
Best workaround to suggest is to create a symlink :)
ln -sf /share/CACHEDEV1_DATA/.qpkg/container-station/bin/docker /usr/bin/docker
Then the docker binary is available, and docker ps or any other command will work.
Assumedly as long as your SSH user is an administrator.
Justification
Methodology: Symlink to an existing location on PATH.
In pro:
- Simple, relies on one system: POSIX filesystem links.
- Persists after reboot, since it is part of the filesystem.
In contra:
- May not be vendor update-proof, if system updates modify or change how /usr/bin is populated.*
Another method may be to edit the shell environment in some way to include the .qpkg/container-station/bin directory in its PATH.
However, this seemed to have greater complexity for me in terms of investigation time.
I would rather edit a filesystem link in a POSIX directory, than edit vendor-managed bash shell files.
Though there are others who perhaps would much prefer the other way around.
I note there is a clear difference in the PATH already between making an ssh call directly with a command, and opening an executable shell.
PATH when executing docker system dial-stdio -- echo '$PATH', as docker context would:
# 4 paths
/usr/bin:/bin:/usr/sbin:/sbin
PATH when executing echo $PATH in an interactive shell:
# many
seb@nas1 ~ $ echo $PATH
/opt/bin:/opt/sbin:/share/CACHEDEV1_DATA/.qpkg/container-station/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/bin/X11:/usr/local/sbin:/usr/local/bin:/opt/QPython312/bin:/opt/Nano/bin:/opt/QGit/bin:/opt/bin/go/bin
seb@nas1 ~ $ echo $SHELL
/bin/bash
seb@nas1 ~ $
Clearly this shows some other processes are involved in establishing the bash environment, between vendor (QNAP QTS), bash versus sh, my own bash profile in my home directory if any, Entware, … there could be dragons … enough said for now.
Use case
Remote Docker context opens lots of doors for interacting with the QNAP host as you would any other container host.
Apply compose files stored in an IAC repo, that is not located directly on the remote host, instead your working client.
Exec shell into containers for troubleshooting, or quickly grep log output.
Use cp to copy data to/from containers on the local host, where the transport is SSH.
Easier management among many other servers, using a UI tool like Termix or XPipe.
Not have to mess too much with a QNAP shell, missing utils or binaries, or other environmental nuances.
The docker version details for this distribution of QTS (an x86_64 host, QNAP TS-873).
seb@nas1 ~ $ docker info
Client:
Version: 27.1.2-qnap7
Context: default
Debug Mode: false
Plugins:
buildx: Docker Buildx (Docker Inc.)
Version: v0.21.2-qnap1
Path: /usr/local/lib/docker/cli-plugins/docker-buildx
compose: Docker Compose (Docker Inc.)
Version: v2.29.1-qnap2
Path: /usr/local/lib/docker/cli-plugins/docker-compose
Server:
Containers: 1
Running: 1
Paused: 0
Stopped: 0
Images: 8
Server Version: 27.1.2-qnap7
Storage Driver: overlay2
Backing Filesystem: extfs
Supports d_type: true
Using metacopy: true
Native Overlay Diff: false
userxattr: false
Logging Driver: json-file
Cgroup Driver: cgroupfs
Cgroup Version: 1
Plugins:
Volume: local
Network: bridge host ipvlan macvlan null overlay qnet
Log: awslogs fluentd gcplogs gelf journald json-file local splunk syslog
Swarm: inactive
Runtimes: runc io.containerd.runc.v2 kata-runtime nvidia-runtime
Default Runtime: runc
Init Binary: docker-init
containerd version: 8fc6bcff51318944179630522a095cc9dbf9f353
runc version: v1.1.13-0-g58aa920
init version: de40ad0
Security Options:
apparmor
seccomp
Profile: builtin
Kernel Version: 5.10.60-qnap
Operating System: QTS 5.2.7 (20251024)
OSType: linux
Architecture: x86_64
CPUs: 4
Total Memory: 15.59GiB
Name: nas1
ID: [xxxxxxx]
Docker Root Dir: /share/CACHEDEV1_DATA/Container/container-station-data/lib/docker
Debug Mode: true
File Descriptors: 30
Goroutines: 50
System Time: 2026-01-09T18:03:00.811063986+11:00
EventsListeners: 1
Experimental: false
Insecure Registries:
127.0.0.0/8
Live Restore Enabled: false
Product License: Community Engine
Default Address Pools:
Base: 172.29.0.0/16, Size: 22
Notes
(*) I also added this to an autorun script using OneCDOnly’s autorun tool for QTS create-autorun. Since I wanted some assurance it would stick through future updates long after I had forgotten about this.
Comments
Thanks for reading!
Commenting is provided through Giscus. Giscus is an open source comment system with no tracking or analytics, aside from OAuth via GitHub. To comment, you just need to sign in to GitHub and authorise Giscus once.