Skip to content
Go back

Devcontainers Without VS Code Lock-In

By SumGuy 7 min read
Devcontainers Without VS Code Lock-In

Everyone Acts Like VS Code Owns Devcontainers

Here’s the thing: VS Code’s Remote Containers extension marketed devcontainers so hard that most folks think they’re a Microsoft-only invention. They’re not. A devcontainer is just a standardized config file—devcontainer.json—that describes a containerized dev environment. Any tool that understands the spec can spin one up.

VS Code just made it look shiny first.

If you’re running neovim, tmux, or any terminal-based editor, you don’t have to abandon devcontainers. You don’t have to SSH into a Compose stack or run Docker commands manually. There’s a better path: DevPod.

DevPod is an open-source CLI tool that treats devcontainers as a first-class concept—agnostic to your editor. Spin up an isolated, reproducible dev environment in seconds. Tear it down when you’re done. No VS Code required. No lock-in. Pure command-line bliss.


What Devcontainers Actually Are

Before we jump to the tool, let’s nail the concept. A devcontainer is three things:

  1. A container image — your dev environment packaged up. Python 3.13, Node 22, Ruby, whatever.
  2. A config file (devcontainer.json) — specifies the image, mount points, environment variables, and which dev tools to install inside the container.
  3. Mounted source code — your local repo synced into the container so you can edit locally and have your editor talk to tools inside.

That’s it. It’s not magic. It’s just Docker Compose with a standardized manifest.

Why This Matters

Imagine you’re jumping between three projects:

Without devcontainers, you’re either:

With devcontainers, you cd into each project and your whole environment—versions, tools, everything—materializes. No pollution. No cross-project contamination. Your system stays clean.


The Devcontainer.json Schema

Here’s a minimal example:

{
"name": "my-python-project",
"image": "mcr.microsoft.com/devcontainers/python:1.3-3.12-bullseye",
"features": {
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {},
"ghcr.io/devcontainers/features/github-cli:1": {}
},
"forwardPorts": [8000, 5432],
"postCreateCommand": "pip install -r requirements.txt",
"remoteUser": "vscode",
"mounts": [
"source=${localEnv:HOME}/.ssh,target=/home/vscode/.ssh,readonly",
"source=${localEnv:HOME}/.gitconfig,target=/home/vscode/.gitconfig,readonly"
]
}

Let’s break this down:

There are dozens more fields—customizations (editor-specific settings), remoteEnv (container-side env vars), containerEnv, onCreateCommand, and so on. The full spec is your reference.


Enter DevPod: The Editor-Agnostic Solution

DevPod is a Loft Labs project that abstracts away the editor. Instead of VS Code Extension orchestrating containers, DevPod’s CLI does it. You manage devcontainers from your terminal, then connect any editor you want.

Installation

Terminal window
# macOS / Linux
curl -fsSL https://devpod.sh/install.sh | sh
# Verify
devpod --version

DevPod stores config in ~/.devpod/ and metadata in .devpod/ inside each project.

The DevPod Workflow

1. Initialize a devcontainer in a new (or existing) project:

Terminal window
cd my-project
devpod up --init

DevPod prompts you:

If you choose “none”, DevPod sets up the container without trying to inject editor configs.

2. Start the devcontainer:

Terminal window
devpod up

DevPod:

  1. Builds/pulls the image
  2. Launches the container with your source code mounted
  3. Runs any postCreateCommand hooks
  4. Sits waiting for you to connect

3. SSH into the running container:

Terminal window
devpod ssh my-project

You’re now inside. Your editor runs locally, but tools (compilers, linters, language servers) are inside the container. Your $PATH inside includes everything the Dockerfile installed.

4. Use neovim or any editor as normal:

Inside the SSH session, just fire up neovim:

Terminal window
nvim src/main.py

Your neovim talks to the Python language server running inside the container. LSP over SSH works flawlessly.

5. Tear down when done:

Terminal window
devpod down

Container stops, image and mounts cleaned up (unless you keep them for next time).


Neovim + DevPod: The Real Power Move

Here’s the workflow that’ll make you forget VS Code exists:

1. Create a .devcontainer/devcontainer.json

{
"name": "rust-project",
"image": "mcr.microsoft.com/devcontainers/rust:latest",
"features": {
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {}
},
"postCreateCommand": "rustup component add rust-analyzer",
"forwardPorts": [8080],
"remoteUser": "vscode"
}

2. Start the devcontainer

Terminal window
devpod up
# Choose Docker, confirm settings, container boots

3. Drop into a shell inside the container

Terminal window
devpod ssh

4. Launch neovim

Terminal window
nvim src/main.rs

Because rust-analyzer and Cargo are installed inside the container, your LSP works over SSH. Diagnostics, go-to-definition, autocomplete—all responsive.

5. Terminal stays connected

Open a split in tmux:

Terminal window
# First pane: neovim
nvim src/main.rs
# Second pane: same shell
cargo build
cargo test

Both talk to the container seamlessly.

Why This Beats Manual Setup


Real Talk: When Devcontainers Might Be Overkill

If your project is dead simple—vanilla Node app with zero native dependencies—spinning up a whole container might feel heavy. A single global node binary is fine.

But the moment you have:

…devcontainers save you days of onboarding friction.


The Deeper Win: Language Matrices Without Docker Compose Hell

Here’s where devcontainers really shine for teams:

Create multiple configs:

.devcontainer/
python-3.11/devcontainer.json
python-3.12/devcontainer.json
python-3.13/devcontainer.json

Each one specifies a different base image:

{
"image": "python:3.11",
"postCreateCommand": "pip install -r requirements.txt && pytest"
}

Now your CI pipeline can test all three versions:

strategy:
matrix:
python-version: [3.11, 3.12, 3.13]
steps:
- run: devpod up --folder .devcontainer/python-${{ matrix.python-version }}
- run: devpod ssh -- pytest
- run: devpod down

No duplicate Compose files. No version sprawl. One .devcontainer/ directory that scales.


The Missing Piece: Keep Your Dotfiles in Sync

DevPod mounts your source code, but you probably want your neovim config, tmux settings, and shell history inside the container too.

Mount your dotfiles repo in devcontainer.json:

{
"mounts": [
"source=${localEnv:HOME}/.config/nvim,target=/home/vscode/.config/nvim,readonly",
"source=${localEnv:HOME}/.tmux.conf,target=/home/vscode/.tmux.conf,readonly",
"source=${localEnv:HOME}/.zshrc,target=/home/vscode/.zshrc,readonly"
]
}

Now when you SSH in, your editor and shell feel exactly like home. No context switching.


The Verdict

VS Code’s devcontainers are excellent, but they’re not the only game in town. If you’re a terminal person—neovim, tmux, ssh—DevPod frees you from the VS Code extension ecosystem without sacrificing the reproducibility that devcontainers offer.

You get:

No lock-in. No Extension Marketplace dependency. Just a simple JSON file and a container.

That’s the real power of open standards.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it'll show up above once verified.


Next Post
WebAuthn & Passkeys for Sysadmins

Discussion

Powered by Garrul . Sign in with GitHub or Google, or post anonymously.

Related Posts