<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.3">Jekyll</generator><link href="https://devcontainers.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://devcontainers.github.io/" rel="alternate" type="text/html" /><updated>2024-01-25T10:05:59+01:00</updated><id>https://devcontainers.github.io/feed.xml</id><title type="html">Development containers</title><subtitle>Development containers documentation and specification page.
</subtitle><entry><title type="html">General Availability of Dependabot Integration</title><link href="https://devcontainers.github.io/guide/dependabot" rel="alternate" type="text/html" title="General Availability of Dependabot Integration" /><published>2024-01-23T00:00:00+01:00</published><updated>2024-01-23T00:00:00+01:00</updated><id>https://devcontainers.github.io/guide/dependabot</id><content type="html" xml:base="https://devcontainers.github.io/guide/dependabot">&lt;p&gt;We are excited to announce that starting today, in collaboration with the Dependabot Team, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainers&lt;/code&gt; package ecosystem is now generally available! 
 Dependabot will now be able to update your public Dev Container &lt;a href=&quot;https://containers.dev/features&quot;&gt;Features&lt;/a&gt;, keeping them up-to-date with the latest published versions.&lt;/p&gt;

&lt;p&gt;To opt-in, add a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.github/dependabot.yml&lt;/code&gt; to a repository containing one or more &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer.json&lt;/code&gt; configuration files:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# To get started with Dependabot version updates, you'll need to specify which&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# package ecosystems to update and where the package manifests are located.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Please see the documentation for all configuration options:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;updates&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;package-ecosystem&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;devcontainers&quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# See documentation for possible values&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;schedule&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;interval&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;weekly&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once configured, Dependabot will begin to create pull requests to update your Dev Container Features:&lt;/p&gt;

&lt;div style=&quot;display: flex; justify-content: center;&quot;&gt;
    &lt;img style=&quot;max-width: 100%; height: auto;&quot; src=&quot;/img/dependabot-pr.png&quot; alt=&quot;Dependabot PR&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;An example diff generated by Dependabot is shown below:&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;---
&lt;/span&gt; .devcontainer-lock.json              | 8 ++++----
 .devcontainer.json                   | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/.devcontainer-lock.json b/.devcontainer-lock.json
&lt;span class=&quot;gh&quot;&gt;index 324582b..a3868d9 100644
&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;--- a/.devcontainer-lock.json
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+++ b/.devcontainer-lock.json
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@@ -1,9 +1,9 @@&lt;/span&gt;
 {
   &quot;features&quot;: {
&lt;span class=&quot;gd&quot;&gt;-    &quot;ghcr.io/devcontainers/features/docker-in-docker:1&quot;: {
-      &quot;version&quot;: &quot;1.0.9&quot;,
-      &quot;resolved&quot;: &quot;ghcr.io/devcontainers/features/docker-in-docker@sha256:b4c04ba88371a8ec01486356cce10eb9fe8274627d8d170aaec87ed0d333080d&quot;,
-      &quot;integrity&quot;: &quot;sha256:b4c04ba88371a8ec01486356cce10eb9fe8274627d8d170aaec87ed0d333080d&quot;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    &quot;ghcr.io/devcontainers/features/docker-in-docker:2&quot;: {
+      &quot;version&quot;: &quot;2.7.1&quot;,
+      &quot;resolved&quot;: &quot;ghcr.io/devcontainers/features/docker-in-docker@sha256:f6a73ee06601d703db7d95d03e415cab229e78df92bb5002e8559bcfc047fec6&quot;,
+      &quot;integrity&quot;: &quot;sha256:f6a73ee06601d703db7d95d03e415cab229e78df92bb5002e8559bcfc047fec6&quot;
&lt;/span&gt;     }
   }
 }
&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt; No newline at end of file
&lt;span class=&quot;gh&quot;&gt;diff --git a/.devcontainer.json b/.devcontainer.json
index e9d9af5..9eb9165 100644
&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;--- a/.devcontainer.json
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+++ b/.devcontainer.json
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@@ -1,6 +1,6 @@&lt;/span&gt;
 {
     &quot;image&quot;: &quot;mcr.microsoft.com/devcontainers/base:jammy&quot;,
     &quot;features&quot;: {
&lt;span class=&quot;gd&quot;&gt;-        &quot;ghcr.io/devcontainers/features/docker-in-docker:1&quot;: {}
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+        &quot;ghcr.io/devcontainers/features/docker-in-docker:2&quot;: {}
&lt;/span&gt;     }
 }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This updater ensures publicly-accessible Features are pinned to the latest version in the associated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer.json&lt;/code&gt; file.  If a dev container has an associated lockfile, that file will also be updated. For more information on lockfiles, see this &lt;a href=&quot;https://github.com/devcontainers/spec/blob/main/docs/specs/devcontainer-lockfile.md&quot;&gt;specification&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Features in any &lt;a href=&quot;https://containers.dev/implementors/spec/#devcontainerjson&quot;&gt;valid dev container location&lt;/a&gt; will be updated in a single pull request.&lt;/p&gt;

&lt;p&gt;Dependabot version updates are free to use for all repositories on GitHub.com.  For more information &lt;a href=&quot;https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/about-dependabot-version-updates#supported-repositories-and-ecosystem&quot;&gt;see the Dependabot version update documentation&lt;/a&gt;.&lt;/p&gt;</content><author><name>[&quot;@joshspicer&quot;]</name></author><summary type="html">We are excited to announce that starting today, in collaboration with the Dependabot Team, the devcontainers package ecosystem is now generally available! Dependabot will now be able to update your public Dev Container Features, keeping them up-to-date with the latest published versions.</summary></entry><entry><title type="html">Speed Up Your Workflow with Prebuilds</title><link href="https://devcontainers.github.io/guide/prebuild" rel="alternate" type="text/html" title="Speed Up Your Workflow with Prebuilds" /><published>2023-08-22T00:00:00+02:00</published><updated>2023-08-22T00:00:00+02:00</updated><id>https://devcontainers.github.io/guide/prebuild</id><content type="html" xml:base="https://devcontainers.github.io/guide/prebuild">&lt;p&gt;Getting dev containers up and running for your projects is exciting - you’ve unlocked environments that include all the dependencies your projects need to run, and you can spend so much more time on coding rather than configuration.&lt;/p&gt;

&lt;p&gt;Once your dev container has everything it needs, you might start thinking more about ways to optimize it. For instance, it might take a while to build. Maybe it takes 5 minutes. Maybe it takes an hour!&lt;/p&gt;

&lt;p&gt;You can get back to working fast and productively after that initial container build, but what if you need to work on another machine and build the container again? Or what if some of your teammates want to use the container on their machines and will need to build it too? It’d be great to make the build time faster for everyone, every time.&lt;/p&gt;

&lt;p&gt;After configuring your dev container, a great next step is to &lt;strong&gt;prebuild your image&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this guide, we’ll explore what it means to prebuild an image and the benefits of doing so, such as speeding up your workflow, simplifying your environment, and pinning to specific versions of tools.&lt;/p&gt;

&lt;p&gt;We have a variety of tools designed to help you with prebuilds. In this guide, we’ll explore two different repos as examples of how our team uses different combinations of these tools:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The prebuilt image for the &lt;a href=&quot;https://github.com/craiglpeters/kubernetes-devcontainer&quot;&gt;Kubernetes repo&lt;/a&gt; developed by one of our spec maintainers &lt;a href=&quot;https://github.com/craiglpeters&quot;&gt;Craig&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;The prebuilt images we host in the &lt;a href=&quot;https://github.com/devcontainers/images/tree/main/src&quot;&gt;devcontainers/images&lt;/a&gt; repo as part of the dev container spec&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;-what-is-prebuilding-&quot;&gt;&lt;a href=&quot;#what-is-a-prebuild&quot; name=&quot;what-is-a-prebuild&quot; class=&quot;anchor&quot;&gt; What is prebuilding? &lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;We should first define: What is prebuilding?&lt;/p&gt;

&lt;p&gt;If you’re already using dev containers, you’re likely already familiar with the idea of building a container, where you package everything your app needs to run into a single unit.&lt;/p&gt;

&lt;p&gt;You need to build your container once it has all the dependencies it needs, and rebuild anytime you add new dependencies. Since you may not need to rebuild often, it might be alright if it takes a while for that initial build. But if you or your teammates need to use that container on another machine, you’ll need to wait for it to build again in those new environments.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The &lt;a href=&quot;/implementors/reference#prebuilding&quot;&gt;dev container CLI doc&lt;/a&gt; is another great resource on prebuilding.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;-prebuilt-codespaces-&quot;&gt;&lt;a href=&quot;#prebuilt-codespaces&quot; name=&quot;prebuilt-codespaces&quot; class=&quot;anchor&quot;&gt; Prebuilt Codespaces &lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;You may have heard (or will hear about) &lt;a href=&quot;https://docs.github.com/en/codespaces/prebuilding-your-codespaces/about-github-codespaces-prebuilds&quot;&gt;GitHub Codespaces prebuilds&lt;/a&gt;. Codespaces prebuilds are similar to prebuilt container images, with some additional focus on the other code in your repo.&lt;/p&gt;

&lt;p&gt;GitHub Codespaces prebuilds help to speed up the creation of new codespaces for large or complex repositories. A prebuild assembles the main components of a codespace for a particular combination of repository, branch, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer.json&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;By default, whenever you push changes to your repository, GitHub Codespaces uses GitHub Actions to automatically update your prebuilds.&lt;/p&gt;

&lt;p&gt;You can learn more about codespaces prebuilds and how to manage them in the &lt;a href=&quot;https://docs.github.com/en/codespaces/prebuilding-your-codespaces/about-github-codespaces-prebuilds&quot;&gt;codespaces docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;-how-do-i-prebuild-my-image-&quot;&gt;&lt;a href=&quot;#how-to&quot; name=&quot;how-to&quot; class=&quot;anchor&quot;&gt; How do I prebuild my image? &lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;We try to make prebuilding an image and using a prebuilt image as easy as possible. Let’s walk through the couple of steps to get started.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prebuilding an image:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Install the &lt;a href=&quot;/implementors/reference&quot;&gt;Dev Container CLI&lt;/a&gt;:&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;   npm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; @devcontainers/cli
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Build your image and push it to a container registry (like the &lt;a href=&quot;https://learn.microsoft.com/azure/container-registry/container-registry-get-started-docker-cli?tabs=azure-cli&quot;&gt;Azure Container Registry&lt;/a&gt;, &lt;a href=&quot;https://docs.github.com/packages/working-with-a-github-packages-registry/working-with-the-container-registry#pushing-container-images&quot;&gt;GitHub Container Registry&lt;/a&gt;, or &lt;a href=&quot;https://docs.docker.com/engine/reference/commandline/push&quot;&gt;Docker Hub&lt;/a&gt;):&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;   devcontainer build &lt;span class=&quot;nt&quot;&gt;--workspace-folder&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--push&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--image-name&lt;/span&gt; &amp;lt;my_image_name&amp;gt;:&amp;lt;optional_image_version&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;You can automate pre-building your image by scheduling the build using a DevOps or continuous integration (CI) service like GitHub Actions. We’ve created a &lt;a href=&quot;https://github.com/marketplace/actions/dev-container-build-and-run-action&quot;&gt;GitHub Action&lt;/a&gt; and &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=devcontainers.ci&quot;&gt;Azure DevOps task&lt;/a&gt; to help with this.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Using a prebuilt image:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Determine the published URL of the prebuilt image you want to use&lt;/li&gt;
  &lt;li&gt;Reference it in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer.json&lt;/code&gt;, Dockerfile, or Docker Compose file
    &lt;ul&gt;
      &lt;li&gt;In our previous guide on &lt;a href=&quot;/guide/dockerfile&quot;&gt;“Using Images, Dockerfiles, and Docker Compose,”&lt;/a&gt; we also showed how you can use prebuilt images, Dockerfiles, or Docker Compose files for your configurations&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;-prebuild-examples-&quot;&gt;&lt;a href=&quot;#prebuild-examples&quot; name=&quot;prebuild-examples&quot; class=&quot;anchor&quot;&gt; Prebuild Examples &lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;As mentioned above, let’s walk through a couple examples of these steps, one using Craig’s &lt;a href=&quot;https://github.com/craiglpeters/kubernetes-devcontainer&quot;&gt;Kubernetes repo&lt;/a&gt;, and the other using our &lt;a href=&quot;https://github.com/devcontainers/images/tree/main/src&quot;&gt;devcontainers/images&lt;/a&gt; repo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kubernetes&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;It’s a fork of the main &lt;a href=&quot;https://github.com/kubernetes/kubernetes&quot;&gt;Kubernetes repo&lt;/a&gt; and contributes a prebuilt dev container for use in the main Kubernetes repo or any other forks&lt;/li&gt;
  &lt;li&gt;The dev container it’s prebuilding is defined in the &lt;a href=&quot;https://github.com/craiglpeters/kubernetes-devcontainer/tree/master/.github/.devcontainer&quot;&gt;.github/.devcontainer folder&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Any time a change is made to the dev container, the repo currently uses the dev container &lt;a href=&quot;https://github.com/craiglpeters/kubernetes-devcontainer/actions/workflows/devcontainer-build-and-push.yml&quot;&gt;GitHub Action&lt;/a&gt; to build the image and push it to GHCR
    &lt;ul&gt;
      &lt;li&gt;You can check out its latest prebuilt image in the &lt;a href=&quot;https://github.com/users/craiglpeters/packages/container/package/kubernetes-devcontainer&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Packages&lt;/code&gt; tab&lt;/a&gt; of its GitHub Repo. In this tab, you can see its GHCR URL is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ghcr.io/craiglpeters/kubernetes-devcontainer:latest&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;The main Kubernetes repo and any fork of it can now define a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.devcontainer&lt;/code&gt; folder and &lt;a href=&quot;https://github.com/craiglpeters/kubernetes-devcontainer/blob/master/.devcontainer/devcontainer.json#L7&quot;&gt;reference this prebuilt image&lt;/a&gt; through: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;image&quot;: &quot;ghcr.io/craiglpeters/kubernetes-devcontainer:latest&quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Dev container spec images&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;This repo prebuilds a variety of dev containers, each of which is defined in their individual folders in the &lt;a href=&quot;https://github.com/devcontainers/images/tree/main/src&quot;&gt;src folder&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;As an example, the Python image is defined in the &lt;a href=&quot;https://github.com/devcontainers/images/tree/main/src/python/.devcontainer&quot;&gt;src/python/.devcontainer&lt;/a&gt; folder&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Any time a change is made to the dev container, the repo uses a &lt;a href=&quot;https://github.com/devcontainers/images/actions/workflows/push.yml&quot;&gt;GitHub Action&lt;/a&gt; to build the image and push it to MCR
    &lt;ul&gt;
      &lt;li&gt;Using the Python image as an example again, its MCR URL is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mcr.microsoft.com/devcontainers/python&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Any projects can now reference this prebuilt image through: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;image&quot;: &quot;mcr.microsoft.com/devcontainers/python&quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;-where-do-the-dependencies-come-from-&quot;&gt;&lt;a href=&quot;#where&quot; name=&quot;where&quot; class=&quot;anchor&quot;&gt; Where do the dependencies come from? &lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;If your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer.json&lt;/code&gt; is as simple as just an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;image&lt;/code&gt; property referencing a prebuilt image, you may wonder: How can I tell what dependencies will be installed for my project? And how can I modify them?&lt;/p&gt;

&lt;p&gt;Let’s walk through the Kubernetes prebuild as an example of how you can determine which dependencies are installed and where:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Start at your end user dev container&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;We start at the &lt;a href=&quot;https://github.com/craiglpeters/kubernetes-devcontainer/blob/master/.devcontainer/devcontainer.json&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.devcontainer/devcontainer.json&lt;/code&gt;&lt;/a&gt; designed for end use in the Kubernetes repo and other forks of it
        &lt;ul&gt;
          &lt;li&gt;It sets a few properties, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hostRequirements&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;onCreateCommand&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;otherPortsAttributes&lt;/code&gt;&lt;/li&gt;
          &lt;li&gt;We see it references a prebuilt image, which will include dependencies that don’t need to be explicitly mentioned in this end user dev container. Let’s next go explore the dev container defining this prebuilt image&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Explore the dev container defining your prebuilt image&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;We next open the config that defines the prebuilt image. This is contained in the &lt;a href=&quot;https://github.com/craiglpeters/kubernetes-devcontainer/tree/master/.github/.devcontainer&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.github/.devcontainer&lt;/code&gt; folder&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;We see there’s a &lt;a href=&quot;https://github.com/craiglpeters/kubernetes-devcontainer/blob/master/.github/.devcontainer/devcontainer.json&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer.json&lt;/code&gt;&lt;/a&gt;. It’s much more detailed than the end user dev container we explored above and includes a variety of &lt;a href=&quot;/implementors/features&quot;&gt;Features&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Explore content in the prebuilt dev container’s config&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Each Feature defines additional functionality&lt;/li&gt;
      &lt;li&gt;We can explore what each of them installs in their associated repo. Most appear to be defined in the &lt;a href=&quot;https://github.com/devcontainers/features/tree/main/src&quot;&gt;devcontainers/features repo&lt;/a&gt; as part of the dev container spec&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Modify and rebuild as desired&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;If I’d like to add more content to my dev container, I can either modify my end user dev container (i.e. the one designed for the main Kubernetes repo), or modify the config defining the prebuilt image (i.e. the content in Craig’s dev container)
        &lt;ul&gt;
          &lt;li&gt;For universal changes that anyone using the prebuilt image should get, update the prebuilt image&lt;/li&gt;
          &lt;li&gt;For more project or user specific changes (i.e. a language I need in my project but other forks won’t necessarily need, or user settings I prefer for my editor environment), update the end user dev container&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;Features are a great way to add dependencies in a clear, easily packaged way&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;-benefits-&quot;&gt;&lt;a href=&quot;#benefits&quot; name=&quot;benefits&quot; class=&quot;anchor&quot;&gt; Benefits &lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;There are a variety of benefits (some of which we’ve already explored) to creating and using prebuilt images:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Faster container startup
    &lt;ul&gt;
      &lt;li&gt;Pull an already built dev container config rather than having to build it freshly on any new machine&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Simpler configuration
    &lt;ul&gt;
      &lt;li&gt;Your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer.json&lt;/code&gt; can be as simple as just an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;image&lt;/code&gt; property&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Pin to a specific version of tools
    &lt;ul&gt;
      &lt;li&gt;This can improve supply-chain security and avoid breaks&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;-tips-and-tricks-&quot;&gt;&lt;a href=&quot;#tips&quot; name=&quot;tips&quot; class=&quot;anchor&quot;&gt; Tips and Tricks &lt;/a&gt;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;We explored the prebuilt images we host as part of the spec in &lt;a href=&quot;https://github.com/devcontainers/images/tree/main/src&quot;&gt;devcontainers/images&lt;/a&gt;. These can form a great base for other dev containers you’d like to create for more complex scenarios&lt;/li&gt;
  &lt;li&gt;The spec has a concept of Development container “Templates” which are source files packaged together that encode configuration for a complete development environment
    &lt;ul&gt;
      &lt;li&gt;A Template may be as simple as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer.json&lt;/code&gt; referencing a prebuilt image, and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer-template.json&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;You can learn more about Templates in our &lt;a href=&quot;../implementors/templates&quot;&gt;Templates documentation&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;You can adopt and iterate on &lt;a href=&quot;../templates.html&quot;&gt;existing Templates&lt;/a&gt; from the spec and community, or you can &lt;a href=&quot;../implementors/templates-distribution&quot;&gt;create and share your own&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;You can include Dev Container configuration and Feature metadata in prebuilt images via &lt;a href=&quot;https://docs.docker.com/config/labels-custom-metadata/&quot;&gt;image labels&lt;/a&gt;. This makes the image self-contained since these settings are automatically picked up when the image is referenced - whether directly, in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FROM&lt;/code&gt; in a referenced Dockerfile, or in a Docker Compose file. You can learn more in our &lt;a href=&quot;/implementors/reference#labels&quot;&gt;reference docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;You can use multi-stage Dockerfiles to create a prod container from your dev container
    &lt;ul&gt;
      &lt;li&gt;You’d typically start with your prod image, then add to it&lt;/li&gt;
      &lt;li&gt;Features provide a quick way to add development and CI specific layers that you wouldn’t use in production&lt;/li&gt;
      &lt;li&gt;For more information and an example, check out our &lt;a href=&quot;https://github.com/orgs/devcontainers/discussions/4#discussioncomment-4152158&quot;&gt;discussion on multi-stage builds&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;-feedback-and-closing-&quot;&gt;&lt;a href=&quot;#feedback&quot; name=&quot;feedback&quot; class=&quot;anchor&quot;&gt; Feedback and Closing &lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;We hope this guide will help you optimize your dev container workflows! We can’t wait to hear your tips, tricks, and feedback. How are you prebuilding your images? Would anything in the spec or tooling make the process easier for you?&lt;/p&gt;

&lt;p&gt;If you haven’t already, we recommend joining our dev container &lt;a href=&quot;https://aka.ms/dev-container-community&quot;&gt;community Slack channel&lt;/a&gt; where you can connect with the dev container spec maintainers and community at large. If you have any feature requests or experience any issues as you use the above tools, please feel free to also open an issue in the corresponding repo in the &lt;a href=&quot;https://github.com/devcontainers&quot;&gt;dev containers org&lt;/a&gt; on GitHub.&lt;/p&gt;</content><author><name>[&quot;@bamurtaugh&quot;, &quot;@craiglpeters&quot;]</name></author><summary type="html">Getting dev containers up and running for your projects is exciting - you’ve unlocked environments that include all the dependencies your projects need to run, and you can spend so much more time on coding rather than configuration.</summary></entry><entry><title type="html">Best Practices: Authoring a Dev Container Feature</title><link href="https://devcontainers.github.io/guide/feature-authoring-best-practices" rel="alternate" type="text/html" title="Best Practices: Authoring a Dev Container Feature" /><published>2023-06-14T00:00:00+02:00</published><updated>2023-06-14T00:00:00+02:00</updated><id>https://devcontainers.github.io/guide/feature-authoring-best-practices</id><content type="html" xml:base="https://devcontainers.github.io/guide/feature-authoring-best-practices">&lt;p&gt;Last November I wrote about the basics around &lt;a href=&quot;/guide/author-a-feature&quot;&gt;authoring a Dev Container Feature&lt;/a&gt;. Since then, &lt;a href=&quot;https://containers.dev/features&quot;&gt;hundreds&lt;/a&gt; of Features have been written by the community. The flexibility of Features has enabled a wide variety of use cases, from installing a single tool to setting up specific aspects of a project’s development environment that can be shared across repositories.  To that effect, many different patterns for Feature authorship have emerged, and the core team has learned a lot about what works well and what doesn’t.&lt;/p&gt;

&lt;h2 id=&quot;utilize-the-test-command&quot;&gt;Utilize the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt; command&lt;/h2&gt;

&lt;p&gt;Bundled with the &lt;a href=&quot;https://github.com/devcontainers/cli&quot;&gt;devcontainer cli&lt;/a&gt; is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer features test&lt;/code&gt; command.  This command is designed to help Feature authors test their Feature in a variety of scenarios.  It is highly recommended that Feature authors use this command to test their Feature before publishing. Some documentation on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt; command can be found &lt;a href=&quot;https://github.com/devcontainers/cli/blob/main/docs/features/test.md&quot;&gt;here&lt;/a&gt;, and an example can be found in the &lt;a href=&quot;https://github.com/devcontainers/feature-starter&quot;&gt;Feature quick start repo&lt;/a&gt;. This repo is updated periodically as new functionality is added to the reference implementation.&lt;/p&gt;

&lt;h2 id=&quot;feature-idempotency&quot;&gt;Feature idempotency&lt;/h2&gt;

&lt;p&gt;The most useful Features are idempotent. This means that if a Feature is installed multiple times with different options (something that will come into play with &lt;a href=&quot;https://github.com/devcontainers/spec/blob/main/proposals/feature-dependencies.md&quot;&gt;Feature Dependencies&lt;/a&gt;), the Feature should be able to handle this gracefully. This is especially important for option-rich Features that you anticipate others may depend on in the future.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;🔧 There is an open spec proposal for installing the same Feature twice in a given &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer.json&lt;/code&gt; &lt;a href=&quot;https://github.com/devcontainers/spec/issues/44&quot;&gt;(devcontainers/spec#44)&lt;/a&gt;.  While the syntax to do so in a given &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer.json&lt;/code&gt; is not yet defined, Feature dependencies will effectively allow for this.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For Features that install a versioned tool (eg: version x of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go&lt;/code&gt; and version y of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ruby&lt;/code&gt; ), a robust Feature should be able to install multiple versions of the tool.  If your tool has a version manager (java’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SDKMAN&lt;/code&gt;, ruby’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rvm&lt;/code&gt;) it is usually as simple as installing the version manager and then running a command to install the desired version of that tool.&lt;/p&gt;

&lt;p&gt;For instances where there isn’t an existing version manager available, a well-designed Feature should consider installing distict versions of itself to a well known location.  A pattern that many Features utilize successfully is writing each version of each tool to a central folder and symlinking the “active” version to a folder on the PATH.&lt;/p&gt;

&lt;p&gt;Features can redefine the PATH variable with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;containerEnv&lt;/code&gt;, like so:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# devcontainer-feature.json&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;&quot;containerEnv&quot;&lt;/span&gt;: &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;PATH&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;/usr/local/myTool/bin:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PATH&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;🔧 A spec proposal is open for simplifying the process of adding a path to the $PATH variable: &lt;a href=&quot;https://github.com/devcontainers/spec/issues/251&quot;&gt;(devcontainers/spec#251)&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To make testing for idempotency easy, &lt;a href=&quot;https://github.com/devcontainers/cli/pull/553&quot;&gt;this change to the reference implementation&lt;/a&gt; introduces a new mode to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer features test&lt;/code&gt; command that will attempt to install a Feature multiple times.  This is useful for testing that a Feature is idempotent, and also for testing that a Feature is able to logically “juggle” multiple versions of a tool.&lt;/p&gt;

&lt;h2 id=&quot;writing-your-install-script&quot;&gt;Writing your install script&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;🔧 Many of the suggestions in this section may benefit from the &lt;a href=&quot;https://github.com/devcontainers/spec/blob/main/proposals/features-library.md&quot;&gt;Feature library/code reuse proposal&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This section includes some tips for the contents of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;install.sh&lt;/code&gt; entrypoint script.&lt;/p&gt;

&lt;h3 id=&quot;detect-platformos&quot;&gt;Detect Platform/OS&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;🔧 A spec proposal is open for detecting the platform/OS and providing better warnings &lt;a href=&quot;https://github.com/devcontainers/spec/issues/58&quot;&gt;(devcontainers/spec#58)&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Features are often designed to work on a subset of possible base images.  For example, the majority of Features in the &lt;a href=&quot;https://github.com/devcontainers/features&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainers/features&lt;/code&gt;&lt;/a&gt; repo are designed to work broadly with debian-derived images.  The limitation is often simply due to the wide array of base images available, and the fact that many Features will use an OS-specific package manager.  To make it easy for users to understand which base images a Feature is designed to work with, it is recommended that Features include a check for the OS and provide a helpful error message if the OS is not supported.&lt;/p&gt;

&lt;p&gt;One possible way to implement this check is shown below.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Source /etc/os-release to get OS info&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Looks something like:&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#     PRETTY_NAME=&quot;Debian GNU/Linux 11 (bullseye)&quot;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#     NAME=&quot;Debian GNU/Linux&quot;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#     VERSION_ID=&quot;11&quot;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#     VERSION=&quot;11 (bullseye)&quot;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#     VERSION_CODENAME=bullseye&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#     ID=debian&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#     HOME_URL=&quot;https://www.debian.org/&quot;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#     SUPPORT_URL=&quot;https://www.debian.org/support&quot;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#     BUG_REPORT_URL=&quot;https://bugs.debian.org/&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; /etc/os-release
&lt;span class=&quot;c&quot;&gt;# Store host architecture&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;architecture&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;dpkg &lt;span class=&quot;nt&quot;&gt;--print-architecture&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;buster bullseye focal bionic xenial&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;VERSION_CODENAME&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;print_error &lt;span class=&quot;s2&quot;&gt;&quot;Unsupported  distribution version '&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;VERSION_CODENAME&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;'. To resolve, either: (1) set feature option '&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;moby&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: false' , or (2) choose a compatible OS distribution&quot;&lt;/span&gt;
    print_error &lt;span class=&quot;s2&quot;&gt;&quot;Supported distributions include:  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you are targeting distros that may not have your desired scripting language installed (eg: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt; is often not installed on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alpine&lt;/code&gt; images), you can either use plain &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/bin/sh&lt;/code&gt; - which is available virtually everywhere - or you can verify (and install) the scripting language in a small bootstrap script as shown below.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/sh &lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# ... &lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# ...&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-ne&lt;/span&gt; 0 &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Script must be run as root. Use sudo, su, or add &quot;USER root&quot; to your Dockerfile before running this script.'&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# If we're using Alpine, install bash before executing&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; /etc/os-release
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;alpine&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;apk add &lt;span class=&quot;nt&quot;&gt;--no-cache&lt;/span&gt; bash
&lt;span class=&quot;k&quot;&gt;fi

&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; /bin/bash &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;dirname&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/main.sh&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Validating functionality against several base images can be done by using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer features test&lt;/code&gt; command with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--base-image&lt;/code&gt; flag, or with a &lt;a href=&quot;https://github.com/devcontainers/cli/blob/main/docs/features/test.md#scenarios&quot;&gt;scenario&lt;/a&gt;.  For example,  one could add a &lt;a href=&quot;https://github.com/devcontainers/features/blob/d934503a050ba84e6b42a006aacd891c4088eb62/.github/workflows/test-all.yaml#L9-L52&quot;&gt;workflow like this to their repo&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Features&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;matrixed&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;images&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;main&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;workflow_dispatch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;continue-on-error&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;strategy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;features&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;anaconda&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;aws-cli&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;azure-cli&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;baseImage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ubuntu:bionic&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ubuntu:focal&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ubuntu:jammy&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;debian:11&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;debian:12&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mcr.microsoft.com/devcontainers/base:ubuntu&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mcr.microsoft.com/devcontainers/base:debian&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/checkout@v3&lt;/span&gt;

      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Install&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;latest&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;devcontainer&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;CLI&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;npm install -g @devcontainers/cli&lt;/span&gt;
        
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Generating&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;tests&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'${{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;matrix.features&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;}}'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;against&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'${{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;matrix.baseImage&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;}}'&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;devcontainer features test  --skip-scenarios -f ${{ matrix.features }} -i ${{ matrix.baseImage }}&lt;/span&gt;
         
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;detect-the-non-root-user&quot;&gt;Detect the non-root user&lt;/h3&gt;

&lt;p&gt;Feature installation scripts are run as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt;.  In contrast, many dev containers have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;remoteUser&lt;/code&gt; set (either implicitly through &lt;a href=&quot;https://containers.dev/implementors/spec/#image-metadata&quot;&gt;image metadata&lt;/a&gt; or directly in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer.json&lt;/code&gt;).  In a Feature’s installation script, one should be mindful of the final user and account for instances where the user is not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Feature authors should take advantage of the &lt;a href=&quot;https://containers.dev/implementors/features/#user-env-var&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_REMOTE_USER&lt;/code&gt; and similar variables&lt;/a&gt; injected during the build.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Install tool in effective remoteUser's bin folder&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_REMOTE_USER_HOME&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/bin&quot;&lt;/span&gt;
curl &lt;span class=&quot;nv&quot;&gt;$TOOL_DOWNLOAD_LINK&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_REMOTE_USER_HOME&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/bin/&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$TOOL&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;chown&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_REMOTE_USER&lt;/span&gt;:&lt;span class=&quot;nv&quot;&gt;$_REMOTE_USER&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_REMOTE_USER_HOME&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/bin/&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$TOOL&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;755 &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_REMOTE_USER_HOME&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/bin/&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$TOOL&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;implement-redundant-pathsstrategies&quot;&gt;Implement redundant paths/strategies&lt;/h3&gt;

&lt;p&gt;Most Features in &lt;a href=&quot;https://containers.dev/features&quot;&gt;the index today&lt;/a&gt; have some external/upstream dependency.  Very often these upstream dependencies can change (ie: versioning pattern, rotated GPG key, etc…) that may cause a Feature to fail to install.  To mitigate this, one strategy is to implement multiple paths to install a given tool (if available).  For example, a Feature that installs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go&lt;/code&gt; might try to install it from the upstream package manager, and if not fall back to a GitHub release.&lt;/p&gt;

&lt;p&gt;Writing several &lt;a href=&quot;https://github.com/devcontainers/cli/blob/main/docs/features/test.md#scenarios&quot;&gt;scenario tests&lt;/a&gt; that force the Feature to go down distinct installation paths will help you catch cases where a given path no longer works.&lt;/p&gt;</content><author><name>[&quot;@joshspicer&quot;]</name></author><summary type="html">Last November I wrote about the basics around authoring a Dev Container Feature. Since then, hundreds of Features have been written by the community. The flexibility of Features has enabled a wide variety of use cases, from installing a single tool to setting up specific aspects of a project’s development environment that can be shared across repositories. To that effect, many different patterns for Feature authorship have emerged, and the core team has learned a lot about what works well and what doesn’t.</summary></entry><entry><title type="html">Working with GitLab CI</title><link href="https://devcontainers.github.io/guide/gitlab-ci" rel="alternate" type="text/html" title="Working with GitLab CI" /><published>2023-02-15T00:00:00+01:00</published><updated>2023-02-15T00:00:00+01:00</updated><id>https://devcontainers.github.io/guide/gitlab-ci</id><content type="html" xml:base="https://devcontainers.github.io/guide/gitlab-ci">&lt;p&gt;For simple use cases you can use your development container (dev container) for CI without much issue. Once you begin using more advanced dev container functionality such as &lt;a href=&quot;/features&quot;&gt;Features&lt;/a&gt;, you will need dev container tooling in your CI pipeline. While GitHub CI has the &lt;a href=&quot;https://github.com/marketplace/actions/devcontainers-ci&quot;&gt;devcontainers-ci GitHub Action&lt;/a&gt;, there is no such analog in GitLab CI. To achieve the goal of using your dev container in GitLab CI, the container must be pre-built.&lt;/p&gt;

&lt;p&gt;This document will guide you on how to build a dev container with GitLab CI, push that dev container to the GitLab Container Registry, and finally reference that dev container in your main project for both local development and GitLab CI.&lt;/p&gt;

&lt;p&gt;For the purpose of this document, we will assume the main project is named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my-project&lt;/code&gt; and lives under the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my-user&lt;/code&gt; path. The example here uses a few &lt;a href=&quot;/features&quot;&gt;Features&lt;/a&gt;, which is what forces the container to be pre-built.&lt;/p&gt;

&lt;h2 id=&quot;the-development-container-gitlab-project&quot;&gt;&lt;a href=&quot;#dev-container-project&quot; name=&quot;dev-container-project&quot; class=&quot;anchor&quot;&gt;The Development Container GitLab project&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;Create a project in GitLab where the stand-alone dev container project will live. As the main project is assumed to be named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my-project&lt;/code&gt;, let’s assume the dev container project name will be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my-project-dev-container&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&quot;development-container-devcontainerdevcontainerjson&quot;&gt;&lt;a href=&quot;#dev-container-json&quot; name=&quot;dev-container-json&quot; class=&quot;anchor&quot;&gt;Development Container .devcontainer/devcontainer.json&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;The example here is a CDK project for Python makes use of both the &lt;a href=&quot;https://github.com/devcontainers/features/tree/main/src/aws-cli&quot;&gt;AWS CLI&lt;/a&gt; and the community-maintained &lt;a href=&quot;http://github.com/devcontainers-contrib/features/tree/main/src/aws-cdk&quot;&gt;AWS CDK&lt;/a&gt; Features.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.devcontainer/devcontainer.json&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;context&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;..&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;dockerfile&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Dockerfile&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;features&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ghcr.io/devcontainers/features/aws-cli:1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ghcr.io/devcontainers-contrib/features/aws-cdk:2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;customizations&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;vscode&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;settings&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;python.formatting.provider&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;black&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;development-container-dockerfile&quot;&gt;&lt;a href=&quot;#dev-container-dockerfile&quot; name=&quot;dev-container-dockerfile&quot; class=&quot;anchor&quot;&gt;Development Container Dockerfile&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;As this is a Python project working with CDK, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; will begin by using the latest Python dev container image and then install some basic packages via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-Dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; mcr.microsoft.com/devcontainers/python:latest&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;pip3 &lt;span class=&quot;nt&quot;&gt;--disable-pip-version-check&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--no-cache-dir&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;aws_cdk_lib constructs jsii pylint &lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-rf&lt;/span&gt; /tmp/pip-tmp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;development-container-gitlab-ciyml&quot;&gt;&lt;a href=&quot;#dev-container-gitlab-ci&quot; name=&quot;dev-container-gitlab-ci&quot; class=&quot;anchor&quot;&gt;Development Container .gitlab-ci.yml&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;Since there is no GitLab CI equivalent to &lt;a href=&quot;https://github.com/marketplace/actions/devcontainers-ci&quot;&gt;devcontainers-ci GitHub Action&lt;/a&gt;, we will need to install the devcontainers CLI manually. The following will:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Install the packages that the devcontainers CLI requires&lt;/li&gt;
  &lt;li&gt;Install the devcontainers CLI itself&lt;/li&gt;
  &lt;li&gt;Login to GitLab Container Repository&lt;/li&gt;
  &lt;li&gt;Build the dev container and push it to the GitLab Container Repository&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gitlab-ci.yml&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;docker:latest&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;DOCKER_TLS_CERTDIR&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/certs&quot;&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;docker:dind&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;before_script&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apk add --update nodejs npm python3 make g++&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;npm install -g @devcontainers/cli&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;docker login -u gitlab-ci-token -p ${CI_JOB_TOKEN} ${CI_REGISTRY}&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;devcontainer build --workspace-folder . --push &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --image-name ${CI_REGISTRY_IMAGE}:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-main-gitlab-project&quot;&gt;&lt;a href=&quot;#main-project&quot; name=&quot;main-project&quot; class=&quot;anchor&quot;&gt;The Main GitLab project&lt;/a&gt;&lt;/h2&gt;

&lt;h3 id=&quot;main-devcontainerdevcontainerjson&quot;&gt;&lt;a href=&quot;#main-project-devcontainer-json&quot; name=&quot;main-project-devcontainer-json&quot; class=&quot;anchor&quot;&gt;Main .devcontainer/devcontainer.json&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.devcontainer/devcontainer.json&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;image&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;registry.gitlab.com/my-user/my-project-dev-container&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;main-gitlabciyml&quot;&gt;&lt;a href=&quot;#main-project-gitlab-ci-json&quot; name=&quot;main-project-gitlab-ci-json&quot; class=&quot;anchor&quot;&gt;Main .gitlab.ci.yml&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;Assuming the dev container project name is based off the main project name, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;${CI_REGISTRY_NAME}&lt;/code&gt; variable can be used. This configuration performs some basic sanity checks and linting once merge requests are submitted.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gitlab-ci.json&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${CI_REGISTRY_IMAGE}-dev-container:latest&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;before_script&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;python --version&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cdk --version&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;stages&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Build&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Lint&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;py_compile&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Build&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;find . -type f -name &quot;*.py&quot; -print | xargs -n1 python3 -m py_compile&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;rules&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$CI_PIPELINE_SOURCE&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;merge_request_event&quot;'&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;cdk synth&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Build&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;JSII_DEPRECATED=fail cdk --app &quot;python3 app.py&quot; synth&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;rules&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$CI_PIPELINE_SOURCE&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;merge_request_event&quot;'&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;Pylint&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Lint&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;pylint *&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;rules&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$CI_PIPELINE_SOURCE&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;merge_request_event&quot;'&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;Black code format&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Lint&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;black --check --diff .&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;rules&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$CI_PIPELINE_SOURCE&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;merge_request_event&quot;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a href=&quot;#conclusion&quot; name=&quot;conclusion&quot; class=&quot;anchor&quot;&gt;Conclusion&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;It’s worth noting that the best practice would be to pin the versions of the various packages installed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apk&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm&lt;/code&gt; and the like. Version pinning was omitted from this guide so that it can be executed as-is without issue.&lt;/p&gt;

&lt;p&gt;The above provides a starting point for a dev container that’s used for both local development and in GitLab CI. It can easily be customized for other languages and tool chains. Take it and make it your own, happy coding!&lt;/p&gt;</content><author><name>[&quot;@raginjason&quot;]</name></author><summary type="html">For simple use cases you can use your development container (dev container) for CI without much issue. Once you begin using more advanced dev container functionality such as Features, you will need dev container tooling in your CI pipeline. While GitHub CI has the devcontainers-ci GitHub Action, there is no such analog in GitLab CI. To achieve the goal of using your dev container in GitLab CI, the container must be pre-built.</summary></entry><entry><title type="html">Using Images, Dockerfiles, and Docker Compose</title><link href="https://devcontainers.github.io/guide/dockerfile" rel="alternate" type="text/html" title="Using Images, Dockerfiles, and Docker Compose" /><published>2022-12-16T00:00:00+01:00</published><updated>2022-12-16T00:00:00+01:00</updated><id>https://devcontainers.github.io/guide/dockerfile</id><content type="html" xml:base="https://devcontainers.github.io/guide/dockerfile">&lt;p&gt;When creating a development container, you have a variety of different ways to customize your environment like &lt;a href=&quot;/features&quot;&gt;“Features”&lt;/a&gt; or &lt;a href=&quot;/implementors/json_reference/#lifecycle-scripts&quot;&gt;lifecycle scripts&lt;/a&gt;. However, if you are familiar with containers, you may want to use a &lt;a href=&quot;/guide/dockerfile#dockerfile&quot;&gt;Dockerfile&lt;/a&gt; or &lt;a href=&quot;/guide/dockerfile#docker-compose&quot;&gt;Docker Compose / Compose&lt;/a&gt; to customize your environment. This article will walk through how to use these formats with the Dev Container spec.&lt;/p&gt;

&lt;h2 id=&quot;--using-a-dockerfile-&quot;&gt;&lt;a href=&quot;#dockerfile&quot; name=&quot;dockerfile&quot; class=&quot;anchor&quot;&gt;  Using a Dockerfile &lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;To keep things simple, many &lt;a href=&quot;/templates&quot;&gt;Dev Container Templates&lt;/a&gt; use container image references.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;image&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;mcr.microsoft.com/devcontainers/base:ubuntu&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;However, &lt;a href=&quot;https://docs.docker.com/engine/reference/builder/&quot;&gt;Dockerfiles&lt;/a&gt; are a great way to extend images, add additional native OS packages, or make minor edits to the OS image. You can reuse any Dockerfile, but let’s walk through how to create one from scratch.&lt;/p&gt;

&lt;p&gt;First, add a file named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; next to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer.json&lt;/code&gt;. For example:&lt;/p&gt;

&lt;div class=&quot;language-Dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; mcr.microsoft.com/devcontainers/base:ubuntu&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Install the xz-utils package&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;apt-get update &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; xz-utils
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, remove the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;image&lt;/code&gt; property from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer.json&lt;/code&gt; (if it exists) and add the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dockerfile&lt;/code&gt; properties instead:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;relative&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;devcontainer.json&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;file.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;dockerfile&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Dockerfile&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it! When you start up your Dev Container, the Dockerfile will be automatically built with no additional work. See &lt;a href=&quot;/implementors/json_reference/#image-specific&quot;&gt;Dockerfile scenario reference&lt;/a&gt; for more information on other related devcontainer.json properties.&lt;/p&gt;

&lt;h3 id=&quot;-iterating-on-an-image-that-includes-dev-container-metadata-&quot;&gt;&lt;a href=&quot;#dockerfile-image-iteration&quot; name=&quot;dockerfile-image-iteration&quot; class=&quot;anchor&quot;&gt; Iterating on an image that includes Dev Container metadata &lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;Better yet, you can can use a Dockerfile as a part of authoring an image you can share with others. You can even &lt;strong&gt;add Dev Container settings and metadata right into the image itself&lt;/strong&gt;. This avoids having to duplicate config and settings in multiple devcontainer.json files and keeps them in sync with your images!&lt;/p&gt;

&lt;p&gt;See the guide on &lt;strong&gt;&lt;a href=&quot;/guide/prebuild&quot;&gt;pre-building&lt;/a&gt;&lt;/strong&gt; to learn more!&lt;/p&gt;

&lt;h2 id=&quot;--using-docker-compose-&quot;&gt;&lt;a href=&quot;#docker-compose&quot; name=&quot;docker-compose&quot; class=&quot;anchor&quot;&gt;  Using Docker Compose &lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.docker.com/compose/&quot;&gt;Docker Compose&lt;/a&gt; is a great way to define a multi-container development environment. Rather than adding things like databases or redis to your Dockerfile, you can reference existing images for these services and focus your Dev Container’s content on tools and utilities you need for development.&lt;/p&gt;

&lt;h3 id=&quot;-using-an-image-with-docker-compose-&quot;&gt;&lt;a href=&quot;#docker-compose-image&quot; name=&quot;docker-compose-image&quot; class=&quot;anchor&quot;&gt; Using an image with Docker Compose &lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;As mentioned in the Dockerfile section, to keep things simple, many &lt;a href=&quot;/templates&quot;&gt;Dev Container Templates&lt;/a&gt; use container image references.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;image&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;mcr.microsoft.com/devcontainers/base:ubuntu&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt; file next to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer.json&lt;/code&gt; that references the same image and includes a PostgreSQL database:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;3.8'&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;devcontainer&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mcr.microsoft.com/devcontainers/base:ubuntu&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;../..:/workspaces:cached&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;network_mode&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;service:db&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;sleep infinity&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres:latest&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;restart&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;unless-stopped&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres-data:/var/lib/postgresql/data&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;POSTGRES_USER&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;POSTGRES_DB&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;postgres-data&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this example:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;../..:/workspaces:cached&lt;/code&gt; mounts the workspace folder from the local source tree into the Dev Container.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;network_mode: service:db&lt;/code&gt; puts the Dev Container on the same network as the database, so that it can access it on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db&lt;/code&gt; section uses the &lt;a href=&quot;https://hub.docker.com/_/postgres&quot;&gt;Postgres&lt;/a&gt; image with a few settings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, let’s configure devcontainer.json to use it.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;dockerComposeFile&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;docker-compose.yml&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;service&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;devcontainer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;workspaceFolder&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/workspaces/${localWorkspaceFolderBasename}&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this example:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;service&lt;/code&gt; indicates which service in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt; file is the Dev Container.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dockerComposeFile&lt;/code&gt; indicates where to find the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt; file.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;workspaceFolder&lt;/code&gt; indicates where to mount the workspace folder. This corresponds to a sub-folder under the mount point from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;../..:/workspaces:cached&lt;/code&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt; file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s it!&lt;/p&gt;

&lt;h3 id=&quot;-using-a-dockerfile-with-docker-compose-&quot;&gt;&lt;a href=&quot;#docker-compose-dockerfile&quot; name=&quot;docker-compose-dockerfile&quot; class=&quot;anchor&quot;&gt; Using a Dockerfile with Docker Compose &lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;You can also combine these scenarios and use Dockerfile with Docker Compose. This time we’ll update &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt; to reference the Dockerfile by replacing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;image&lt;/code&gt; with a similar &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build&lt;/code&gt; section:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;3.8'&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;devcontainer&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; 
      &lt;span class=&quot;na&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;.&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;dockerfile&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Dockerfile&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;../..:/workspaces:cached&lt;/span&gt;      
    &lt;span class=&quot;na&quot;&gt;network_mode&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;service:db&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;sleep infinity&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres:latest&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;restart&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;unless-stopped&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres-data:/var/lib/postgresql/data&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;POSTGRES_USER&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;POSTGRES_DB&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;postgres-data&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, as in the Dockerfile example, you can use this same setup to create a Dev Container image that you can share with others. You can also add Dev Container settings and metadata right into the image itself.&lt;/p&gt;

&lt;p&gt;See the guide on &lt;strong&gt;&lt;a href=&quot;/guide/prebuild&quot;&gt;pre-building&lt;/a&gt;&lt;/strong&gt; to learn more!&lt;/p&gt;</content><author><name>[&quot;@chuxel&quot;]</name></author><summary type="html">When creating a development container, you have a variety of different ways to customize your environment like “Features” or lifecycle scripts. However, if you are familiar with containers, you may want to use a Dockerfile or Docker Compose / Compose to customize your environment. This article will walk through how to use these formats with the Dev Container spec.</summary></entry><entry><title type="html">Authoring a Dev Container Feature</title><link href="https://devcontainers.github.io/guide/author-a-feature" rel="alternate" type="text/html" title="Authoring a Dev Container Feature" /><published>2022-11-01T00:00:00+01:00</published><updated>2022-11-01T00:00:00+01:00</updated><id>https://devcontainers.github.io/guide/author-a-feature</id><content type="html" xml:base="https://devcontainers.github.io/guide/author-a-feature">&lt;p&gt;Development container &lt;a href=&quot;/features&quot;&gt;“Features”&lt;/a&gt; are self-contained, shareable units of installation code and development container configuration. We &lt;a href=&quot;/implementors/features-distribution&quot;&gt;define a pattern&lt;/a&gt; for authoring and self-publishing Features.&lt;/p&gt;

&lt;p&gt;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 &lt;a href=&quot;https://github.com/devcontainers/feature-starter&quot;&gt;devcontainers/feature-starter&lt;/a&gt; repo as well.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;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 &lt;a href=&quot;https://oras.land/docs/compatible_oci_registries#registries-supporting-oci-artifacts&quot;&gt;OCI Artifact supporting&lt;/a&gt; container registry instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;-create-a-repo-&quot;&gt;&lt;a href=&quot;#create-repo&quot; name=&quot;create-repo&quot; class=&quot;anchor&quot;&gt; Create a repo &lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;Start off by creating a repository to host your Feature. In this guide, we’ll use a public GitHub repository.&lt;/p&gt;

&lt;p&gt;For the simplest getting started experience, you may use our example &lt;a href=&quot;https://github.com/devcontainers/feature-starter&quot;&gt;feature-starter&lt;/a&gt; repo. You may select the green &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Use this template&lt;/code&gt; button on the repo’s page.&lt;/p&gt;

&lt;p&gt;You may also &lt;a href=&quot;https://docs.github.com/en/get-started/quickstart/create-a-repo&quot;&gt;create your own repo on GitHub&lt;/a&gt; if you’d prefer.&lt;/p&gt;

&lt;h2 id=&quot;-create-a-folder-&quot;&gt;&lt;a href=&quot;#create-folder&quot; name=&quot;create-folder&quot; class=&quot;anchor&quot;&gt; Create a folder &lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;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 &lt;a href=&quot;https://github.com/devcontainers/feature-starter/tree/main/src&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src&lt;/code&gt;&lt;/a&gt; folder.&lt;/p&gt;

&lt;p&gt;If you’d like to create multiple Features, you may add multiple folders within &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;-add-files-&quot;&gt;&lt;a href=&quot;#add-files&quot; name=&quot;add-files&quot; class=&quot;anchor&quot;&gt; Add files &lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;At a minimum, a Feature will include a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer-feature.json&lt;/code&gt; and an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;install.sh&lt;/code&gt; entrypoint script.&lt;/p&gt;

&lt;p&gt;There are many possible properties for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer-feature.json&lt;/code&gt;, which you may review in the &lt;a href=&quot;/features#devcontainer-feature-json-properties&quot;&gt;Features spec&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Below is a hello world example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer-feature.json&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;install.sh&lt;/code&gt;. You may review the &lt;a href=&quot;https://github.com/devcontainers/features/blob/main/src&quot;&gt;devcontainers/features&lt;/a&gt; repo for more examples.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/devcontainers/feature-starter/blob/main/src/hello/devcontainer-feature.json&quot;&gt;devcontainer-feature.json&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Hello, World!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;1.0.2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;A hello world feature&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;options&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;greeting&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;proposals&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;hey&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;hi&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;howdy&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;hey&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Select a pre-made greeting, or enter your own&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/devcontainers/feature-starter/blob/main/src/hello/install.sh&quot;&gt;install.sh&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/sh&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Activating feature 'hello'&quot;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;GREETING&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;GREETING&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The provided greeting is: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$GREETING&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; /usr/local/bin/hello &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
#!/bin/sh
RED='&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\0&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;33[0;91m'
NC='&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\0&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;33[0m' # No Color
echo &quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;{RED}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;GREETING&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;(whoami)!&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;{NC}&quot;
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF

&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chmod&lt;/span&gt; +x /usr/local/bin/hello
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;-publishing-&quot;&gt;&lt;a href=&quot;#publishing&quot; name=&quot;publishing&quot; class=&quot;anchor&quot;&gt; Publishing &lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;feature-starter&lt;/code&gt; repo contains a GitHub Action &lt;a href=&quot;https://github.com/devcontainers/feature-starter/blob/main/.github/workflows/release.yaml&quot;&gt;workflow&lt;/a&gt; that will publish each feature to GHCR. By default, each feature will be prefixed with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;owner/&amp;lt;repo&amp;gt;&lt;/code&gt; namespace. Using the hello world example from above, it can be referenced in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer.json&lt;/code&gt; with: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ghcr.io/devcontainers/feature-starter/color:1&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Note: You can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer features publish&lt;/code&gt; command from the &lt;a href=&quot;https://github.com/devcontainers/cli&quot;&gt;Dev Container CLI&lt;/a&gt; if you are not using GitHub Actions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;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 &lt;a href=&quot;#add-to-index&quot;&gt;crawl&lt;/a&gt; metadata about available Features in the collection without downloading &lt;em&gt;all the Features individually&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;By default, GHCR packages are marked as private. To stay within the free tier, Features need to be marked as public.&lt;/p&gt;

&lt;p&gt;This can be done by navigating to the Feature’s “package settings” page in GHCR, and setting the visibility to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;public&lt;/code&gt;. The URL may look something like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://github.com/users/&amp;lt;owner&amp;gt;/packages/container/&amp;lt;repo&amp;gt;%2F&amp;lt;featureName&amp;gt;/settings
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img style=&quot;max-width:70%;height:auto&quot; alt=&quot;Changing package visibility to public&quot; src=&quot;/img/make-package-public.png&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;-adding-features-to-the-index-&quot;&gt;&lt;a href=&quot;#add-to-index&quot; name=&quot;add-to-index&quot; class=&quot;anchor&quot;&gt; Adding Features to the Index &lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;If you’d like your Features to appear in our &lt;a href=&quot;/features&quot;&gt;public index&lt;/a&gt; so that other community members can find them, you can do the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Go to &lt;a href=&quot;https://github.com/devcontainers/devcontainers.github.io&quot;&gt;github.com/devcontainers/devcontainers.github.io&lt;/a&gt;, which is the GitHub repo backing &lt;a href=&quot;https://containers.dev/&quot;&gt;containers.dev&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Open a PR to modify the &lt;a href=&quot;https://github.com/devcontainers/devcontainers.github.io/blob/gh-pages/_data/collection-index.yml&quot;&gt;collection-index.yml&lt;/a&gt; file&lt;/li&gt;
  &lt;li&gt;Features housed in other OCI Artifact container registries can be included as long as they can be downloaded without a login.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feature collections are scanned to populate a Feature index on the &lt;a href=&quot;/features&quot;&gt;containers.dev site&lt;/a&gt; and allow them to appear in Dev Container creation UX in &lt;a href=&quot;https://containers.dev/supporting&quot;&gt;supporting tools&lt;/a&gt; like &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers&quot;&gt;VS Code Dev Containers&lt;/a&gt; and &lt;a href=&quot;https://github.com/features/codespaces&quot;&gt;GitHub Codespaces&lt;/a&gt;.&lt;/p&gt;</content><author><name>[&quot;@joshspicer&quot;]</name></author><summary type="html">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.</summary></entry></feed>