zed devcontainer with ssh
The editor ‘zed’ does not support devcontainers. However, it does work with ssh.
Instead of direct support for devcontainers, ‘zed’ offers the Remote Development feature, which relies on a connection via ssh.
You can run an ssh server in a Docker container and get something close to a native devcontainer.
The idea comes from Supercharged Development with Zed.
This requires:
Dockerfile– Integration of ssh server into thedevcontainerdocker-compose.yaml– Build and start configuration of thedevcontainer~/.ssh/config– SSH client entry for easy access to the container- zed
settings.json– appropriate remote config entry in the editor settings
A working example can be found on codeberg.org: go http3 testing / learning + zed devcontainer
Dockerfile
Example based on a small GO project. The image is essentially based on a Debian derivative.
#
# @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
The devcontainer can be started with 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
Add a special host entry to ~/.ssh/config
#
# zed devcontainer
#
Host devcontainer
Hostname localhost
User app
Port 2222
StrictHostKeyChecking accept-new
UserKnownHostsFile /dev/nullSettings in »zed«
In ‘zed’, the devcontainer can be easily opened using ssh devcontainer.
{
"ssh_connections": [
{
"host": "devcontainer",
"username": "app",
"projects": [
{
"paths": [
"/app"
]
}
]
}
],
}