A docker CLI plugin which pushes Docker / OCI images directly from a local
containerd image store to a remote containerd runtime over SSH. No intermediate
registry, and no remote daemon required.
Layers transfer over a gRPC connection tunneled through the SSH session and unpack
on the remote in parallel where the snapshotter advertises the rebase capability
(containerd 2.2+), or serially otherwise.
Requires Go 1.26+ and a working docker CLI. Build and drop the binary into
Docker's plugin directory with:
make install-docker-pluginThis installs the plugin to ~/.docker/cli-plugins/docker-scp. Verify with:
docker scp --helpTo remove:
make uninstall-docker-plugindocker scp [OPTIONS] IMAGE [USER@]HOST[:PORT]
Options:
--local-socket string Local containerd socket path (default "/run/containerd/containerd.sock")
--platform strings Push specific platforms of a multi-platform image
(comma-separated, e.g. linux/amd64,linux/arm64)
--remote-socket string Remote containerd socket path (default "/run/containerd/containerd.sock")
Push an image to a remote host:
docker scp ubuntu:24.04 user@remoteSelect specific platforms from a multi-platform image:
docker scp --platform linux/amd64,linux/arm64 ubuntu:24.04 user@remoteThe plugin reads from the local containerd image store and writes to the remote one over SSH, so both the local and remote machines need:
-
Docker's containerd image store enabled.
-
Group access to
/run/containerd/containerd.sock. The user runningdocker scplocally and the SSH user on the remote both need to be in the socket's group.
If a containerd socket lives somewhere else (e.g. a rootless containerd under
$XDG_RUNTIME_DIR), point the plugin at it with --local-socket /
--remote-socket.
When the SSH server does not allow unix socket forwarding (e.g. Tailscale
SSH), the remote also needs nc from netcat-openbsd 1.130 or newer (Ubuntu
18.04+, Debian stretch+).
Run these steps on both machines. Create a containerd group and add
your user to it:
sudo groupadd containerd
sudo usermod -aG containerd $USERTell containerd to chown the socket to that group by editing
/etc/containerd/config.toml:
[grpc]
gid = 999 # replace with: getent group containerd | cut -d: -f3Restart containerd and start a new login session so the group membership takes effect:
sudo systemctl restart containerd
# log out and back in (or `newgrp containerd` in the current shell)- psviderski/unregistry: Same goal, but requires temporarily running a registry on the remote.
podman image scp: Built into Podman. Transfers the full image via save/load over SSH (no layer dedup).