Reproducing the devcontainer DX, but securely with SBX

•7 min read

I have always loved using devcontainer, because it lets VS Code open a project in a preconfigured container with its tools, dependencies and extensions, so you can start coding right away in a reproducible environment without installing everything on your machine. devcontainer connects VS Code to the container, so you keep your usual editor while using the container's tools and dependencies for the terminal, autocompletion, tests and debugging: you develop in the same environment as the one where the code runs.

Today, with sbx and its SSH access feature, you can absolutely connect VS Code to a running sbx sandbox over SSH. Your code is then compiled and executed inside this isolated environment, rather than on your host machine. You get a setup similar to what devcontainer offers, but with the security hardening tools of sbx such as the microVM, secrets protection and network policies.

Let's see how to put this into practice with sbx kits, and make it all smooth and straightforward thanks to the sbx env create and sbx env rm commands.

Docker sbx kits are declarative YAML configurations that define sandboxes and enrich them with network policies, environment variables, commands ...

Prerequisites

First of all, you need VS Code installed. Then, you need to install the Remote-SSH extension in VS Code (only once):

bash
code --install-extension ms-vscode-remote.remote-ssh

Then, you need to add the *.sbx block to ~/.ssh/config (idempotent):

bash
sbx setup ssh

Creating the vscode-ssh kit

In a folder, create a tree like this one:

bash
.
├── demo
│   └── README.md
├── kits
│   └── vscode-ssh
│       ├── config
│       │   └── machine-settings.json
│       ├── kit.dockerfile
│       └── kit.yaml
└── sbxenv.yaml
  • The demo folder will be the VS Code workspace (this is where you can create the .vscode folder with the extensions.json and settings.json files)
  • The kits folder will contain all the kits needed to build our secure working environment.
  • The sbxenv.yaml file will let us manage the environment of our sbx sandbox (think of it as the compose file of sbx)

vscode-ssh

The kit is made of the following 3 files:

vscode-ssh/config/machine-settings.json:

json
{
    "telemetry.telemetryLevel": "off",
    "update.mode": "none",
    "extensions.autoUpdate": false,
    "terminal.integrated.defaultProfile.linux": "bash"
}

vscode-ssh/kit.dockerfile:

dockerfile
# syntax=docker/dockerfile:1
# vscode-ssh: the shell template
FROM docker/sandbox-templates:shell

USER root

# What the VS Code server expects on a Remote-SSH host
RUN <<EOF
set -eux
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
  libstdc++6 tar gzip curl wget ca-certificates procps git
rm -rf /var/lib/apt/lists/*
EOF

USER agent

# Default machine settings for the VS Code server
RUN --mount=type=bind,target=/tmp/kit <<EOF
set -eux
install -D -m 0644 /tmp/kit/config/machine-settings.json \
  /home/agent/.vscode-server/data/Machine/settings.json
EOF

WORKDIR /home/agent/workspace

vscode-ssh/kit.yaml:

yaml
# syntax=docker/sandbox-kit:3
schemaVersion: "3"
kind: workload
displayName: vscode-ssh
description: >-
    Shell sandbox for VS Code Remote-SSH
sourceUrl: https://rickub.com/k33g/devsbx
licenses: [MIT]
version: "0.1.0"
capabilities:
    - type: com.docker.sandbox/sbx@1
    - type: com.docker.sandbox/network-policy@1
      config:
        runtime:
            allow:
                - update.code.visualstudio.com
                - vscode.download.prss.microsoft.com
                - "*.vscode-cdn.net"
                - marketplace.visualstudio.com
                - "*.gallery.vsassets.io"
                - "*.gallerycdn.vsassets.io"
                - open-vsx.org

sbxenv.yaml

sbxenv.yaml is a declarative configuration file that describes a Docker Sandboxes environment (AI agent, mounted folders, variables, secrets and tools) so you can create and manage it with the sbx env commands.

sbx env documentation

yaml
schemaVersion: "1"

# Sandbox name = SSH host (<name>.sbx): letters, digits and hyphens only.
name: vscode-demo

# The workload kit is the base "agent": its name is the kit directory name.
agent: vscode-ssh

kits:
  - ./kits/vscode-ssh

# Mounted at the same path as on the host, 
# so VS Code opens that same path remotely.
workspace: ./demo

So let's see how to launch our new development environment.

Starting the secure development environment

First, you need to create and start the sbx sandbox with the following command:

bash
sbx env create --force

--force skips the confirmation messages

And once the sbx sandbox is created, you can start VS Code with the command below to connect it to the sandbox over SSH:

bash
code --new-window --remote ssh-remote+vscode-demo.sbx "$PWD/demo"

Note: in ssh-remote+vscode-demo.sbx we reuse the sandbox name defined in sbxenv.yaml: name: vscode-demo

And VS Code will start in the demo folder and initialize the connection to the vscode-demo sandbox:

VS Code + SSH
VS Code + SSH

But now it would be nice to add a few tools, like a Rust toolchain for example. So we are going to create a second type of kit, a mixin kit, to install Rust in our environment.

Adding Rust to our working environment

First, let's stop and remove the running sbx instance with the command:

bash
sbx env rm --force

And let's create a new rust-toolchain kit in the ./kits folder:

bash
.
├── demo
│   └── README.md
├── kits
│   ├── rust-toolchain
│   │   ├── rust-toolchain.dockerfile
│   │   └── rust-toolchain.yaml
│   └── vscode-ssh
│       ├── config
│       │   └── machine-settings.json
│       ├── kit.dockerfile
│       └── kit.yaml
└── sbxenv.yaml

Now, let's have a look at the contents of rust-toolchain.dockerfile and rust-toolchain.yaml.

rust-toolchain/rust-toolchain.dockerfile:

dockerfile
# syntax=docker/dockerfile:1

# --- build stage: the Rust toolchain, through rustup ---
FROM debian:trixie-slim AS rust
ARG RUST_VERSION

RUN <<EOF
set -eu
apt-get update
apt-get install -y --no-install-recommends curl ca-certificates
rm -rf /var/lib/apt/lists/*
EOF

ENV RUSTUP_HOME=/usr/local/rustup \
    CARGO_HOME=/usr/local/cargo
RUN <<EOF
set -eu
curl -fsSL https://sh.rustup.rs | sh -s -- -y --no-modify-path \
  --profile minimal \
  --default-toolchain "${RUST_VERSION}" \
  --component rustfmt,clippy,rust-analyzer,rust-src
EOF

RUN <<EOF
set -eu
toolchain="$(ls -d /usr/local/rustup/toolchains/*)"
mkdir -p /out/usr/local/bin
for bin in cargo rustc rustdoc rustfmt cargo-fmt cargo-clippy clippy-driver rust-analyzer; do
  ln -s "${toolchain}/bin/${bin}" "/out/usr/local/bin/${bin}"
done
# Kept in the overlay at the same path: the links point into it.
mkdir -p /out/usr/local/rustup/toolchains
mv "${toolchain}" /out/usr/local/rustup/toolchains/
EOF

# --- linker stage: what the workload lacks to link, from its own distro ---
FROM docker/sandbox-templates:shell AS linker
USER root
RUN <<EOF
set -eu
apt-get update
apt-get install -y --no-install-recommends --download-only gcc libc6-dev
mkdir -p /out
for deb in /var/cache/apt/archives/*.deb; do
  dpkg-deb -x "${deb}" /out
done
# Set by update-alternatives in a real install, which unpacking skips.
ln -sf gcc /out/usr/bin/cc
rm -rf /out/usr/share/doc /out/usr/share/man /out/usr/share/info /out/usr/share/locale
EOF

FROM scratch
COPY --from=linker /out/ /
COPY --from=rust /out/ /

rust-toolchain/rust-toolchain.yaml:

yaml
# syntax=docker/sandbox-kit:3
schemaVersion: "3"
kind: mixin
displayName: Rust toolchain
description: Rust toolchain (cargo, rustc, rustfmt, clippy) and rust-analyzer (language server)

args:
  rust_version:
    # A Rust release (e.g. 1.99.0), or stable / beta / nightly (not reproducible).
    default: "1.99.0"
    pattern: '^([0-9]+\.[0-9]+(\.[0-9]+)?|stable|beta|nightly)$'
    buildArg: RUST_VERSION

provides: ["rust@${{ kit.args.rust_version }}"]

capabilities:
  # Runtime access with the tool: the crates.io index and crate downloads.
  - type: com.docker.sandbox/network-policy@1
    config:
      runtime:
        allow:
          - index.crates.io
          - static.crates.io
          - crates.io
          - github.com

Then let's update sbxenv.yaml to add ./kits/rust-toolchain:

sbxenv.yaml:

yaml
schemaVersion: "1"

# Sandbox name = SSH host (<name>.sbx): letters, digits and hyphens only.
name: vscode-demo

# The workload kit is the base "agent": its name is the kit directory name.
agent: vscode-ssh

# Relative paths resolve against the directory of this file.
kits:
  - ./kits/vscode-ssh
  - ./kits/rust-toolchain

# Mounted at the same path as on the host, 
# so VS Code opens that same path remotely.
workspace: ./demo

And now all you have to do is run again:

bash
sbx env create --force

Wait a few moments while the new kit builds.

Then, we can start VS Code:

bash
code --new-window --remote ssh-remote+vscode-demo.sbx "$PWD/demo"

And you can check that Rust is properly installed:

VS Code + SSH
VS Code + SSH

And of course, you can absolutely add the AI agent of your choice to your setup.

Adding Claude Code

So I added a claude-code kit of type mixin to my project. You can grab it here: sbx-vscode/kits/claude-code, and I added this line to the kits section in sbxenv.yaml:

yaml
- ./kits/claude-code

And once my sandbox is recreated, I can now use Claude Code:

VS Code + Claude
VS Code + Claude
VS Code + Claude
VS Code + Claude

Et voilà! You now have everything you need to build yourself development environments that are secure for real.

Written by

Keep reading

0 Comments

No comments yet. Be the first to comment!

From other blogs

Related reading

Copyright © 2026•k33g_org's Blog•Powered by Writizzy