Devsy
Developing Providers

Drivers

In Devsy you can specify a Driver in the Agent's configuration.

A Driver indicates how Devsy deploys the workspace container.

Devsy currently supports five built-in driver types:

  • Docker driver
  • Kubernetes driver
  • Apple driver
  • Microsandbox driver
  • Custom driver

The provider schema also accepts external runtime declarations. Their host adapter is under development.

If no driver is specified, the default is Docker

External runtime declarations (under development)

The provider schema accepts agent.driver: external and validates the configuration when the provider is installed. Devsy launches the declared runtime through the workspace driver factory and negotiates its Runtime Protocol v1 capabilities.

agent:
  driver: external
  external:
    binary: RUNTIME_DRIVER
    args: ["serve"]
    imageBackend: docker
  binaries:
    RUNTIME_DRIVER:
      - os: linux
        arch: amd64
        path: https://example.com/runtime-driver
        checksum: "<64 hexadecimal characters: SHA-256 of the extracted executable>"

binary is the exact key in agent.binaries, not an executable path or a name to search on PATH. Each declared platform requires a SHA-256 checksum and must appear only once. Declare the agent's OS and architecture, which may differ from the desktop's platform. The runtime must already be prepared in the agent binary directory; the resolver verifies its checksum and never downloads or removes files. A declared local absolute path is also verified.

args is a static argument array, with no shell or option substitution. Its first entry cannot be empty; later empty arguments are preserved. imageBackend accepts docker (the default) or none; it selects the Devsy-side image backend, not an image-building capability in the runtime plugin.

For the protocol and SDK contract, see Runtime Protocol v1.

Runtime and image integration

The internal host now negotiates Runtime Protocol v1 Info and performs preflight, Find, TargetArchitecture, RunImage, Start, Stop, and Delete calls. Each operation uses a fresh plugin process owned by the SDK supervisor, exposed through a hidden helper in the running Devsy executable. Cancellation also covers startup and terminates the leased process tree. The workspace runner uses the external runtime for lifecycle and execution, and a separate Docker image backend for inspection, builds, tagging, and publication. Docker settings use agent.docker. Selecting none skips Docker backend construction; ordinary builds retain the existing dockerless fallback, while prebuild publication requires an image backend.

Run options preserve image identity, entrypoint, literal arguments, environment, labels, privilege/init presence, namespace mappings, platform, and mount options. CPU, memory, storage, and GPU requirements are translated into protocol values. Invalid sizes, unsupported mount types, Compose external volumes, raw runArgs, appPort mappings (use port forwarding instead), and GPU core or memory requirements that v1 cannot express are rejected before runtime creation. During recreation, validation happens before stopping or removing the existing workspace. Mount streaming, workspace ownership, and recreate policy follow the negotiated runtime capabilities; in-place reprovisioning remains unsupported.

The host also supports binary-safe Exec with separate stdout/stderr, literal argv, stdin closure, and terminal exit status. Shell commands use /bin/sh -c; text stdout and stderr are redacted, while RawStdout and argv execution preserve raw stdout for protocol consumers. Logs capability is checked before launch and its merged binary stream is written to stdout.

The host retains environment values supplied through RunImage for that workspace throughout its lifetime, including prior values from retries or failed requests. Retention belongs to the current workspace runner and is released with that host; it is not a persisted secret store and does not survive a new CLI process. Runtime plugins must keep diagnostics safe when values are not available to a new host. Later text output, errors, and diagnostics use that workspace's redaction snapshot; raw stdout and merged Logs remain binary streams for their callers to handle.

Once Exec streaming starts, the host closes closable stdin on completion or cancellation. Blocking input must unblock when closed or when the caller context is canceled, and output writers must return from writes. Go cannot interrupt an arbitrary blocking reader or writer. The host joins the input pump and reaps the owned plugin session before returning.

The host inherits its environment for compatibility with proxy, certificate, HOME/XDG, Docker, and runtime CLI settings. Provider environment overrides and the verified runtime binary key are forwarded; plugin transport metadata keeps precedence. Arguments are passed literally without shell expansion, and text diagnostics are redacted before logging.

Before every operation the host rechecks the prepared runtime's checksum and permissions. Relative declarations also reject symlink targets outside their binary directory; explicit absolute declarations retain their documented behavior. These checks do not provide a filesystem sandbox or atomic verification-to-execution guarantee. The runtime and its directories must be trusted. Unix command descendants must remain in the supervised process group and retain signalable privileges; detached or elevated services need their own owner. Real-runtime compatibility and startup measurements against a complete workspace launch remain gates before runtime cutover or session reuse.

Docker Driver

The Docker driver is the default driver that Devsy uses to deploy the workspace container.

This container (specified through a devcontainer.json), is executed through Docker inside the provider environment, for example in a VM in case of Machine Providers.

Some optional configs are available:

  • path: where to find the Docker CLI or a replacement, such as Podman. Defaults to docker.
  • install: whether to install Docker or not in the target environment
  • builder: which docker builder to use
  • runtime: explicitly select the container runtime (docker, podman, or nerdctl). When empty, the runtime is auto-detected from the binary at path.
  • elevation: optionally run docker commands through a privilege-elevation helper for rootful daemons whose socket the current user can't access. One of pkexec, sudo, doas, or none (default).
  • helperImage: overrides the helper image used for volume operations. Empty falls back to the DEVSY_HELPER_IMAGE environment variable, then a built-in default.
  • env: a map of environment variables to set when running docker commands, e.g. DOCKER_HOST

Example config:

agent:
  containerInactivityTimeout: 10m
  docker:
    path: /usr/bin/docker
    install: false
    elevation: none
    env:
      DOCKER_HOST: ""

Kubernetes Driver

Instead of Docker, Devsy is also able to use Kubernetes as a Driver, which allows you to deploy the workspace to a Kubernetes cluster instead. For example, this makes it possible to create a provider that spins up a remote Kubernetes cluster (or just a namespace), connects to it, and creates a workspace there. Devsy also has a default Kubernetes provider that uses the local Kubernetes config file to deploy the workspace.

The allowed options for the Kubernetes driver are:

  • kubernetesContext: which kube context to use (if empty will use current kube context)
  • kubernetesConfig: path to which kube config to use (if empty will use default kube config, or $KUBECONFIG)
  • kubernetesNamespace: which namespace to use (if empty will use current namespace or default)
  • kubernetesPullSecretsEnabled: if true, Devsy will create Kubernetes pull secrets from injected Docker credentials for private registries
  • podTimeout: how long the provider waits for the workspace pod to come up, e.g. 10m
  • createNamespace: if true, Devsy will try to create the namespace
  • clusterRole: if defined, Devsy will create a role binding for the given cluster role for the workspace container. This is useful if you need Kubernetes access within the workspace container
  • serviceAccount: if defined, Devsy will use the given service account for the dev container
  • architecture: the CPU architecture to use for the workspace pod, e.g. amd64, arm64. If empty, Devsy inspects the cluster's nodes to auto-detect it (and errors out if the cluster has mixed architectures).
  • inactivityTimeout: after how much time to automatically stop the pod due to inactivity
  • storageClass: the storage class to use to create the persistent volume claim
  • diskSize: the default size for the persistent volume to use, e.g. 10Gi
  • pvcAccessMode: the access mode to use for the persistent volume claim, e.g. RWO, ROX, RWX or RWOP
  • pvcAnnotations: annotations to add to the main workspace PVC
  • nodeSelector: the node selector to use for the workspace pod, e.g. my-label=value,my-label-2=value-2
  • resources: resource requests/limits for the workspace container, e.g. requests.cpu=500m,limits.memory=5Gi
  • workspaceVolumeMount: overrides the path where the workspace volume is mounted. Defaults to the root of your workspace source code.
  • podManifestTemplate: a pod manifest template (inline YAML or a file path) used as the base to build the Devsy pod
  • labels: labels to add to the workspace pod, e.g. devsy.sh/example=value,devsy.sh/example2=value2
  • strictSecurity: Clears the hardcoded runAsUser/runAsGroup/runAsNonRoot fields (retaining capabilities and privileged), letting the cluster assign the container's UID/GID instead of forcing root. It sets hostUsers: false unless podManifestTemplate explicitly supplies hostUsers; this satisfies OpenShift restricted-v3.
  • agentSecurityContext: Inline YAML or a file path for a corev1.SecurityContext merged field by field onto the workspace and init containers. It can configure run-as, capabilities, privilege escalation, seccomp, and other supported security-context fields, overriding Devsy's defaults. It sets hostUsers: false unless podManifestTemplate explicitly supplies hostUsers. A matching named container in podManifestTemplate remains the highest-precedence override.
  • agentInstallPath: overrides where the agent binary is installed inside the devsy/devsy-init containers. Defaults to /usr/local/bin/devsy, which requires root to write; set this to a path under a writable mount (e.g. the workspace volume) when running non-root.
  • kubernetesUserNamespaces: Sets hostUsers: false (unless podManifestTemplate already set it), mapping the pod's UIDs into a Linux user namespace. strictSecurity and agentSecurityContext also opt in for OpenShift restricted-v3; provide hostUsers explicitly through podManifestTemplate on clusters without user-namespace support. UserNamespacesSupport is disabled by default in Kubernetes 1.30–1.32, enabled by default in 1.33–1.35, and becomes GA with the gate locked on from 1.36. Node-level support is also required (Linux kernel 6.3+, containerd 2.0+/CRI-O 1.25+).

On OpenShift, the default container security context (fixed runAsUser/runAsGroup) is rejected by the restricted-v2/restricted-v3 SCCs, which assign UIDs/GIDs from a per-namespace range. Set strictSecurity: "true" and/or agentSecurityContext (or override the container's securityContext via a named container in podManifestTemplate) to satisfy those SCCs. Also set agentInstallPath to a path under a writable mount, because a non-root container cannot write the default /usr/local/bin/devsy.

Devsy also supports building images inside Kubernetes without Docker, via a dockerless build path (agent.dockerless.disabled / agent.dockerless.image). This replaces the previous buildkit-based building approach.

Example Kubernetes Provider

Example Kubernetes provider that uses local kubectl to run a workspace in the current kube context:

name: simple-kubernetes
version: v0.0.1
agent:
  containerInactivityTimeout: 10m # Pod will automatically kill itself after timeout
  path: ${DEVSY}
  driver: kubernetes
  kubernetes:
    # kubernetesContext: default
    # kubernetesNamespace: my-namespace-for-devsy
    # clusterRole: ""
    # serviceAccount: ""
    diskSize: 20Gi
    createNamespace: true
exec:
  command: |-
    ${DEVSY} helper sh -c "${COMMAND}"

Then add the provider via devsy provider add ./simple-kubernetes.yaml

Apple Driver

The Apple driver runs the workspace as a Linux container using Apple's native container runtime (macOS 26+, Apple silicon). This is the driver used by the built-in apple provider.

Available options:

  • path: where to find the container binary. Defaults to container.
  • rosetta: enable Rosetta for x86_64 emulation inside the Linux guest.
  • env: a map of environment variables to set when running container commands
agent:
  containerInactivityTimeout: 10m
  driver: apple
  apple:
    path: container
    rosetta: "false"

Microsandbox Driver

The Microsandbox driver boots the devcontainer image as a hardware-isolated microVM (via libkrun) using the microsandbox runtime. This is the driver used by the built-in microsandbox provider.

Available options:

  • memory: guest memory limit in MiB. Empty uses the runtime default.
  • cpus: number of virtual CPUs. Empty uses the runtime default.
  • maxMemory: hotplug memory ceiling in MiB. Empty uses the runtime default.
  • maxCpus: hotplug CPU ceiling. Empty uses the runtime default.
  • storage: OCI root disk size in GiB. Empty uses the runtime default, falling back to the devcontainer's hostRequirements.storage when set.
  • blockEgress: if true, deny the microVM outbound public network access (sandbox hardening).
  • ephemeral: if true, boot from a tmpfs root disk so the microVM's disk state is discarded when it stops. Sized by storage/hostRequirements.storage, or 8GiB if neither is set.
  • workspaceHostPermissions: controls chmod behavior for the primary workspace bind mount. mirror (the default) mirrors guest rwx changes to the host; private keeps them in MicroSandbox metadata.
  • workspaceStatVirtualization: controls workspace ownership and metadata virtualization. strict (the default) requires compatible host metadata support; relaxed tolerates filesystems without it; off exposes literal host ownership and modes. off cannot be combined with mirror.

The primary workspace bind mount uses stat-virt=strict and host-perms=mirror by default, while additional bind mounts retain MicroSandbox's defaults. containerUser controls the workload execution user; remoteUser controls the workspace developer identity, falling back to containerUser, the image user, and then root. Devsy resolves the developer's numeric UID and primary GID from the final image's account files and sets them as the mount's guest-visible fallback owner. These IDs apply to host-created entries without per-file guest metadata; they do not change host inode ownership. Account resolution does not execute image code. An unresolvable user fails provisioning before replacement of the existing VM. With workspaceStatVirtualization: "off", Devsy omits the owner override.

In Dockerless mode, the developer filesystem is built inside the VM after its workspace mount is created. Ownership synchronization therefore supports only remoteUser: "root" in this mode. Other identities fail before replacing an existing VM. For a non-root developer, use a prebuilt image or explicitly select workspaceStatVirtualization: "off" with private host permissions. Devsy never looks up the developer account in the unrelated Dockerless runner image.

Image inspection and preparation prefer an image cached in the configured Docker-compatible CLI, permitting offline/private-registry startup. Preparation freezes the final image before account resolution and import. Both use that same snapshot, imported under a content-derived tag, so retagging the original image cannot change the imported filesystem or its fallback owner. Registry-only images do not require Docker initialization.

Devsy-managed workspaces with an incompatible persisted workspace mount contract require explicit devsy up --recreate, including workspaces created before the contract label existed. Changing workspace permission policy or the effective developer identity also requires explicit recreation. Ordinary devsy up reports an actionable error and preserves the existing VM. Back up VM-local data before recreating: the host workspace and named volumes persist, but the VM root disk is discarded. Replacement cannot roll back failures after removal. Externally managed containers cannot be replaced through this flow.

Devsy requires MicroSandbox v0.7.2 or newer when creating or recreating workspaces that use workspace ownership synchronization. Runtime compatibility, account resolution, final image import, and volume preparation complete before removing an existing VM. The provisioning version check does not block inspection, logs, stop, or deletion of an existing sandbox. Apple Silicon macOS is the canonical supported integration-test host; use relaxed when a host filesystem cannot provide strict metadata virtualization.

agent:
  containerInactivityTimeout: 10m
  driver: microsandbox
  microsandbox:
    memory: "2048"
    blockEgress: "false"
    ephemeral: "false"
    workspaceHostPermissions: "mirror"
    workspaceStatVirtualization: "strict"

Custom Driver

The custom driver lets a provider fully own how the devcontainer lifecycle is implemented, by supplying its own shell commands instead of relying on Devsy's built-in Docker, Kubernetes, Apple or Microsandbox integration.

When driver: custom is set, the following commands become required under agent.custom:

  • findDevContainer: locate an existing devcontainer
  • commandDevContainer: execute a command inside the devcontainer
  • targetArchitecture: determine the target architecture
  • runDevContainer: run the devcontainer
  • startDevContainer: start the devcontainer
  • stopDevContainer: stop the devcontainer
  • deleteDevContainer: delete the devcontainer

The optional getDevContainerLogs command retrieves devcontainer logs, and canReprovision signals whether the driver supports reprovisioning the devcontainer in place.

On this page