Last time I mentioned that infrastructure-as-code, in the form of a scriptable build environment, is the solution to special snowflake build machines, as well as a host of other problems.

Let’s explore this a bit more, and discuss one specific way of achieving this state of nirvana.

What I’m proposing is that you determine absolutely everything that is needed to build all of your software artifacts. Compilers, installers, libraries, code packages, repositories, everything.

Write a command-line script that installs every. single. one. of those components, so that starting from a fresh OS install, you have everything you need to build your software after running this script. If you want to break it up into multiple scripts for modularity, fine.

Then, use this script to install all your build prerequisites onto a Virtual Machine running a fresh installation of the OS you use to build your software.

The easiest, most automatic way of doing this is with Vagrant.

I’m not going to teach you how to use Vagrant in this post, but once you’ve gone through this exercise, setting up a build environment on a fresh machine is as easy as:

  1. installing vagrant
  2. installing VM software like VirtualBox or VMWare
  3. cloning your code repository
  4. typing vagrant up on the command line, and…
  5. waiting for it to be ready

The first time, it takes a while. Do it before lunch for most low-level embedded development projects. If you are setting up an environment for an embedded linux build using Buildroot or Yocto, then let Vagrant chew on it overnight.

Quick aside: are you using Yocto for building your embedded linux image? You probably shouldn’t be. Most teams don’t need the extra flexibility of Yocto, and the complexity is a nightmare. Buildroot is simpler. Simpler is better.

OK, back to our regularly scheduled programming.

The second, third, fourth, and every other time you run vagrant up, the VM comes up in seconds. Boom, you’re ready to go.


Old developers, new developers, QA testers, your build servers – everyone uses the same environment to build, run, and test the code.

It doesn’t even matter if you have some developers who like OSX, some who like Linux, and some who like Windows. Vagrant and VirtualBox run on all platforms.

I’ve even set up development environments in such a virtual machine, where I write and debug embedded software in a pre-configured IDE. This isn’t necessary, and some developers will balk at it, since they prefer their own code editors and debugging tools. Fine. They can do that. But, building the software? Everyone does it the same way.

Plus, the installation script is the best kind of documentation for how to set up your build environment. It is fully complete, by definition.


As always, it’s not all puppies and unicorn sparkles.

It takes an investment to set this up the first time. But, this is a stupid objection. It’s a net time-saver in the long run, without a doubt.

Builds can take longer because everything executes inside a virtual machine instead of on the host. This is getting better over time, but it’s certainly noticeable.

Sharing code and build artifacts between the host OS and the VM can be tricky. This is particularly a problem on a Windows host.

Sharing code repository credentials between the host OS and the VM can be even more tricky. But, solveable by a persistent engineer.


Another solution for describing repeatable build environments is Docker. Rather than running a separate instance of an operating system within a virtual machine, the build software executes within an isolated container on your host machine.

I haven’t used Docker much before, but I’m exploring it now and will discuss it more after I’ve had a chance to evaluate it for embedded development.

Got questions? Reach out and let me know.

Happy developing!