Development Containers Specification

repository·gh-pages·Indexed 41 days ago

https://github.com/devcontainers/devcontainers.github.io

Documentation for the Development Containers Specification, including guides on creating, packaging, and distributing Dev Container Features. Covers the devcontainer-feature.json metadata schema, OCI registry distribution, lifecycle hooks, and integration with devcontainer.json.

Tokens
29K
Snippets
62
Records
125
Agent score
49%

What's inside Development Containers

  1. Overview of supported Dev Container services

    gh-pages

    Several cloud and client-side services support the Dev Container Specification:

    • GitHub Codespaces: Cloud-hosted development environments (VM-based) that can be accessed via browser or VS Code.
    • CodeSandbox: Cloud environments running on microVM architecture using rootless Podman.
    • DevPod: A client-only tool that creates reproducible environments on any backend (local, Kubernetes, remote machines, or cloud VMs).
    • Ona (formerly Gitpod): Provides secure, ephemeral development environments for humans and agents, fully adhering to the Dev Container Specification.
  2. What is prebuilding a dev container image?

    gh-pages
    Prebuilding is the process of packaging all your application's dependencies into a single container image ahead of time. Instead of building the container from scratch every time a developer or a CI/CD pipeline starts a new environment, they can pull a pre-built image from a container registry. This significantly reduces startup times, simplifies devcontainer.json configurations, and allows you to pin specific versions of tools for better stability and security.
  3. Overview of the Dev Container CLI

    gh-pages

    The Dev Container Command Line Interface (CLI) is the reference implementation of the Dev Container Specification. It is designed for both direct use and for integration by other tools or services.

    Key Capabilities:

    • Container Creation: Takes a devcontainer.json to create and configure a dev container.
    • Prebuilding: Enables prebuilding dev container configurations in CI/CD environments like GitHub Actions.
    • Feature Support: Detects and applies dev container features at runtime.
    • Lifecycle Management: Executes lifecycle scripts such as postCreateCommand, providing more advanced orchestration than standard docker build or docker run commands.
  4. Package and distribute Templates as tarballs

    gh-pages

    Templates are distributed as tarballs named devcontainer-template-<id>.tgz. The tarball must contain the entire contents of the Template's sub-directory, including the metadata and configuration files.

    Versioning: Templates use semantic versioning (semver). Tooling should republish major and minor versions according to semver, but should not republish if the exact version has already been published.

  5. Manage Feature dependencies with `dependsOn` and `installsAfter`

    gh-pages

    Feature authors can control the installation order using two types of dependencies defined in devcontainer-feature.json:

    Hard Dependencies: dependsOn

    • Purpose: Indicates required features that must be installed before the current feature.
    • Behavior: Evaluated recursively. If any dependency fails to resolve or install, the entire dev container creation fails.
    • Configuration: Elements mirror the features object in devcontainer.json, allowing you to provide specific options for the dependency.

    Soft Dependencies: installsAfter

    • Purpose: Influences the order of features that are already queued for installation.
    • Behavior: Not evaluated recursively. It only affects features already in the installation list. It cannot be used to pull in new features that weren't already requested.
    • Configuration: An array of Feature IDs. It cannot provide options or use version pinning/digests.
    {
        "name": "My Feature",
        "id": "myFeature",
        "version": "1.0.0",
        "dependsOn": {
            "foo:1": {
                "flag": true
            },
            "bar:1.2.3": {}
        }
    }
  6. Use `${devcontainerId}` in Feature Metadata

    gh-pages

    The ${devcontainerId} variable can be used in devcontainer.json and Feature metadata to reference a unique, stable identifier for the dev container.

    Usage Constraints:

    • It should only be used in properties that are not used during the image build process (to allow for pre-building images).
    • Supported properties: entrypoint, mounts, and customizations.

    Example: Using ID for unique volume mounts

    {
        "id": "docker-in-docker",
        "version": "1.0.4",
        "mounts": [
            {
                "source": "dind-var-lib-docker-${devcontainerId}",
                "target": "/var/lib/docker",
                "type": "volume"
            }
        ]
    }
  7. Use Lifecycle Hooks in Dev Container Features

    gh-pages

    Features can declare lifecycle hooks to execute commands at specific stages of the container lifecycle. These commands are executed from the context of the project workspace folder.

    Key Behaviors:

    • Commands provided by Features are always executed before user-provided commands in devcontainer.json.
    • Commands are executed in sequence (blocking the next command).
    • If the object syntax is used, all commands within that group execute in parallel but still block subsequent Features or devcontainer.json commands.

    Available Hooks:

    • onCreateCommand
    • updateContentCommand
    • postCreateCommand
    • postStartCommand
    • postAttachCommand

    Note: These properties are stored within the image metadata.

  8. What are Dev Container Templates?

    gh-pages

    Development Container Templates are packaged source files that encode the configuration for a complete development environment. They allow users to quickly add a pre-configured environment to a new or existing project.

    A Template consists of a folder containing:

    1. A devcontainer-template.json file (metadata).
    2. A devcontainer.json file (configuration).
    3. Optional additional files (e.g., Dockerfile, docker-compose.yml, boilerplate code, or lifecycle scripts).

    Supporting tools use these templates to build development containers by downloading them from an OCI registry and applying their configurations to the user's project.

  9. Implement redundant installation strategies

    gh-pages
    To increase the reliability of a Feature, implement multiple installation paths for upstream dependencies. If one method fails (e.g., a package manager error or a rotated GPG key), the script should fall back to an alternative method (e.g., downloading a specific GitHub release). Use scenario tests to ensure all fallback paths are functional.
  10. Understand formatting for string vs array properties

    gh-pages

    In devcontainer.json, certain properties like postCreateCommand, postStartCommand, postAttachCommand, and initializeCommand support three formats. The choice affects how the command is executed and how arguments are parsed.

    1. String Format: The command is passed to /bin/sh. Shell features like && or environment variable expansion work.

      • Example: "postAttachCommand": "echo foo='bar'" (Shell parses the quotes).
    2. Array Format: The command is passed directly to the OS without a shell. This is more robust for arguments containing spaces, but shell features like && will not work.

      • Example: "postAttachCommand": ["echo", "foo='bar'"] (The quotes are preserved and passed to the executable).
      • Note for runArgs: When using the array format for runArgs, do not include single quotes for parameters with spaces; they are not passed to the executable.
    3. Object Format: Allows for parallel execution of multiple commands.

      • Example:
        {
          "postAttachCommand": {
            "server": "npm start",
            "db": ["mysql", "-u", "root", "-p", "my database"]
          }
        }
    "runArgs": ["--device-cgroup-rule=my rule here"]
  11. Configure devcontainer.json lifecycle scripts

    gh-pages

    Lifecycle scripts allow you to run commands at specific stages of the dev container lifecycle. Commands can be defined as a string (executed via /bin/sh), an array (executed directly without a shell), or an object (to support parallel execution).

    Execution Order & Context:

    1. initializeCommand: Runs on the host machine during initialization. Can run multiple times per session.
    2. onCreateCommand: Runs inside the container immediately after its first creation. Used for initial setup; cloud services may use this for prebuilding (limited access to user secrets).
    3. updateContentCommand: Runs inside the container after onCreateCommand whenever new content is available. Used by cloud services to refresh cached containers.
    4. postCreateCommand: Runs inside the container after updateContentCommand and once assigned to a user. Can access user-specific secrets.
    5. postStartCommand: Runs inside the container every time it is successfully started.
    6. postAttachCommand: Runs inside the container every time a tool successfully attaches.

    Important Notes:

    • If any lifecycle script fails, subsequent scripts are skipped.
    • Use waitFor (enum) to specify which command tools should wait for before connecting (defaults to updateContentCommand).
    • Commands execute from the workspaceFolder.
    {
      "postAttachCommand": {
        "server": "npm start",
        "db": ["mysql", "-u", "root", "-p", "my database"]
      }
    }