zed devcontainer mit ssh

Der Editor »zed« unterstützt keine devcontainer Unterstützung. Mittels ssh geht es doch.

Statt der direkten Unterstützung von devcontainern gibt es in »zed« das Feature Remote Development welches auf die Verbindung via ssh setzt.

Man kann einen ssh-server in einem Docker Container laufen lassen und bekommt damit etwas, was einem nativen devcontainer nahe kommt.

Die Idee stammt aus Supercharged Development with Zed.

Dazu wird benötigt:

  • Dockerfile – Integration ssh-server in den devcontainer
  • docker-compose.yaml – Build und Start-Konfiguration des devcontainer
  • ~/.ssh/config – SSH Client Eintrag für den einfachen Zugang in den Container
  • zed settings.json – passender Remote Config Eintrag in den Editor-Einstellungen

Dockerfile

Beispiel anhand eines kleinen GO Projektes. Das Image beruht im Kern auf einen Debian-Derivat.

#
# @see https://www.friedrichkurz.me/posts/2025-06-07-zed-devcontainer/
#

FROM docker.io/library/golang:latest

#
# zed didn't ask for a password - leave app user password empty
#
ARG APP_USER="app"
ARG APP_PASSWORD=""
ARG APP_DIR="/app"
ARG APP_UID="1000"
ARG SSH_PORT="2222"

#
# install ssh server and some essential development commands
#
RUN apt update \
    && apt install -y --no-install-recommends \
    iproute2 \
    net-tools \
    procps \
    ssh \
    sudo \
    && apt clean \
    && echo >/etc/sudoers.d/10-installer  "%sudo ALL=(ALL) NOPASSWD: ALL"

#
# basic sshd configuration
# - accept (empty) password authentication
# - listen on custom high port
# - accept some env variables
#
RUN cat <<EOT >/etc/ssh/sshd_config.d/app.conf
AcceptEnv LANG LC_* GIT_*
AllowUsers ${APP_USER}
AuthenticationMethods none
#AuthenticationMethods none,keyboard-interactive,password
KbdInteractiveAuthentication yes
LogLevel VERBOSE
PasswordAuthentication yes
PermitEmptyPasswords yes
Port ${SSH_PORT}
PrintMotd no
Subsystem sftp /usr/lib/openssh/sftp-server
UsePAM yes
EOT

#
# add development group and user
#
RUN groupadd -f -g ${APP_UID} ${APP_USER}
RUN useradd -u ${APP_UID} -g ${APP_UID} -G sudo -p "${APP_PASSWORD}" -m -s /bin/bash ${APP_USER}

#
# check sshd config
#
RUN /usr/sbin/sshd -t -e

#
# clone the relevant docker environment for ssh login
#
RUN env | grep -i go >> /home/${APP_USER}/.bashrc

#
# prepare app
#
USER ${APP_USER}
WORKDIR ${APP_DIR}

COPY --chown=${APP_UID}:${APP_UID} . ${APP_DIR}
RUN go mod vendor
RUN go build
RUN openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes -subj "/C=DE/ST=Berlin/O=private/CN=devcontainer.local"
VOLUME [ "${APP_DIR}" ]

#
# start sshd
#
USER root
EXPOSE ${SSH_PORT}
CMD ["/usr/sbin/sshd", "-D", "-e"]

Docker Compose

Starten lässt sich der devcontainer mit docker compose up --build

services:
  devcontainer:
    image: devcontainer:latest
    pull_policy: always
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - type: bind
        source: .
        target: /app
    ports:
      - name: ssh
        protocol: tcp
        app_protocol: ssh
        target: 2222
        published: 2222
      - name: http
        protocol: tcp
        app_protocol: http
        target: 80
      - name: https
        protocol: tcp
        app_protocol: https
        target: 443
      - name: http3
        protocol: udp
        app_protocol: http3
        target: 443
    cap_add:
      - CAP_NET_BIND_SERVICE
    networks:
      - localdev
    working_dir: /app
    tty: true

networks:
    localdev:
        driver: bridge
        driver_opts:
            # https://docs.docker.com/network/packet-filtering-firewalls/#setting-the-default-bind-address-for-containers
            "com.docker.network.bridge.host_binding_ipv4": "127.0.0.1"

ssh Config

Ein Eintrag in ~/.ssh/config

#
# zed devcontainer
#
Host devcontainer
        Hostname localhost
        User app
        Port 2222
        StrictHostKeyChecking accept-new
        UserKnownHostsFile /dev/null

Settings in »zed«

In »zed« lässt sich der devcontainer einfach mittels ssh devcontainer öffnen.

{
  "ssh_connections": [
    {
      "host": "devcontainer",
      "username": "app",
      "projects": [
        {
          "paths": [
            "/app"
          ]
        }
      ]
    }
  ],
}