22 Aug 2023 - @bamurtaugh @craiglpeters
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.
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!
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.
After configuring your dev container, a great next step is to prebuild your image.
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.
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:
We should first define: What is prebuilding?
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.
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.
Note: The dev container CLI doc is another great resource on prebuilding.
You may have heard (or will hear about) GitHub Codespaces prebuilds. Codespaces prebuilds are similar to prebuilt container images, with some additional focus on the other code in your repo.
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 devcontainer.json file.
By default, whenever you push changes to your repository, GitHub Codespaces uses GitHub Actions to automatically update your prebuilds.
You can learn more about codespaces prebuilds and how to manage them in the codespaces docs.
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.
Prebuilding an image:
Install the Dev Container CLI:
npm install -g @devcontainers/cli
Build your image and push it to a container registry (like the Azure Container Registry, GitHub Container Registry, or Docker Hub):
devcontainer build --workspace-folder . --push true --image-name <my_image_name>:<optional_image_version>
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 GitHub Action and Azure DevOps task to help with this.
Using a prebuilt image:
devcontainer.json, Dockerfile, or Docker Compose file
As mentioned above, let’s walk through a couple examples of these steps, one using Craig’s Kubernetes repo, and the other using our devcontainers/images repo.
Kubernetes
Packages tab of its GitHub Repo. In this tab, you can see its GHCR URL is ghcr.io/craiglpeters/kubernetes-devcontainer:latest.devcontainer folder and reference this prebuilt image through: "image": "ghcr.io/craiglpeters/kubernetes-devcontainer:latest"Dev container spec images
mcr.microsoft.com/devcontainers/python"image": "mcr.microsoft.com/devcontainers/python"If your devcontainer.json is as simple as just an image 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?
Let’s walk through the Kubernetes prebuild as an example of how you can determine which dependencies are installed and where:
.devcontainer/devcontainer.json designed for end use in the Kubernetes repo and other forks of it
hostRequirements, onCreateCommand, and otherPortsAttributes.github/.devcontainer folder devcontainer.json. It’s much more detailed than the end user dev container we explored above and includes a variety of Features There are a variety of benefits (some of which we’ve already explored) to creating and using prebuilt images:
devcontainer.json can be as simple as just an image propertydevcontainer.json referencing a prebuilt image, and a devcontainer-template.jsonFROM in a referenced Dockerfile, or in a Docker Compose file. You can learn more in our reference docs 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?
If you haven’t already, we recommend joining our dev container community Slack channel 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 dev containers org on GitHub.