Authoring a Dev Container Feature

01 Nov 2022 - @joshspicer

Development container ‘Features’ are self-contained, shareable units of installation code and development container configuration. We define a pattern for authoring and self-publishing Features.

In this document, we’ll outline a “quickstart” to help you get up-and-running with creating and sharing your first Feature. You may review an example along with guidance in our devcontainers/feature-starter repo as well.

Note: While this walkthrough will illustrate the use of GitHub and the GitHub Container Registry, you can use your own source control system and publish to any OCI Artifact supporting container registry instead.

Create a repo

Start off by creating a repository to host your Feature. In this guide, we’ll use a public GitHub repository.

For the simplest getting started experience, you may use our example feature-starter repo. You may select the green Use this template button on the repo’s page.

You may also create your own repo on GitHub if you’d prefer.

Create a folder

Once you’ve forked the feature-starter repo (or created your own), you’ll want to create a folder for your Feature. You may create one within the src folder.

If you’d like to create multiple Features, you may add multiple folders within src.

Add files

At a minimum, a Feature will include a devcontainer-feature.json and an install.sh entrypoint script.

There are many possible properties for devcontainer-feature.json, which you may review in the Features spec.

Below is a hello world example devcontainer-feature.json and install.sh. You may review the devcontainers/features repo for more examples.

devcontainer-feature.json:

{
    "name": "Hello, World!",
    "id": "hello",
    "version": "1.0.2",
    "description": "A hello world feature",
    "options": {
        "greeting": {
            "type": "string",
            "proposals": [
                "hey",
                "hello",
                "hi",
                "howdy"
            ],
            "default": "hey",
            "description": "Select a pre-made greeting, or enter your own"
        }
    }
}

install.sh:

#!/bin/sh
set -e

echo "Activating feature 'hello'"

GREETING=${GREETING:-undefined}
echo "The provided greeting is: $GREETING"

cat > /usr/local/bin/hello \
<< EOF
#!/bin/sh
RED='\033[0;91m'
NC='\033[0m' # No Color
echo "\${RED}${GREETING}, \$(whoami)!\${NC}"
EOF

chmod +x /usr/local/bin/hello

Publishing

The feature-starter repo contains a GitHub Action workflow that will publish each feature to GHCR. By default, each feature will be prefixed with the <owner/<repo> namespace. Using the hello world example from above, it can be referenced in a devcontainer.json with: ghcr.io/devcontainers/feature-starter/color:1.

Note: You can use the devcontainer features publish command from the Dev Container CLI if you are not using GitHub Actions.

The provided GitHub Action will also publish a third “metadata” package with just the namespace, eg: `ghcr.io/devcontainers/feature-starter. This is useful for supporting tools to crawl metadata about available Features in the collection without downloading all the Features individually.

By default, GHCR packages are marked as private. To stay within the free tier, Features need to be marked as public.

This can be done by navigating to the Feature’s “package settings” page in GHCR, and setting the visibility to public. The URL may look something like:

https://github.com/users/<owner>/packages/container/<repo>%2F<featureName>/settings

Changing package visibility to public

Adding Features to the Index

If you’d like your Features to appear in our public index so that other community members can find them, you can do the following:

Feature collections are scanned to populate a Feature index on the containers.dev site and allow them to appear in Dev Container creation UX in supporting tools like VS Code Dev Containers and GitHub Codespaces.