Using LXD on your Chromebook


Starting with Chrome OS 69, a new feature called Linux Apps was introduced.
This allows Chrome OS users to install normal Linux applications from the Debian repository and have them integrate with the underlying Chrome OS desktop.

The feature has evolved quite a bit since its introduction, including recent work on getting sound, USB and even GPU acceleration to those applications.

Getting started

On a supported Chromebook, search for “Terminal” in your application list.

Selecting it will trigger installation of the Linux Apps support package:

Once installed, you get presented with a terminal emulator on a system called penguin:

From there, you can install regular Debian packages:

When installing something that comes with a GUI, the appropriate launcher will be added to your Chrome OS launcher. Click on it, and it’ll start the program:

Pretty nice and simple, even comes with integration in the file explorer!

Under the hood

Where it gets interesting is how this was all put together.

Chrome OS is designed to be extremely safe, as much of the system as possible is read-only with the majority of user data stored in the cloud and synced as needed.

So how is it that you can now get root access and install whatever you want on your Chromebook?

Well, containers, LXD containers specifically.
When you install the Linux apps support, a small read-only virtual machine is setup (it’s called termina), LXD runs inside that virtual machine and is provided some amount of persistent storage for the containers.

A default container, called penguin is then created for you from a small Debian based image distributed by Google. That container is passed a number of devices and sockets so it can interact with the Chrome OS desktop.

ChromeOS itself also knows how to interact with the container to pull things like the list of installed desktop applications, access the files stored in the container, …

Using LXD directly

So the default setup is nice and all, but this all means that you actually do have a working LXD daemon on your Chromebook, so can’t you start more containers, run different Linux distributions, … ?
Yes, yes you can.

The first thing you’ll want to do is get into the VM itself rather than inside the container.
To do so, you need to get a crosh terminal by pressing <ctrl>+<alt>+t, this will look like this:

Now run: vmc start termina
This will get you a shell inside the termina virtual machine.
From there, you can directly interact with LXD:

As you can see, there is that penguin container running in there.

And you can launch a few more containers of various Linux distributions and interact with LXD as you normally would on any system.

Getting the LXD client tool inside the penguin container

Having to do that crosh and vmc dance every time you want to interact with LXD may get a bit annoying after a while. Not to mention that termina is seriously locked down so it’s not a particularly great environment to work from.

As LXD can be driven over the network, how about we access it from that penguin container instead?

First thing, you’ll want LXD to listen to the network and setup a trust password, do this from within termina with:

  • lxc config set core.https_address :8443
  • lxc config set core.trust_password some-password

Then we’ll want to copy a working LXD client that will work on Debian.
If you followed the previous instructions, you’ll have an Ubuntu 18.04 container called c1 which conveniently come with a pre-installed version of the LXD client which we can copy into the penguin container.

With that done, you can now get yourself a normal Terminal shell and run:

Here you need to look for the gateway address of the penguin container, this will be the termina VM and so the address of our LXD server. After that, you add it as a remote and make it the default remote so all LXD commands go to it by default.

After that, you can interact with LXD exactly as you would on any other system, including now having a nicely working shell to drive it from :slight_smile:

Low level details

For this feature to be supported, you need a Chromebook that’s still actively supported by Chrome OS and have hardware support for virtualization. Both x86_64 and aarch64 machines will work with this feature.

LXD is configured in a way which only allows for unprivileged containers to be run.
This combined with the intermediate virtual machine that’s used on a per-user basis makes the entire environment extremely safe and makes attacking the host largely impossible (you’d need both a container & VM escape).

Currently most Chromebooks use LXD 3.0.0 but an update is actively rolling-out now bringing them to 3.0.2. Storage is using btrfs, allowing for fast container creation, snapshots and copies.

It’s possible to install and run Docker inside the LXD container though not all images will work properly as they may not have been designed with unprivileged containers in mind and so may be surprised when some specific actions are rejected by the kernel.

Other resources

There is a great sub-Reddit for users of Crostini:
LXD documentation:
List of LXD images:


This was a very interesting and informative article, thank you.

I have one question, which is about the installation of the lxd client. Why didn’t you just install that using apt in the penguin container? That way it would be updated automatically and one could be sure that all the dependencies etc. are in place.

I think the penguin container is Debian, therefore it does not come with the LXD client tools in the default repositories.

Some more background info about crostini and LXD,

You can add the crostini LXD repository of container images to your own LXD remotes, then create a container using this penguin container image and peek inside.

I don’t own a chromebook but this interests me for lots of reasons.

Does anyone know if crostini support “nested” containers ?

It does, you can set the security.nesting=true flag if needed (it may be set by default for penguin).
The ability to run Docker containers is something that was worked into this setup from the beginning.

My Acer R13 does not complete the Linux install. (Many, many attempts and removals, bug reports etc.) Although it never completes, I can launch termina (vmc start termina) and get a termina prompt. There are times when I get a penguin vm but not often. When I do it won’t start. I have run the run_container script to download penguin, but that script usually freezes the tab at 100%. (left it for hours).
Do you have any suggestions?

Thank you, you are quite right. I knew lxc was in the repos and wrongly assumed lxd-client was as well.

Hello hello : ) Kind of an newbie here in the container world but i have been messing around allot with the containers on my chromebook last couple of months. Must say that this is great way exploring around in the linux world. I do have an machine running manjaro that i probably reinstall like once a week because of stupid configs and beginner mistakes. Lxd made my life so much easier. hehe. Snapshots and nuking containers is actually fun : D Great way to explore and especially that you can make these fucked up configs without spending a whole day reinstalling a full machine. Now to the question. Is there any way to pipe the lxd containers via the android container. As for now im running cloudflares dns app that occupies the localhost at so its pretty much impossible to start the containers when it’s on. Is there anyway to set termina and the lxd containers localhost to lets say and then forward traffic via androids container that would be on 0.1. I guess termina is pretty much locked down so it might not be possible but is there any other solutions? Looking at ngrok right now if that might be an possible path. I had success running pihole as an container including some minor issues around the changing mac addresses… and the chromeos way to handle dns… as you always need to connect via chromeos internal dns before you can even get things started. What i am looking into achieving now is to pipe a bunch of containers… this might be overkill by i still want to try : D Chromeos -> (deb container ‘Pihole’ running dnscrypt) -> (alpine container running unbound) -> (cros android container running vpn or dns app) --> way to paranoid dns solution out to the cat meme information highway!. I understand that the easy way would be to get 3 rpi’s… but with my new found lxd fetish i am looking into ways solving this via containers : )


The communication between the Chromebook and the LXD containers is quite restricted in order to protect the security of the Chromebook from something unexpected running in a LXD container.
Having said that, if you want to run a browser (or other internet application) in a LXD container, and have them use Pi-hole/unbound/etc from other containers, then you can do so. It is possible to direct the traffic from one container to another.

However, if you want your Chromebook to enjoy the love of unbound/pihole and other network services, you could instead get an inexpensive Virtual Private Server (VPS) like the 1-S, install LXD and add unbound/pihole/etc. You need to also add a VPN software like OpenVPN so that the whole Chromebook connects to the Internet through this VPN.

Hello hello.

Actually pihole kind of works from main chromeos as well as long you preconnect to the main working dns from cros and then switch back the dns to the container ip. You can get into issues sometimes depending on start order of containers when running the hole on cros but usually it startup fine. I am about to look into the parts how to direct traffic between containers right now : ) I know : D The whole thing is pretty stupid to muck around with the chromebook when i actually do got other hardware to mess around. I guess its the convenient way how the chromebook handles a total failure. If i totally screw up all containers and system a powerwash or reinstall get me back in tops 10 minutes. Would never dare to go into this crash and burn learn path on my actual workstation.

Been looking around for a vps but been struggling in my mind if i really need one. “Casual and broke user”. ; ) might be the time to take the step. If i would run an s-1 and 1 or 2 lxd containers using pihole rssfeedr buku and minor data apps would be fine i guess. Would be nice to throw in an personal vpn as well but that will hog my cost. Thank you for the scalaway tip. One reason as of holding back was actually the vps jungle… got stuck on choosing.

Hello Stéphane,

I have a couple of Dell Chromebooks but I cannot find Terminal in the application list.

Can you provide instructions on how to install it?

I thought I was making headway towards a good understanding of the mechanisms crostini uses until I ran across this otherwise really useful article.

Can someone help me understand why this article is very firmly about LXD, but all the examples use LXC and not LXD?

actually, installing lxd replace the lxc client with the lxd one, whose name is also lxc.

It’s a common question. In LXD, the client command happens to be lxc and it is indeed confusing.

Linux Containers is a set of functionalities of the Linux kernel that help create containers (imagine a proper extension to chariot).
Then, you get the implementations of Linux Containers,

  1. LXC at the client commands look like lxc-create. Is hosted and supported here.
  2. The LXC implementations at libvirt.
  3. LXD, more recent, I think 2015. LXD is the full package and makes it easy to manage containers. Has more features. The client command is lxc.

Thank you both for your answers. My first thought was to wonder how I could tell if I was using the lxc that was the lsd client, or the “other” lxc (which presumably isn’t, or doesn’t have to be.
And then I though that the article here was presenting the Ubuntu view of the world ( i see some views expressed that there’s a difference), and I would just live with it.
And now, I reread the article title, and see that not only is the article specifically about LXD, it’s also specifically about LXD on Chromebook.

So is the LXC that I use on Chromebook, within termina, the same lxc that is described in ?

Some users may accidentally install one or the other, or even both LXD and LXC.
Here is how to figure out which is which.

In this here, I am running LXD, and I have installed LXD from the snap package (of LXD). It’s a popular choice, and here you can see on how many Linux distros you can get the the snap package of LXD. Quite versatile and is supported all over the place. You have the option of either the latest version (currently 3.10), the latest stable version (currently 3.0.3, the Chromebooks will get it soonish), the previous stable version (currently 2.0.11).

$ which lxc

The LTS versions of Ubuntu currently come with a DEB package of LXD. Ubuntu 16.04 has LXD 2.0.11, Ubuntu 18.04 has LXD 3.0.3. The above command would say here /usr/bin/lxc.

What about LXC? Not the LXC Linux Containers kernel functionality but rather the implementation also named LXC, which is the precursor of LXD and also supported from this website?

Here we run the command to locate lxc-create, the equivalent to lxc launch. It’s not installed on my computer and it comes from a different package.

$ which lxc-create
$ lxc-create

Command 'lxc-create' not found, but can be installed with:

sudo apt install lxc-utils

The following page, is for LXC (the older implementation of Linux Containers), and commands like lxc-create.

The one on the Chromebook is LXD, found at
If you have a Linux distribution, you can install LXD and get more familiar with it. The latest version of LXD is 3.10, which has more features than the version the Chromebooks. With snap packages, you can switch channel and install the exact same version from your Chromebook.

If, instead, you do not have Linux, then you can use virtualization to install a Linux distribution (I suggest Ubuntu 18.04) and then setup LXD in there.

I have written a few tutorials on LXD.
I don’t have a Chromebook, so I do not have a section on this. But there is a related post from when Crostini was first introduced.

Thanks again for the help and not least for the acknowledgement that there are differences as well as similarities. The related post was already in my bookmarks for further study, and I will add your tutorials to the list. My longer term goal is to actually understand what’s going on here, while in the short term trying out features by following canned recipes.

Most of my systems run an openSuSE variant, (which has taken a different LX[CD] path), but I have several VirtualBox environments to try things out on.

Great post. Very informative and well organized.
I’m new to LXD, but very familiar with a Vagrant / Virtual Box workflow.

Now that I can get other containers up and running, I would like to be able to get a shell in them easily. The Terminal app seems to be stuck to the Penguin container. This feels like I am missing something trivial, but how do I get a version of the Terminal app (or similar) that opens in one of my newly launched containers? (Or is the fact I am asking this mean I am probably going about this the wrong way?)

@jgillam the Terminal app opens the container named “penguin”. But you can get around that by swapping the names around, i.e. moving the penguin container to some temp name like “debian” and then creating a new container and naming it “penguin”. See this post for an example: