▸ Push docker images directly to remote servers without an external registry ◂
<a href="https://discord.gg/eR35KQJhPu"><img src="https://img.shields.io/badge/discord-5865F2.svg?style=for-the-badge&logo=discord&logoColor=white" alt="Join Discord"></a>
<a href="https://x.com/psviderski"><img src="https://img.shields.io/badge/follow-black?style=for-the-badge&logo=X&logoColor=while" alt="Follow on X"></a>
<a href="https://github.com/sponsors/psviderski"><img src="https://img.shields.io/badge/Donate-EA4AAA.svg?style=for-the-badge&logo=githubsponsors&logoColor=white" alt="Donate"></a>
Unregistry is a lightweight container image registry that stores and serves images directly from your Docker daemon's storage.
The included docker pussh command (extra 's' for SSH) lets you push images straight to remote Docker servers over SSH.
It transfers only the missing layers, making it fast and efficient.
https://github.com/user-attachments/assets/9d704b87-8e0d-4c8a-9544-17d4c63bd050
You've built a Docker image locally. Now you need it on your server. Your options suck:
docker save | ssh <remote server> docker load transfers the entire image, even if 90% already exists
on the serverYou just want to move an image from A to B. Why is this so hard?
docker pussh myapp:latest user@server
That's it. Your image is on the remote server. No registry setup, no subscription, no intermediate storage, no exposed ports. Just a direct transfer of the missing layers over SSH.
Here's what happens under the hood:
docker push to unregistry through the forwarded port, transferring only the layers that don't already exist
remotely. The transferred image is instantly available on the remote Docker daemonIt's like rsync for Docker images — simple and efficient.
[!NOTE] Unregistry was created for Uncloud, a lightweight tool for deploying containers across multiple Docker hosts. We needed something simpler than a full registry but more efficient than save/load.
docker commands (user is root or non-root user is in docker group — see
Manage Docker as a non-root user
for details)sudo is required, ensure the user can run sudo docker without a password promptYour server has internet access to ghcr.io to pull the unregistry image
ghcr.io/psviderski/unregistry:latest on first docker pussh use.
# On a machine with internet access
docker pull ghcr.io/psviderski/unregistry:X.Y.Z
docker save ghcr.io/psviderski/unregistry:X.Y.Z | ssh user@server docker load
``
- Unregistry container requires access to the containerd socket at/run/containerd/containerd.sock, so the container
runs asroot` to have the necessary permissions
brew install psviderski/tap/docker-pussh
After installation, to use docker-pussh as a Docker CLI plugin (docker pussh command) you need to create a symlink:
mkdir -p ~/.docker/cli-plugins
ln -sf $(brew --prefix)/bin/docker-pussh ~/.docker/cli-plugins/docker-pussh
Download the current version:
mkdir -p ~/.docker/cli-plugins
# Download the script to the docker plugins directory
curl -sSL https://raw.githubusercontent.com/psviderski/unregistry/v0.4.3/docker-pussh \
-o ~/.docker/cli-plugins/docker-pussh
# Make it executable
chmod +x ~/.docker/cli-plugins/docker-pussh
If you want to download and use the latest version from the main branch:
curl -sSL https://raw.githubusercontent.com/psviderski/unregistry/main/docker-pussh \
-o ~/.docker/cli-plugins/docker-pussh
chmod +x ~/.docker/cli-plugins/docker-pussh
Via unofficial repository packages created and maintained at unregistry-debian by @dariogriffo
You can install unregistry the debian way by running:
curl -sS https://debian.griffo.io/EA0F721D231FDD3A0A17B9AC7808B4DD62C41256.asc | sudo gpg --dearmor --yes -o /etc/apt/trusted.gpg.d/debian.griffo.io.gpg
echo "deb https://debian.griffo.io/apt $(lsb_release -sc 2>/dev/null) main" | sudo tee /etc/apt/sources.list.d/debian.griffo.io.list
apt install -y unregistry
apt install docker-pussh
or in the releases page of the repository here
Windows is not currently supported, but you can try using WSL 2 with the above Linux instructions.
docker pussh --help
Unregistry stores images directly in containerd's image store, which is the underlying container runtime used by Docker. However, by default, Docker maintains its own separate storage layer and doesn't directly use images from containerd.
When you enable containerd image store in Docker, it allows Docker to directly use the same images that unregistry stores in containerd, eliminating duplication.
pussh operations without the additional pull step from unregistry to the classic Docker image storepussh runs an additional docker pull on the remote host to pull the image from unregistry to make
it available to Dockershell
sudo ctr -n moby images ls
sudo ctr -n moby images rm <image>Please refer to the official Docker documentation: Enable containerd image store on Docker Engine.
[!WARNING] Switching to containerd image store causes you to temporarily lose images and containers created using the classic storage driver. Those resources still exist on your filesystem, and you can retrieve them by turning off the containerd image store feature.
Push an image to a remote server. Please make sure the SSH user has permissions to run docker commands (user is
root or non-root user is in docker group). If sudo is required, ensure the user can run sudo docker without a
password prompt.
docker pussh myapp:latest user@server.example.com
With SSH key authentication if the private key is not added to your SSH agent:
docker pussh myapp:latest ubuntu@192.168.1.100 -i ~/.ssh/id_rsa
Using a custom SSH port:
docker pussh myapp:latest user@server:2222
Using a custom SSH config file:
docker pussh myapp:latest prod-server -F ~/.ssh/config.prod
Push a specific platform for a multi-platform image. The local Docker has to use containerd image store to support multi-platform images.
docker pussh myapp:latest user@server --platform linux/amd64
Use a specific unregistry image version on the remote host:
UNREGISTRY_IMAGE=ghcr.io/psviderski/unregistry:A.B.C docker pussh myapp:latest user@server.example.com
Build locally and push directly to your production servers. No middleman.
docker build --platform linux/amd64 -t myapp:1.2.3 .
docker pussh myapp:1.2.3 deploy@prod-server
ssh deploy@prod-server docker run -d myapp:1.2.3
Skip the registry complexity in your pipelines. Build and push directly to deployment targets.
- name: Build and deploy
run: |
docker build -t myapp:${{ github.sha }} .
docker pussh myapp:${{ github.sha }} deploy@staging-server
Distribute images in isolated networks that can't access public registries over the internet.
Sometimes you want a local registry without the overhead. Unregistry works great for this:
# Run unregistry locally and expose it on port 5000
docker run -d -p 5000:5000 --name unregistry \
-v /run/containerd/containerd.sock:/run/containerd/containerd.sock \
ghcr.io/psviderski/unregistry
# Use it like any registry
docker tag myapp:latest localhost:5000/myapp:latest
docker push localhost:5000/myapp:latest
Need custom SSH settings? Use the standard SSH config file, or pass a specific config with -F:
# ~/.ssh/config
Host prod-server
HostName server.example.com
User deploy
Port 2222
IdentityFile ~/.ssh/deploy_key
# Now just use
docker pussh myapp:latest prod-server
# Or use an alternate config file
docker pussh myapp:latest prod-server -F ~/.ssh/config.prod
docker-pussh plugin for Docker CLIdocker-pussh plugin for Docker CLIFound a bug or have a feature idea? We'd love your help!
🐛 Found a bug? Open an issue
💡 Have questions, ideas, or need help?
Built with ❤️ by Pasha Sviderski who just wanted to deploy his images
$ claude mcp add unregistry \
-- python -m otcore.mcp_server <graph>