docker2ssh is a Linux tool for exposing Docker containers through SSH-compatible ports.
It is useful for Cursor, Code-OSS, Trae, and other VS Code-like environments that cannot use the Remote Docker extension because of compliance or platform restrictions.
Typical use cases:
- connect to a specific container with normal SSH tooling
- use
DOCKER_HOST=ssh://...from local Docker CLI workflows - support editor workflows that need SSH access or Docker-over-SSH access to a remote container environment
python3 -m pip install -U pip
python3 -m pip install -U docker2sshNotes:
- The published package is for Linux
x86_64andaarch64 - Upgrading
pipfirst is recommended on older systems so it recognizes the published wheel tags - Installing or upgrading the package also installs or updates the bundled
d2sruntime used bydocker2ssh
cargo build --releaseBinary:
./target/release/d2s helpFor a Linux binary without external .so dependencies, build against musl:
./scripts/build-static-linux.shDefault output:
./dist/d2s-x86_64-unknown-linux-muslNotes:
- Default target:
x86_64-unknown-linux-musl - Override target with
TARGET=aarch64-unknown-linux-musl - Use
USE_DOCKER=1to build insideclux/muslrust:stable - The script verifies the output with
lddwhen available
- Exposes one SSH listener per configured port.
- Accepts normal SSH shell / exec requests and maps them to
docker exec. - Accepts Docker CLI's
docker system dial-stdioexec request. - Proxies Docker HTTP API traffic to the host Docker socket.
- Restricts
/containers/<id or name>/...requests to the mapped container. - Allows container references by name, full ID, or short ID.
- Supports stopped containers in config storage.
- Shows only running containers in
show. - Verifies active mappings with
doctor.
docker2ssh
docker2ssh help
docker2ssh config set <port> <container>
docker2ssh config rm <port>
docker2ssh config list
docker2ssh show
docker2ssh doctor --host 127.0.0.1 --user docker [--identity /path/to/id_ed25519]
docker2ssh serve
docker2ssh stop
docker2ssh upgradeNotes:
docker2sshwithout subcommand prints help.config setvalidates that the container exists, even if stopped.showonly prints mappings whose container is currently running.servestarts a background daemon and writes a pid file next to the config file.stopstops the daemon started from the same config path.upgraderunspython3 -m pip install -U docker2sshto update the PyPI package and bundled runtime.
Default path:
- Linux:
~/.config/d2s/config.toml
Example:
listen_host = "0.0.0.0"
docker_socket = "/var/run/docker.sock"
authorized_keys = "/etc/d2s/authorized_keys"
host_key = "/etc/d2s/host_key_ed25519"
[[mappings]]
port = 2222
container = "my-app"
shell = "/bin/bash"
[[mappings]]
port = 2223
container = "3d2c1b0a9f87"Fields:
listen_host: bind address for SSH listeners.docker_socket: Docker daemon Unix socket path.authorized_keys: optional OpenSSHauthorized_keysfile. If omitted, server accepts unauthenticated test connections. Do not use that in production.host_key: optional SSH host private key path. If missing, the bundled runtime auto-generates one.mappings[].shell: optional shell override inside the container. If omitted, the bundled runtime tries/bin/bash, then/bin/sh, thensh.
Map a port to a container:
docker2ssh config set 2222 my-app
docker2ssh config set 2223 my-app --shell /bin/bashStart the daemon:
docker2ssh serve
docker2ssh showFrom another machine:
ssh root@your-server -p 2222
ssh root@your-server -p 2222 "ls /"
DOCKER_HOST=ssh://docker@your-server:2222 docker ps
DOCKER_HOST=ssh://docker@your-server:2222 docker cp ./file.txt my-app:/tmp/file.txtStop it later:
docker2ssh stopUpgrade docker2ssh from PyPI:
docker2ssh upgradeIf you need a specific Python interpreter:
PYTHON=/path/to/python docker2ssh upgradedocker2ssh serve already detaches into the background, so you can run it directly without an external service manager for the normal case.
Typical lifecycle:
docker2ssh --config /etc/d2s/config.toml serve
docker2ssh --config /etc/d2s/config.toml stopNotes:
- Access to
/var/run/docker.sockis required unless you run with a user in the Docker group. - Binding high ports like
2222does not require root. - The daemon pid file is written next to the config file as
d2s.pid. - The foreground daemon watches the config file and restarts listeners automatically when mappings change.
This repository also includes the Python packaging used to publish precompiled Linux builds to PyPI.
Package directory:
python/
What it provides:
- a
docker2sshconsole script - a small Python API that shells out to the
d2sbinary D2Smethods forserve,upgrade,show,doctor,config set/rm/list- a bundled
d2sbinary inside each published wheel
Build a precompiled wheel after producing the static binary:
TARGET=x86_64-unknown-linux-musl ./scripts/build-static-linux.sh
TARGET=x86_64-unknown-linux-musl ./scripts/build-python-wheel.shNotes:
- The wheel metadata is emitted as
py3-none-<platform> - CI builds Linux
x86_64andaarch64wheels in.github/workflows/python-wheels.yml - Tag
vX.Y.Zpublishes package versionX.Y.Z; if that version already exists on PyPI, CI automatically usesX.Y.Z.postN - No source distribution is required for installs from published wheels
D2S_BINstill overrides the bundled binary when you want to use another runtime binary
If you want to point the wrapper at a non-standard binary path:
export D2S_BIN=/usr/local/bin/d2sExample:
from docker2ssh import D2S
client = D2S(config="/etc/d2s/config.toml")
print(client.show())
client.config_set(2222, "my-container")- Authentication is SSH public-key based when
authorized_keysis configured. - Normal SSH access is translated to
docker exec. - Docker CLI's
docker system dial-stdiois still accepted for remote Docker API access. - Requests targeting
/containers/<ref>/...are rewritten and restricted to the mapped container. - Global Docker endpoints like
/_ping,/version,/info,/eventsare still allowed.
This is a pragmatic container-scoped proxy, not a complete Docker RBAC implementation.
Run:
cargo testCurrent test coverage includes:
- config read/write and upsert/remove behavior
- Docker path authorization and rewriting
- HTTP proxy over Unix socket
- SSH end-to-end doctor flow over a mock Docker backend
- SSH shell mode mapped to container exec
- SSH exec mode mapped to container exec
- Rust server:
src/ - Static build script:
scripts/build-static-linux.sh - Python package:
python/