# Contents

One crucial aspect of development environments is quick launch times. If opening remote dev environments feels slower than a local terminal, there will be friction in adopting the tool. 

Efficient launch times are essential for maintaining developer productivity and minimizing delays in the development process. In Daytona, we overcame this challenge by leveraging pre-built images for our dev containers, ensuring faster launch times and consistent environments across projects.

In this post, I'll share some valuable insights I've gained from optimizing dev container build times in my own projects.

  • How to enhance the efficiency of development environments by using pre-built dev containers.

  • Flexibility and potential downsides of dev containers, offers a solution to reduce build times, and highlights the benefits for both internal developers and the wider community.

The Lego-Like Flexibility of Dev Containers

A great feature of dev containers is the ability to take a Lego-like approach, layering various base images like Ubuntu, Debian, Alpine, or language-specific images, then adding features on top like Docker, AWS CLI, GitHub CLI, and more. This modularity allows developers to customize their environments to meet specific needs without starting from scratch each time.

Each feature layer not only installs the tool but also configures VS Code appropriately, bringing in relevant extensions and settings. For instance, the Go feature adds Go plugins and configurations, making it seamless for developers to start coding immediately without additional setup.

The Pitfall of Layering Features

While the flexibility of layering features is powerful, it can also lead to slower build times if not managed carefully. Each feature is essentially a set of bash script that get executed during the build process. As you add more features, these scripts can compound and significantly increase your build times.

In the case of Daytona, our environments include Go, Node, and a JDK built on top of a custom Ubuntu image. This resulted in long build times, approximately 4.5 minutes, every time you would spin up a fresh environment.

1devcontainer up --workspace-folder
3~ 4.78s user 2.86s system 2% cpu 4:28.72 total

Worse yet, we have a plugin architecture where every plugin has its own devcontainer.json. This meant that every time we updated something in the toolchain, we would need to update it in all the repositories. Each time you open a fresh environment core or plugin, you would have to wait 4.5 minutes for the environment to build. It added up quickly, causing significant delays.

The Solution: Pre-Built Base Images

The solution was to use the dev container tools to build a single base image with everything pre-installed and configured. While the image was still relatively large (approximately 1.8GB), the initial pull took under a minute on a 1 Gbps connection. Subsequent launches were nearly instant at less than 10 seconds, compared to 4-5 minutes previously.

Additionally, maintaining this base image centrally ensures consistent environments across all of our projects and plugins, including community plugins. Updates propagate automatically for everyone without repetitive config management, streamlining the update process. Pre-built images allowed us to speed up developer cycles, while simultaneously keeping us more consistent across more than 30 projects that we have. 

Daytona Example and Community Benefits 

We encountered this issue firsthand while working on Daytona. As the number of Daytona plugins grows, we expect the number of projects to grow exponentially. Many of those projects are being built by our community. As long as they use our dev environment, they don't have to pay attention to updates. They can simply use the dev container and get the same environment we have. When we update, their environments update automatically.

As a bonus, we were able to combine the dev container tools with Docker BuildKit to support multi-platform builds for both x86 and ARM. Even if users run our images locally on their MacBook Air laptops with M chips, they’ll work seamlessly. This flexibility ensures that all team members and community members, regardless of their hardware, can enjoy the same streamlined development experience.

Finding the Right Balance

In summary, while dev container features are convenient, pre-building a base image significantly improves launch times, especially for larger projects with multiple dependencies. It's worth considering this tradeoff between setup time and launch speed. Pre-builds can further optimize by including the codebase and compilation steps, reducing the need for repetitive builds.

The key is finding the right balance of layering to make development environments as snappy as possible without sacrificing too much setup complexity. By leveraging the strengths of dev containers and pre-built images, development teams and the broader community can enjoy faster, more efficient workflows, ultimately enhancing productivity and reducing downtime.

  • dev container
  • prebuilds