← Back to DevBytes

Zed Docker Integration: Complete Guide

Introduction to Zed Docker Integration

Zed is a high-performance, multiplayer code editor built in Rust by the creators of Atom. While Zed is renowned for its blazing speed and collaborative features, its integration with Docker has become an increasingly important workflow for developers working in containerized environments. This guide walks you through everything you need to know about using Docker with Zed, from basic setup to advanced configurations.

What Is Zed Docker Integration?

Zed Docker integration refers to the set of workflows and configurations that allow you to develop inside Docker containers using the Zed editor. Unlike VS Code, which has a first-party "Dev Containers" extension, Zed approaches containerized development through its remote development capabilities and SSH-based workflows. This means you can connect Zed to a Docker container running an SSH server, effectively editing code that lives inside the container while leveraging Zed's native performance.

The integration enables you to:

Why Docker Integration Matters

Modern development teams increasingly rely on containers to ensure reproducibility. When every developer runs the same container image, the "it works on my machine" problem disappears. Zed's ability to connect to these containers means you get the best of both worlds: the speed of a native editor and the consistency of a containerized environment.

Key benefits include:

Prerequisites

Before you begin, ensure you have the following installed and configured:

Verify your installations:

# Check Zed version
zed --version

# Check Docker
docker --version
docker compose version

# Check SSH
ssh -V

Setting Up a Docker Container for Zed

The core strategy for Zed Docker integration is to run an SSH server inside your container. Zed's remote development feature connects via SSH, giving you full access to the container's filesystem and environment.

Creating a Development Dockerfile

Here is a complete Dockerfile that sets up a development environment with SSH access:

# Dockerfile.dev
FROM ubuntu:22.04

# Avoid interactive prompts during package installation
ENV DEBIAN_FRONTEND=noninteractive

# Install essential development tools and SSH server
RUN apt-get update && apt-get install -y \
    openssh-server \
    curl \
    git \
    build-essential \
    ca-certificates \
    vim \
    && rm -rf /var/lib/apt/lists/*

# Configure SSH server
RUN mkdir /var/run/sshd

# Set a password for root (change this in production)
RUN echo 'root:devpassword' | chpasswd

# Allow root login with password
RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config

# Allow password authentication
RUN sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config

# SSH login fix so that systemd does not need to run
RUN sed -i 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' /etc/pam.d/sshd

# Create a non-root developer user
RUN useradd -m -s /bin/bash developer && \
    echo 'developer:devpassword' | chpasswd

# Set up the project directory
WORKDIR /workspace
RUN chown developer:developer /workspace

# Expose SSH port
EXPOSE 22

# Start SSH server
CMD ["/usr/sbin/sshd", "-D"]

Building and Running the Container

Build the image and run the container with port forwarding for SSH:

# Build the development image
docker build -t zed-dev-env -f Dockerfile.dev .

# Run the container with SSH port mapped to 2222
docker run -d \
  --name zed-dev \
  -p 2222:22 \
  -v $(pwd):/workspace \
  zed-dev-env

The -v $(pwd):/workspace flag mounts your current directory into the container, so changes made from Zed are reflected both in the container and on your host.

Using SSH Keys Instead of Passwords

For a more secure setup, use SSH key-based authentication. First, generate a key pair if you do not have one:

# Generate an SSH key (skip if you already have one)
ssh-keygen -t ed25519 -f ~/.ssh/zed_docker_key -N ""

Then update your Dockerfile to copy the public key into the container:

# Add this to your Dockerfile.dev before the CMD instruction
COPY ~/.ssh/zed_docker_key.pub /home/developer/.ssh/authorized_keys
RUN chown developer:developer /home/developer/.ssh/authorized_keys && \
    chmod 600 /home/developer/.ssh/authorized_keys

Alternatively, you can inject the key at runtime:

# Copy your public key into the running container
docker cp ~/.ssh/zed_docker_key.pub zed-dev:/tmp/authorized_keys
docker exec zed-dev bash -c "mkdir -p /home/developer/.ssh && \
  cp /tmp/authorized_keys /home/developer/.ssh/authorized_keys && \
  chown -R developer:developer /home/developer/.ssh && \
  chmod 600 /home/developer/.ssh/authorized_keys"

Connecting Zed to Your Docker Container

Configuring SSH for the Container

Add an entry to your SSH config file to simplify the connection:

# ~/.ssh/config

Host zed-docker
  HostName localhost
  Port 2222
  User developer
  IdentityFile ~/.ssh/zed_docker_key
  StrictHostKeyChecking no
  UserKnownHostsFile /dev/null

Test the connection:

ssh zed-docker

If you see the container's shell prompt, the connection is working. Type exit to return to your host.

Opening a Remote Project in Zed

Zed supports remote projects through its command palette and CLI. To connect to your Docker container:

You can also use the Zed CLI to open a remote project directly:

# Open a remote project via SSH
zed ssh:developer@localhost:2222/workspace

# Or using your SSH config alias
zed ssh:zed-docker/workspace

Using Docker Compose for Multi-Service Development

Most real-world projects involve multiple services. Docker Compose lets you define your entire development stack, and you can connect Zed to any container that has SSH enabled.

Defining a Compose File

# docker-compose.dev.yml
version: "3.9"

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    ports:
      - "2222:22"
      - "3000:3000"
    volumes:
      - ./:/workspace
      - app_node_modules:/workspace/node_modules
    environment:
      - DATABASE_URL=postgres://devuser:devpass@db:5432/devdb
      - REDIS_URL=redis://redis:6379
    depends_on:
      - db
      - redis

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: devuser
      POSTGRES_PASSWORD: devpass
      POSTGRES_DB: devdb
    ports:
      - "5432:5432"
    volumes:
      - db_data:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

volumes:
  app_node_modules:
  db_data:

Starting the Stack

# Start all services in the background
docker compose -f docker-compose.dev.yml up -d

# Verify all containers are running
docker compose -f docker-compose.dev.yml ps

# Connect Zed to the app container
zed ssh:zed-docker/workspace

Installing Zed Extensions Inside the Container

When you connect to a remote project, Zed runs language servers and extensions on the remote host — in this case, inside your Docker container. You need to ensure the necessary language toolchains are installed in the container.

Adding Language Support to Your Dockerfile

# Extend the Dockerfile.dev with language toolchains

# Install Node.js 20
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
    apt-get install -y nodejs

# Install Python 3 and pip
RUN apt-get update && apt-get install -y \
    python3 \
    python3-pip \
    python3-venv

# Install Go
RUN curl -fsSL https://go.dev/dl/go1.22.0.linux-amd64.tar.gz | \
    tar -C /usr/local -xzf - && \
    ln -s /usr/local/go/bin/go /usr/local/bin/go

# Install Rust
RUN su - developer -c "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y"
ENV PATH="/home/developer/.cargo/bin:${PATH}"

# Install useful CLI tools
RUN apt-get install -y ripgrep fd-find && \
    ln -s /usr/bin/fdfind /usr/local/bin/fd

Rebuild the container after updating the Dockerfile:

docker compose -f docker-compose.dev.yml up -d --build app

Configuring Zed Settings for Docker Workflows

Zed stores its settings in a JSON file. You can configure project-specific settings that apply when working inside the container.

Project-Level Settings

Create a .zed/settings.json file in your project root:

{
  "lsp": {
    "rust-analyzer": {
      "binary": {
        "path": "/home/developer/.cargo/bin/rust-analyzer"
      },
      "settings": {
        "checkOnSave": {
          "command": "clippy"
        }
      }
    },
    "typescript-language-server": {
      "settings": {
        "typescript": {
          "preferences": {
            "importModuleSpecifier": "relative"
          }
        }
      }
    }
  },
  "tasks": {
    "version": 2,
    "tasks": {
      "build": {
        "label": "build",
        "command": "npm run build",
        "use_new_terminal": true
      },
      "test": {
        "label": "test",
        "command": "npm test",
        "use_new_terminal": true
      },
      "lint": {
        "label": "lint",
        "command": "npm run lint",
        "use_new_terminal": true
      }
    }
  }
}

Using Zed Tasks for Docker Commands

You can define tasks that run Docker commands directly from Zed's task runner:

{
  "tasks": {
    "version": 2,
    "tasks": {
      "docker-up": {
        "label": "Docker: Start Stack",
        "command": "docker compose -f docker-compose.dev.yml up -d",
        "use_new_terminal": true
      },
      "docker-down": {
        "label": "Docker: Stop Stack",
        "command": "docker compose -f docker-compose.dev.yml down",
        "use_new_terminal": true
      },
      "docker-logs": {
        "label": "Docker: Tail Logs",
        "command": "docker compose -f docker-compose.dev.yml logs -f",
        "use_new_terminal": true
      },
      "docker-rebuild": {
        "label": "Docker: Rebuild App",
        "command": "docker compose -f docker-compose.dev.yml up -d --build app",
        "use_new_terminal": true
      }
    }
  }
}

Run tasks by pressing Cmd+Shift+P and selecting task: spawn.

DevContainer-Style Setup with Zed

If you are migrating from VS Code and already have a devcontainer.json, you can adapt it for Zed. While Zed does not natively parse devcontainer.json, you can extract the Docker configuration and create a compatible setup.

Converting a DevContainer to a Zed-Compatible Setup

Given a typical .devcontainer/devcontainer.json:

{
  "name": "My Dev Container",
  "image": "mcr.microsoft.com/devcontainers/typescript-node:20",
  "features": {
    "ghcr.io/devcontainers/features/docker-outside-of-docker:1": {}
  },
  "forwardPorts": [3000, 5432],
  "postCreateCommand": "npm install",
  "customizations": {
    "vscode": {
      "extensions": ["esbenp.prettier-vscode"]
    }
  }
}

You can create an equivalent Docker Compose file for Zed:

# docker-compose.zed.yml
version: "3.9"

services:
  dev:
    image: mcr.microsoft.com/devcontainers/typescript-node:20
    ports:
      - "2222:22"
      - "3000:3000"
      - "5432:5432"
    volumes:
      - ./:/workspace
      - /var/run/docker.sock:/var/run/docker.sock
    working_dir: /workspace
    command: bash -c "npm install && /usr/sbin/sshd -D"
    user: root

Note that the Microsoft dev container images come with many tools pre-installed, which makes them an excellent base for Zed remote development.

Best Practices for Zed Docker Integration

1. Use Named Volumes for Dependencies

Mounting your host directory is useful for source code, but dependency directories like node_modules or vendor should use named volumes to avoid conflicts between host and container architectures:

volumes:
  - ./:/workspace
  - app_node_modules:/workspace/node_modules
  - app_target:/workspace/target

2. Keep Images Lean

Use multi-stage builds to keep your development image focused. Install only the tools you need, and use Alpine-based images where possible to reduce image size and attack surface.

# Multi-stage Dockerfile for development
FROM node:20-alpine AS base
RUN apk add --no-cache openssh git

FROM base AS dev
RUN mkdir /var/run/sshd
RUN echo 'root:devpassword' | chpasswd
RUN sed -i 's/#PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config
EXPOSE 22
CMD ["/usr/sbin/sshd", "-D"]

3. Use .dockerignore

Prevent unnecessary files from entering your build context:

# .dockerignore
.git
node_modules
dist
build
.env
.env.local
*.log
.DS_Store
.vscode
.zed

4. Pin Your Image Versions

Always pin exact versions of base images and tools to ensure reproducible environments across your team:

FROM node:20.11.1-alpine3.19

5. Secure Your SSH Setup

For team environments, never commit passwords or private keys to version control. Use Docker secrets or environment variables for sensitive credentials:

# Use Docker secrets for SSH keys
echo "my-ssh-private-key" | docker secret create zed_ssh_key -

# Reference in compose
services:
  app:
    secrets:
      - zed_ssh_key
    environment:
      - SSH_KEY_FILE=/run/secrets/zed_ssh_key

6. Leverage Zed's Integrated Terminal

When connected to a remote project, Zed's integrated terminal opens inside the container. This means you can run docker exec, npm test, or any other command without leaving the editor. Use split panes to monitor logs while editing.

7. Automate Container Lifecycle

Create shell scripts to streamline your workflow:

#!/bin/bash
# dev-start.sh - Start development environment and open Zed

set -e

echo "Starting Docker containers..."
docker compose -f docker-compose.dev.yml up -d --build

echo "Waiting for SSH to be ready..."
until docker exec zed-dev ssh -V 2>/dev/null; do
  sleep 1
done

echo "Opening Zed..."
zed ssh:zed-docker/workspace

Make it executable and add it to your project:

chmod +x dev-start.sh
./dev-start.sh

Troubleshooting Common Issues

SSH Connection Refused

If Zed cannot connect, verify the SSH server is running inside the container:

# Check if sshd is running
docker exec zed-dev ps aux | grep sshd

# Restart SSH if needed
docker exec zed-dev /usr/sbin/sshd

# Check SSH logs
docker exec zed-dev cat /var/log/auth.log

Language Server Not Found

When Zed connects remotely, it looks for language servers in the container. If a language server is missing, install it inside the container:

# Install TypeScript language server inside the container
docker exec zed-dev npm install -g typescript typescript-language-server

# Install Rust analyzer
docker exec -u developer zed-dev rustup component add rust-analyzer

File Permissions Issues

If you see permission errors when editing files, ensure the container user matches the mounted volume's owner:

# Check file ownership on the host
ls -la /path/to/project

# Fix ownership inside the container
docker exec zed-dev chown -R developer:developer /workspace

Performance Optimization

If editing feels sluggish over SSH, try these optimizations:

# Optimized SSH config for Zed Docker
Host zed-docker
  HostName localhost
  Port 2222
  User developer
  IdentityFile ~/.ssh/zed_docker_key
  StrictHostKeyChecking no
  UserKnownHostsFile /dev/null
  ServerAliveInterval 60
  ServerAliveCountMax 3
  Compression yes

Conclusion

Zed's Docker integration through SSH-based remote development provides a powerful and flexible way to build software in containerized environments. By setting up an SSH-enabled development container, configuring your SSH client, and leveraging Zed's project settings and task runner, you can create a seamless workflow that combines Zed's exceptional performance with the reproducibility and isolation of Docker. While the setup requires more manual configuration than a first-party Dev Containers extension, the approach is robust, portable, and works with any containerized environment. As Zed continues to evolve, we can expect even deeper container integration features, but the SSH-based workflow described in this guide will remain a reliable foundation for containerized development with Zed.

— Ad —

Google AdSense will appear here after approval

← Back to all articles