How to Install docker-pussh on Debian: Push Docker Images Over SSH

docker-pussh is the client half of the Unregistry project: it uploads a Docker image straight to a remote Docker daemon over SSH, with no registry standing in the middle. You build locally, run one command, and the image lands on the far side ready to docker run. Only the layers the target is missing travel the wire, so repeat deployments stay quick even on a modest connection.

It orchestrates the whole exchange itself: it opens an SSH connection, starts a short-lived Unregistry helper container on the remote host, pushes the missing layers into that host’s containerd store, and tears the helper down again. Nothing is left running afterwards, and the remote host needs no permanent registry.

The tool doubles as a Docker CLI plugin, so once it is wired in you can call it as docker pussh (yes, the double “s” is intentional). Upstream ships it as a script you copy into a plugin directory by hand, which means you own every future update. The unofficial deb.griffo.io repository packages it as a standard Debian .deb served over APT, so installing and updating it uses the tooling you already run for the rest of your system.

Install the Latest docker-pussh on Debian: The Short Version

If you only came for the commands, this adds the repository, installs the latest docker-pussh .deb package, and wires it up as a docker subcommand:

sudo install -d -m 0755 /etc/apt/keyrings
curl -fsSL https://deb.griffo.io/EA0F721D231FDD3A0A17B9AC7808B4DD62C41256.asc | sudo gpg --dearmor --yes -o /etc/apt/keyrings/deb.griffo.io.gpg
echo "deb [signed-by=/etc/apt/keyrings/deb.griffo.io.gpg] https://deb.griffo.io/apt $(lsb_release -sc 2>/dev/null) main" | sudo tee /etc/apt/sources.list.d/deb.griffo.io.list > /dev/null
sudo apt update
sudo apt install docker-pussh

# Optional: expose it as `docker pussh`
mkdir -p ~/.docker/cli-plugins
ln -sf /usr/local/bin/docker-pussh ~/.docker/cli-plugins/docker-pussh

The rest of this guide explains what each command does, why that last step is needed, how to verify the install, and what to check when something goes wrong.

What Makes docker-pussh Special?

  • 🚫 No registry required - push images straight to a host over SSH, skipping Docker Hub, GHCR and self-hosted registries.
  • 📦 Layer-aware transfers - only the layers the remote is missing are sent, so pushes are quick and bandwidth-light.
  • 🔐 SSH-based security - reuses your existing SSH keys, agent and config; no new credentials to manage.
  • 🧹 Nothing left behind - the remote helper container is temporary and is removed once the push completes.
  • 🔌 Docker CLI plugin - symlink it into a plugin directory and it becomes a first-class docker pussh subcommand.
  • 🏗️ Multi-platform aware - target a specific architecture with --platform for cross-arch deployments.
  • 🪶 Tiny footprint - a single self-contained shell program, about 22 KB on disk, with no dependencies.

docker-pussh or unregistry: Which Package Do You Need?

Both packages come from the same upstream project and share a version number, but they sit on opposite ends of the connection and are genuinely separate installs:

  • docker-pussh is the client. Install it on the workstation or CI runner that builds images and wants to push them. This is the package this guide covers, and for the usual “build here, deploy there” workflow it is the only one you need.
  • unregistry is the server: a registry binary plus a systemd unit that serves images directly from a Docker daemon’s storage. Install it when you want a persistent registry endpoint rather than an on-demand push. There is a separate guide for it: How to install Unregistry on Debian.

Installing one does not install the other. A plain docker pussh push does not need the unregistry package on either machine, because the client starts its own temporary helper container on the remote host and pulls that helper image from ghcr.io.

Why Use the deb.griffo.io Repository?

  • Easy installation and updates through APT, no script to fetch and copy by hand.
  • No manual file juggling and no chmod +x to remember.
  • Always tracks upstream releases of the docker-pussh client.
  • Works across supported Debian releases, with the codename detected automatically.

Prerequisites

Before you start, make sure you have:

  • A Debian-based system (Bookworm 12, Trixie 13, Forky, or Sid)
  • sudo privileges
  • curl installed (sudo apt install curl if needed)
  • Docker locally, plus an OpenSSH client
  • Docker 19.03 or newer if you want the docker pussh subcommand form, since that relies on CLI plugin support

On the remote host you push to, you need:

  • Docker installed and running
  • An SSH user allowed to run docker (root, or a member of the docker group; if it needs sudo, that sudo docker must not prompt for a password)
  • Outbound access to ghcr.io so the temporary helper image can be pulled, or that image preloaded for air-gapped hosts

The client auto-detects the remote docker binary and containerd socket; both can be overridden if your host puts them somewhere unusual, as shown later.

Step 1: Add the deb.griffo.io Repository

Add the signing key and repository source:

# Create the keyrings directory
sudo install -d -m 0755 /etc/apt/keyrings

# Download and install the repository GPG key
curl -fsSL https://deb.griffo.io/EA0F721D231FDD3A0A17B9AC7808B4DD62C41256.asc | sudo gpg --dearmor --yes -o /etc/apt/keyrings/deb.griffo.io.gpg

# Add the repository (auto-detects your distro codename)
echo "deb [signed-by=/etc/apt/keyrings/deb.griffo.io.gpg] https://deb.griffo.io/apt $(lsb_release -sc 2>/dev/null) main" | sudo tee /etc/apt/sources.list.d/deb.griffo.io.list > /dev/null

# Update the package list
sudo apt update

What each command does:

  1. Create the keyrings directory - ensures /etc/apt/keyrings exists with proper permissions.
  2. Install the GPG key - downloads the repository key and de-armours it into a dedicated keyring for verification.
  3. Add the repository - writes a signed-by source line, with lsb_release -sc supplying your Debian codename.
  4. Update the package list - refreshes APT so it knows about the new packages.

Step 2: Update the Package List

If you skipped the last line, run the update now:

sudo apt update

Step 3: Install docker-pussh

Install the package with APT:

sudo apt install docker-pussh

This drops a single executable at /usr/local/bin/docker-pussh, which is on the default PATH, so the command works immediately.

Step 4: Enable the docker pussh Subcommand

The package installs the program to /usr/local/bin, and Docker does not scan that directory for plugins. If you want to call it as docker pussh rather than docker-pussh, link it into your user plugin directory:

mkdir -p ~/.docker/cli-plugins
ln -sf /usr/local/bin/docker-pussh ~/.docker/cli-plugins/docker-pussh

Because it is a symlink, apt upgrade keeps the plugin in step with the package automatically. To wire it up for every user on the machine instead, link it into a system plugin directory:

sudo mkdir -p /usr/local/lib/docker/cli-plugins
sudo ln -sf /usr/local/bin/docker-pussh /usr/local/lib/docker/cli-plugins/docker-pussh

This step is entirely optional. Everything below works the same if you keep calling the binary directly as docker-pussh.

Step 5: Verify the Installation

Check the binary responds:

docker-pussh --help

If you linked it as a plugin, confirm Docker discovered it:

docker pussh --help

Either way you should see usage output similar to:

Usage: docker pussh [OPTIONS] IMAGE[:TAG] [USER@]HOST[:PORT]

Upload a Docker image to a remote Docker daemon via SSH without an external registry.

Options:
  -h, --help                Show this help message.
  -F, --ssh-config path     Path to SSH config file.
  -i, --ssh-key path        Path to SSH private key for remote login (if not already added to SSH agent).
      --no-host-key-check   Skip SSH host key checking (use with caution).
      --platform string     Push a specific platform for a multi-platform image (e.g., linux/amd64, linux/arm64).

You can also confirm the plugin is registered with:

docker info --format '{{range .ClientInfo.Plugins}}{{.Name}} {{end}}'

Getting Started with docker-pussh

Push Your First Image

Build an image locally, then push it to a remote host:

# Build an image locally
docker build -t my-app:latest .

# Push it straight to the remote host over SSH
docker pussh my-app:latest deploy@203.0.113.10

Once the push finishes, the image exists in the remote Docker daemon and you can docker run my-app:latest there immediately, with no docker pull in between.

Use a Specific SSH Key or Port

Because it rides on SSH, all the familiar connection options apply. Pass a private key with -i (only needed if the key is not already in your SSH agent), or specify a non-standard port after the host:

# Push using an explicit SSH key
docker pussh my-app:1.2.3 deploy@203.0.113.10 -i ~/.ssh/id_ed25519

# Push to a host listening on a custom SSH port
docker pussh my-app:1.2.3 deploy@203.0.113.10:2222

Reuse Your SSH Config

If you already define hosts in ~/.ssh/config, refer to them by their alias, or point the client at a specific config file with -F / --ssh-config:

# 'prod' is a Host entry in your SSH config
docker pussh my-app:latest prod

# Use a dedicated SSH config file
docker pussh --ssh-config ~/.ssh/config.prod my-app:latest prod-server

For throwaway hosts whose key you have not accepted yet, --no-host-key-check skips verification. It disables a real protection against man-in-the-middle attacks, so keep it for disposable infrastructure rather than production targets.

Push a Specific Platform

For multi-architecture images, select the platform you want to land on the target:

# Push only the amd64 variant
docker pussh --platform linux/amd64 my-app:latest deploy@203.0.113.10

This requires your local Docker to use the containerd image store, which is what allows a single local image to hold several platforms.

Point at a Non-Standard Remote Setup

The client auto-detects the remote docker binary and containerd socket, and overrides exist for hosts that place them elsewhere:

REMOTE_DOCKER_PATH=/usr/local/bin/docker \
REMOTE_CONTAINERD_SOCKET=/var/run/docker/containerd/containerd.sock \
  docker pussh my-app:1.2.3 deploy@203.0.113.10

Pin the Unregistry Helper Version

The push runs a small helper image on the remote side, defaulting to a version chosen by the client. To pin it explicitly, set UNREGISTRY_IMAGE:

UNREGISTRY_IMAGE=ghcr.io/psviderski/unregistry:0.4.3 \
  docker pussh my-app:latest deploy@203.0.113.10

The same variable is how you point an air-gapped host at a helper image you have preloaded yourself.

A typical deploy loop then becomes: build locally, docker pussh to the server, and docker compose up -d over SSH to roll the new image out.

Keeping docker-pussh Updated

Because the client arrived via APT, updates come with the rest of the system:

sudo apt update && sudo apt upgrade

Whenever deb.griffo.io ships a newer docker-pussh, apt upgrade installs it automatically, and any symlink you created in Step 4 keeps pointing at the fresh binary. After a bump it is worth letting the client pull a matching helper image on the next push, or pinning one explicitly with UNREGISTRY_IMAGE as shown above.

Other Tools from deb.griffo.io

The repository is full of tools that complement a registry-free deployment workflow:

  • Unregistry - the server side of the same project, for a persistent registry endpoint.
  • Lazydocker - a terminal UI for Docker, handy for inspecting the images you have just pushed.
  • Uncloud - deploy containers across multiple hosts once your images are in place.
  • Forgejo - a self-hosted Git forge to store the Dockerfiles behind your images.

Troubleshooting

GPG or Key Errors

If APT reports an unsigned repository or an invalid key, import it again:

curl -fsSL https://deb.griffo.io/EA0F721D231FDD3A0A17B9AC7808B4DD62C41256.asc | sudo gpg --dearmor --yes -o /etc/apt/keyrings/deb.griffo.io.gpg
sudo apt update

Package Not Found

If APT cannot see the docker-pussh package:

  1. Re-run sudo apt update after adding the repository.
  2. Confirm your release is supported (Bookworm, Trixie, Forky, or Sid).
  3. Inspect the source list: cat /etc/apt/sources.list.d/deb.griffo.io.list.

docker: 'pussh' is not a docker command

The package installs to /usr/local/bin, which Docker does not search for plugins, so this is expected until you create the symlink from Step 4:

mkdir -p ~/.docker/cli-plugins
ln -sf /usr/local/bin/docker-pussh ~/.docker/cli-plugins/docker-pussh
docker pussh --help

If you install as one user but run Docker as another (for example via sudo), the plugin must be visible from that user’s home directory, or linked into a system-wide plugin directory instead. In the meantime docker-pussh on its own always works.

Push Fails on the Remote Host

If the SSH connection succeeds but the push does not:

  1. Check the SSH user can run Docker unaided: ssh deploy@203.0.113.10 docker version.
  2. Confirm the remote can reach ghcr.io to pull the helper image, or pin a preloaded one with UNREGISTRY_IMAGE.
  3. If the remote install is non-standard, set REMOTE_DOCKER_PATH and REMOTE_CONTAINERD_SOCKET explicitly.

--platform Is Rejected

Selecting a platform needs a multi-platform image locally, which in turn needs Docker’s containerd image store enabled on your machine. Without it, build and push the single-architecture image instead.

Uninstalling

Remove the client with:

sudo apt remove docker-pussh

If you created a plugin symlink, remove that too, since APT does not know about it:

rm -f ~/.docker/cli-plugins/docker-pussh
sudo rm -f /usr/local/lib/docker/cli-plugins/docker-pussh

To also drop the repository and key:

sudo rm -f /etc/apt/sources.list.d/deb.griffo.io.list
sudo rm -f /etc/apt/keyrings/deb.griffo.io.gpg
sudo apt update

Any images you already pushed to remote hosts stay exactly where they are; removing the client only affects your ability to push new ones.

Conclusion

docker-pussh removes an entire layer of infrastructure from container deployment. Instead of standing up a registry, pushing to it and pulling on the far side, you push images directly to the servers that will run them, over the SSH access you already have.

Installing it from deb.griffo.io means the client is a single apt install away and stays current with a routine apt upgrade - no script to fetch, no file to copy by hand. One optional symlink turns it into a native docker pussh subcommand.

Frequently Asked Questions

How do I install the latest docker-pussh on Debian?

Add the deb.griffo.io APT repository and its signing key, then run sudo apt install docker-pussh. The repository tracks upstream Unregistry releases, so you get the latest packaged client rather than a build frozen when your distribution was released.

Is there a .deb package for docker-pussh?

Yes. deb.griffo.io publishes docker-pussh as a signed .deb for Debian. You could download that .deb and install it by hand, but adding the repository is the better option: APT then resolves dependencies and picks up new versions on its own.

Why does docker pussh not work right after installing?

The package installs the program as /usr/local/bin/docker-pussh, and Docker only looks for plugins in dedicated cli-plugins directories. Symlink it into ~/.docker/cli-plugins/docker-pussh and the subcommand appears. Until then, run docker-pussh directly.

Do I also need the unregistry package?

Not for pushing. docker pussh starts its own temporary helper container on the remote host, so the client package alone is enough. Install unregistry only when you want a persistent registry endpoint - see How to install Unregistry on Debian.

How do I update docker-pussh to the latest version?

Run sudo apt update && sudo apt upgrade. Once the client is installed from APT there is no separate updater to remember, since new releases arrive with the rest of your system updates.

How do I install docker-pussh on Ubuntu?

Exactly the same way; lsb_release -sc simply resolves to a different codename. There is a companion guide with the Ubuntu specifics: How to install docker-pussh on Ubuntu.

Which Debian releases are supported?

Bookworm 12, Trixie 13, Forky and Sid. Because the repository line is built from lsb_release -sc, the matching suite is selected for you.

Resources


Disclaimer: The deb.griffo.io repository is an unofficial community project and is not affiliated with the official Debian or Ubuntu projects, or with the upstream Unregistry project.