Skip to content

Dev Containers

sshpod reads the same devcontainer.json that other Dev Container tools read. The Dev Container specification is the compatibility target: sshpod follows it where practical instead of defining an sshpod-specific workspace format. The long-term goal is for existing DevPod/devcontainer projects to migrate with little or no modification.

Version 0.1.x interprets a subset. The parser is strict about it — unknown properties are rejected with devcontainer property "X" is not supported yet rather than silently ignored, so you always know what sshpod did and did not act on.

Discovery

Locations are searched in specification order:

  1. .devcontainer/devcontainer.json
  2. .devcontainer.json
  3. .devcontainer/<folder>/devcontainer.json (one level of nesting)

If several nested configurations exist, an interactive terminal prompts for one; non-interactive callers pass --config <workspace-relative-path>. The choice is saved to the workspace target, so it only has to be made once. When nothing is found, sshpod stops with an error listing the locations it checked — it never invents a default environment.

JSON with C and C++ style comments (JSONC) is accepted, so trailing // notes in a real-world devcontainer.json parse fine.

Supported properties

PropertyNotes
nameUsed as the workspace label.
imagePulled and used as-is.
build.dockerfile, build.contextSimple form only. Exactly one of image or build must be present.
workspaceFolderDefaults to /workspaces/<workspace>.
workspaceMountBind mounts only. Defaults to a bind of the source onto the workspace folder.
mountsString-form bind mounts only.
containerEnv, remoteEnvPassed to podman create and to lifecycle exec calls respectively.
containerUser, remoteUserMapped to --user.
initializeCommandRuns on the host.
onCreateCommand, updateContentCommand, postCreateCommand, postStartCommandRun inside the container.

Lifecycle commands accept both the string form (run through /bin/sh -c) and the argv-array form (run directly).

Variable substitution

Supported today:

  • ${localWorkspaceFolder} and ${localWorkspaceFolderBasename}
  • ${containerWorkspaceFolder} and ${containerWorkspaceFolderBasename}
  • ${localEnv:NAME}
  • ${env:NAME}

Anything else fails loudly with devcontainer variable ${X} is not supported yet.

Lifecycle order

initializeCommand runs on the selected host, in the workspace directory. The remaining commands run inside the container, in basic specification order:

  • new container: onCreateCommandupdateContentCommandpostCreateCommand, then postStartCommand
  • existing container, stopped or already running: postStartCommand only

On SSH providers, both host and Podman commands execute through the configured OpenSSH host.

Not supported yet

  • lifecycle command objects (the parallel form)
  • Dev Container Features
  • customizations
  • Compose-based configurations
  • build arguments and build options
  • non-bind mount forms, and the object form of mounts
  • forwardPorts, portsAttributes, and port forwarding generally
  • postAttachCommand
  • full variable substitution
  • source synchronization from a local directory to a remote host
  • container reconciliation after the configuration changes

Reaching for one of these is not a silent failure — sshpod stops and names the property. Progress against the full specification is tracked on the roadmap.

What the container looks like

sshpod creates the container itself rather than delegating to a shim:

sh
podman create --name sshpod-<workspace>-<provider> \
  --label sshpod.managed=true \
  --label sshpod.workspace=<workspace> \
  --label sshpod.provider=<provider> \
  [--env K=V ...] [--user U] [--mount ...] \
  <image>

It is kept alive by a long sleep loop as its entrypoint, and lifecycle commands run through podman exec --workdir <workspaceFolder>. A build configuration is compiled first with podman build and tagged localhost/sshpod-<workspace>-<provider>:latest.

Because the naming and labels are deterministic, plain podman ps, podman logs, and podman exec all work against an sshpod workspace.

Released under the BSD-3-Clause License