Linux Containers & VMs on your ChromeBook (from vmc to Alpine)

Matt Franz
5 min readJul 21, 2019

For the longest time, I’ve used the SSH Client (or Mosh) to connect into remote systems from my ChromeBook, but this has some limitations — especially AWS systems because it takes a bit of work to use key based authentication. Sure there are workarounds by using crouton or Google Cloud shell or generating keypairs in JavaScript but sometimes you need^H^H^H^H want a real Linux shell.

The good news that on newer ChromeBooks (including my two Dells) you can now run VMs and containers. See the docs for ChromeBooks that are supported. It is pretty straightforward to install the Linux shell and there are multiple online articles such as this one that show the steps. You’ll have to reboot but when you are done you will see “Linux files” and Linux (Beta) under settings.

and

Most importantly crosh will have some new and powerful commands to create and access VMs.

Expanded Commands in your Shell

crosh> vmc
USAGE: vmc
[ start [--enable-gpu] <name> |
stop <name> |
destroy <name> |
export <vm name> <file name> [removable storage name] |
list |
share <vm name> <path> |
container <vm name> <container name> [ <image server> <image alias> ] |
usb-attach <vm name> <bus>:<device> |
usb-detach <vm name> <port> |
usapt b-list <vm name> |
help ]

By default you will see Termina, but you can create others if you want.

crosh> vmc list
termina
Total Size (bytes): 15036870656

And to connect use vsh which will given you a non-privileged VM that allows you to create LXC containers inside — or just use the default Debian container called penguin. This is the container that you will access if you click on the terminal icon and I’ve found this to be good enough. It is the latest version of Debian.

To get to the VM console use vsh.

crosh> vsh termina
(termina) chronos@localhost ~ $
(termina) chronos@localhost ~ $ lxc list
+---------+---------+-----------------------+------+------------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+---------+---------+-----------------------+------+------------+-----------+
| penguin | RUNNING | 100.115.92.199 (eth0) | | PERSISTENT | 0 |
+---------+---------+-----------------------+------+------------+-----------+

From another crosh you can see the following process that you’ll see use a whole bunch of memory (and CPU) on the first boot

6939 crosvm    20   0 3147388 443788 443688 S   0.7  11.1   3:34.22 /usr/bin/crosvm run --cpus 2 --mem 2919 --root /run/imageloader/cros-termina/12105.38.0/vm_rootfs.img --tap-fd 16 --cid 4 --socket /run/vm/vm.obEjXS/crosvm.sock --wayland-sock /run/chrome/wayland-0 --cras-audio --params snd_intel8x0.inside_vm=1 snd_intel8x0.ac97_clock=48000 --wayl+

But back inside the VM you’ll see this is definitely a VM (KVM to be specific) that is running LXD

(termina) chronos@localhost ~ $ uptime
22:30:14 up 10:58, 0 users, load average: 0.00, 0.00, 0.00
(termina) chronos@localhost ~ $ vmstat
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
2 0 0 1345704 672 1388056 0 0 34 296 109 114 1 1 96 1 2
(termina) chronos@localhost ~ $ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/root 229M 226M 0 100% /
devtmpfs 1.4G 0 1.4G 0% /dev
tmp 1.4G 0 1.4G 0% /tmp
run 1.4G 24K 1.4G 1% /run
shmfs 1.4G 0 1.4G 0% /dev/shm
var 1.4G 0 1.4G 0% /var
none 1.4G 0 1.4G 0% /sys/fs/cgroup
9p 2.0G 772K 2.0G 1% /mnt/shared
/dev/vdb 13G 2.2G 8.8G 20% /mnt/stateful
tmpfs 100K 0 100K 0% /mnt/stateful/lxd/shmounts
tmpfs 100K 0 100K 0% /mnt/stateful/lxd/devlxd

(termina) chronos@localhost ~ $ ps aux | grep lxd
root 128 0.2 2.7 1222420 79596 ? Ssl 11:32 1:20 lxd --group lxd --syslog
root 186 0.0 0.5 1014656 16680 ? Ssl 11:32 0:01 tremplin -lxd_subnet 100.115.92.193/28
nobody 237 0.0 0.0 6672 144 ? S 11:32 0:00 dnsmasq --strict-order --bind-interfaces --pid-file=/mnt/stateful/lxd/networks/lxdbr0/dnsmasq.pid --except-interface=lo --interface=lxdbr0 --quiet-dhcp --quiet-dhcp6 --quiet-ra --listen-address=100.115.92.193 --dhcp-no-override --dhcp-authoritative --dhcp-leasefile=/mnt/stateful/lxd/networks/lxdbr0/dnsmasq.leases --dhcp-hostsfile=/mnt/stateful/lxd/networks/lxdbr0/dnsmasq.hosts --dhcp-range 100.115.92.194,100.115.92.206,1h -s lxd -S /lxd/ --conf-file=/mnt/stateful/lxd/networks/lxdbr0/dnsmasq.raw -u nobody
root 313 0.0 0.5 515968 15040 ? Ss 11:35 0:00 /usr/sbin/lxd forkstart penguin /mnt/stateful/lxd/containers /mnt/stateful/lxd/logs/penguin/lxc.conf
chronos 2333 0.0 0.0 4428 808 pts/1 S+ 22:32 0:00 grep --colour=auto lxd

Creating an Alpine Container

Although I’ve found the default Debian penguin container shell to be just fine, you can install other containers. Since flash is tight on a ChromeBook, (especially a 16GB like one of my Dell’s) you can create an Alpine image like this:

(termina) chronos@localhost ~ $ lxc launch images:alpine/edge alpine
Creating alpine
Starting alpine
(termina) chronos@localhost ~ $ lxc exec alpine ash
~ # uptime
22:56:42 up 46 min, load average: 0.32, 0.09, 0.02

And you can see that it creates the necessary processes and mount points

(termina) chronos@localhost ~ $ mount | grep alpine
run on /mnt/stateful/lxd/devices/alpine/disk.cros_milestone.dev-.cros_milestone type tmpfs (rw,nosuid,nodev,noexec,relatime,mode=755)
run on /mnt/stateful/lxd/devices/alpine/disk.host-ip.dev-.host_ip type tmpfs (rw,nosuid,nodev,noexec,relatime,mode=755)
/dev/vda on /mnt/stateful/lxd/devices/alpine/disk.sshd_config.dev-.ssh-sshd_config type ext4 (ro,relatime,block_validity,delalloc,nojournal_checksum,barrier,user_xattr,acl)
9p on /mnt/stateful/lxd/devices/alpine/disk.shared.mnt-chromeos type 9p (rw,nosuid,nodev,noexec,relatime,sync,dirsync,access=any,trans=fd,rfd=25,wfd=25)
/dev/vda on /mnt/stateful/lxd/devices/alpine/disk.cros_containers.opt-google-cros-containers type ext4 (ro,relatime,block_validity,delalloc,nojournal_checksum,barrier,user_xattr,acl)
(termina) chronos@localhost ~ $ ps aux | grep alpine
root 2476 0.0 0.5 588292 15116 ? Ss 22:56 0:00 /usr/sbin/lxd forkstart alpine /mnt/stateful/lxd/containers /mnt/stateful/lxd/logs/alpine/lxc.conf
1000000 2752 0.0 0.0 1560 48 ? Ss 22:56 0:00 udhcpc -b -p /var/run/udhcpc.eth0.pid -i eth0 -x hostname:alpine
chronos 2846 0.0 0.0 4428 896 pts/1 S+ 22:58 0:00 grep --colour=auto alpine

These container should “just” work and allow you to install whatever you need. Performance is what you would expect (not great) but it does work. I’ve run terraform and Python Boto code just fine, albeit a bit slow, but it gives you more something better than you can run the AWS or Azure CLI as needed or check code into github.

NOTE: I do use this to run KeepassX and I have run GUI applications but I don’t recommend it on under-powered ChromeBooks like mine.

Useful References

--

--

Occasional Thoughts on History, Books, Tech and Engineering Management