Apple Just Built WSL for the Mac (Container Machines)
BBetter Stack
Computing/SoftwareInternet Technology
Transcript
00:00:00Hidden behind all of the Apple intelligence stuff at WWDC this year,
00:00:03Apple quietly released their own version of Windows subsystem for Linux called Container
00:00:06Machines. These give you a lightweight persistent Linux environment on your Mac in a really easy
00:00:10to use way, and they're actually built on top of Apple's container project which they
00:00:14released last year, which is a Docker alternative, all of which of course is optimized for Apple
00:00:18Silicon. So let's take a look at what container machines are, how they work,
00:00:21and also do a quick recap of Apple containers for everyone who missed them.
00:00:29So I'm going to start by setting up a container machine, and then I'll talk through how all of
00:00:32this is working in a bit. But the one I want is going to be a Ubuntu Linux environment. So I simply
00:00:37have a Docker file here with the Ubuntu image, and then some things in here to install some common
00:00:41tools. This will work with any OCI compatible image, so pretty much all of the ones that you
00:00:46have working with Docker. The only thing it needs to include to be a VM is the system initialization
00:00:50program. Once we have the Docker file image that we want to use for our VM, all we need to do is
00:00:54build this using Apple's container tool. So you can see this is the command I'm using for mine,
00:00:58since I have the Docker file in this folder, and I'm simply going to tag this as local
00:01:01Ubuntu machine, and we can go ahead and hit enter on that and build. The container tool,
00:01:05by the way, works on macOS 26 and above, and you can install it from GitHub by simply going to the
00:01:09repo, going to releases, and downloading the latest package. It looks like the build of my image is
00:01:13done here, and you can see it's very similar to Docker. It's just building an OCI image.
00:01:17That's all we need for a container machine, so now we can simply run container machine create,
00:01:21say the image that we want for our container machine, give it a friendly name,
00:01:24and I'm also going to set this as default, so any command that I run is going to assume
00:01:27I'm on about this container machine, and I don't have to specify it by name. With this we can hit
00:01:31enter, and in literally seconds it is all set up. We can see a bit of information about the
00:01:35container machine that I just created by doing container machine list. Here you can see it has
00:01:38the Ubuntu one that I just created, the IP address, 7 CPUs, and 18 gigabytes of memory. CPU and memory
00:01:44are configurable by the way, but by default it will use half of the memory of your Mac. To actually get
00:01:48using your container machine though, all you need to do is container machine run,
00:01:51and you can leave this blank if you want to enter the interactive terminal, or you can actually just
00:01:54add a command after that if you want to run that on your Linux machine. In this case you can see I
00:01:58just hit enter, and now I'm on an interactive terminal on my Linux environment. We can confirm
00:02:02this by doing something like uname-a, and you see this is printing back that it's Linux Ubuntu,
00:02:06as opposed to when I run this on my Mac and we get back Darwin. Now one of the cool things about
00:02:10container machines is it's got automatic user sharing, so my user has already been copied from my Mac
00:02:14onto my Linux environment, and the same actually goes for your home directory. This will mount your
00:02:18entire home directory as read-write, so I have access in this Linux environment to all of the files that I
00:02:23have on my Mac. You can see where I actually ran container run, it's put me straight into that file
00:02:27in the Linux environment, and we already have those files in there. It does also have its own volume
00:02:31though, so if we navigate to the home directory of this Ubuntu machine, you can see there's currently
00:02:35nothing in there, even though there is on my Mac, and that is because this one is the Linux
00:02:39environment, and this is where you put your .files that are specific to Linux. The folder sharing
00:02:43makes it super easy to develop something on your Mac using all of your normal tools, and maybe even some
00:02:48that are only compatible with macOS, and then simply switch to Linux when you need to test something.
00:02:52For example, I have a very simple BUN application here, and I want to compile this into a single
00:02:56executable that will work on Linux, but I can't actually test Linux on my macOS, so running this,
00:03:01I don't know whether it's worked or not. If we switch over to the container machine though,
00:03:04you can see I can simply run the command straight away. I don't have to transfer files or anything,
00:03:08thanks to the fact that it shares the same file system. If I hit enter here, it works nicely.
00:03:12This application was just a very simple web server with this web page here, which prints what it's
00:03:16running on, so it's currently running on Ubuntu 24. You can also see there's a subscribe going
00:03:20around, something you should definitely do. Now I was just testing out running the BUN
00:03:23development server on the Linux environment, and it does all work, which we can see here,
00:03:27it's running on BUN dev, and it's not compiled. But if I modify one of the source files from my Mac
00:03:31here, maybe say hello instead of subscribe, I am noticing that the hot reloading doesn't seem to be
00:03:35picking up on that behaviour, and I have to restart the BUN development server to get the changes to
00:03:39apply. There we go, now it says hello. I think hot reloading will work the same way that breakpoints
00:03:43do, where they don't actually work if you're on your macOS code version, but what you can do is get
00:03:47your editor to connect to the container machine via SSH, and then edit the files that way, and that
00:03:52way breakpoints and hot reloading will probably work. They actually have a tutorial for how to do this
00:03:55in their documentation. That's basically all there is to show you when it comes to actually using a
00:03:59container machine. I mean, it's just a Ubuntu environment now, and honestly the whole experience
00:04:03is pretty seamless. It's also worth pointing out that you're not limited to just one machine. You could
00:04:08have an Alpine machine, a Ubuntu one, and a Debian one sitting side by side, so you have one distro
00:04:12per target, and honestly it's very nice if you're doing cross-target work. Plus, because these machines
00:04:17can actually run a real SystemD, you can test a proper service stack in there, like having Postgres
00:04:22running as an actual service with your app next to it, and the whole thing is going to behave like the
00:04:26Linux server that you're going to deploy to. The simplicity is one of the core design principles that
00:04:30Apple was pushing for when developing container machines. They wanted fast, lightweight VMs that
00:04:34integrate into your existing workflow and are super easy to spin up as needed, as well as be persistent
00:04:39over time so that you can set up a whole dev environment VM that has all of the tools that
00:04:42you normally need ready for when you need them. Again, it's pretty similar to what Windows subsystem
00:04:47for Linux was trying to achieve. As for how all of this is built and how it compares to Docker and
00:04:51OrbStack, we first need to understand the container tool that was released last year. This is written
00:04:55in Swift and it's meant to be a Docker alternative that can run containers, and it runs any standard
00:04:59OCI image, so anything that you can pull from Docker Hub is still going to work. The unique thing about
00:05:04Apple's approach though is that every container got its own lightweight virtual machine through their
00:05:08virtualization framework, rather than a bunch of containers sharing one big Linux VM, which is what
00:05:13Docker Desktop does. Some benefits of that approach can be security, since each container has the
00:05:17isolation properties of a full VM. Then there's also privacy, since you're only mounting the necessary
00:05:22data into each VM, whereas when you have a shared VM, you actually mount all of the data into that
00:05:27shared VM, so it can be mounted selectively into the individual containers. Finally, there can also be
00:05:31a performance benefit, since containers created using Apple Container actually require less memory than a
00:05:36full VM, and the boot times are pretty similar to Docker and other tools. If we actually look at some
00:05:41benchmarks that RepoFlow did here, comparing Apple Containers with OrbStack and Docker Desktop, we can see the
00:05:46results aren't actually too bad. It's hard to tell here, but Apple Containers actually does achieve the
00:05:50most single-threaded CPU events, but OrbStack was pretty close, there is fractions in it, and that
00:05:55same story continues when we go to multi-threaded as well, they all perform very well. Where Apple does
00:06:00seem to pull a bit of a lead though is in memory throughput, with OrbStack coming in second and Docker
00:06:04Desktop last, but when it comes to startup times for a tiny container, it seems that Apple does have some
00:06:09work left to do here, but it is still sub-second, it's just Docker Desktop and OrbStack do it in less
00:06:14than a quarter of a second. There's loads more benchmarks here, so I'll leave a link to this,
00:06:17but basically the rest of them show that OrbStack has exceptional file system and small file performance,
00:06:22but they also show that Apple Containers is pretty much level, if not better than Docker Desktop.
00:06:27There is a few catches though that you'll want to be aware of before you use container machines,
00:06:30and the first one is memory. As I mentioned earlier, the machine defaults to half of your system RAM,
00:06:35so it is worth knowing that it actually never gives this back. So if you have a memory-intensive
00:06:39workload in the container, maybe during a big build, that memory is actually held until you
00:06:43restart the machine. This is actually one of the unique benefits of OrbStack, where it has dynamic
00:06:47memory, which reduces the memory usage by releasing that unused memory back to macOS. As far as I know,
00:06:53nothing else does this. Second, there's also no GPU and USB pass-through. I have seen open issues
00:06:57for both of these on that GitHub though, so maybe it'll be supported in the future. Third, it also
00:07:02seems a little complex to get GUI apps running, like maybe if you wanted to run the Linux version of
00:07:06VS Code or other Linux-only apps. It's definitely not a seamless experience, I'd probably use something else for
00:07:11this. Finally, there's also a security trade-off because, as I mentioned earlier, that home directory
00:07:15mount that makes everything so convenient is read-write by default, which means that anything that you run
00:07:20inside of that Linux machine can touch your SSH keys, your cloud credentials, and everything on
00:07:25your Mac. It seems you can actually only set the mount to read-only or turn it off entirely. There
00:07:29doesn't seem to be an ability to only mount a specific folder. Overall, having tried out Apple
00:07:33container machines, I'm probably going to stick to OrbStack as it feels like the more polished option
00:07:37today with better resource management and more features, but I know some people don't like that
00:07:40OrbStack is paid for if you want business and commercial use, so without OrbStack, I probably
00:07:45would choose Apple Containers over Docker Desktop, and there's also Klima, which is another great
00:07:49alternative. What do you use? Is it OrbStack, Docker Desktop, or Lima? Let me know in the comments down
00:07:53below, while you're there to subscribe, and as always, see you in the next one.