blog: 9p Ubuntu server VM post
This commit is contained in:
parent
0093035862
commit
a0c4463b17
1 changed files with 466 additions and 0 deletions
466
blog/2025-08-18_9p-ubuntu-server.md
Normal file
466
blog/2025-08-18_9p-ubuntu-server.md
Normal file
|
|
@ -0,0 +1,466 @@
|
|||
# 9p rootfs on Ubuntu Server VM Using QEMU
|
||||
|
||||
This post is a guide on how to install Ubuntu Server to a Plan 9 filesystem.
|
||||
|
||||
If you are impatient you can skip to the actual tutorial content by clicking
|
||||
[here](#teal-deer).
|
||||
|
||||
## What is Plan 9?
|
||||
[Plan 9](https://en.wikipedia.org/wiki/Plan_9_from_Bell_Labs) started out as a
|
||||
research operating system developed at
|
||||
[Bell Labs](https://en.wikipedia.org/wiki/Bell_Labs). It extends the
|
||||
"everything is a file" API metaphor of UNIX even farther. Filesystem APIs for
|
||||
networking sockets, graphics, and more. Any resource of any system, be it
|
||||
remote or local, can be accessed if you have a mounted filesystem with these
|
||||
special files in them!
|
||||
|
||||

|
||||
|
||||
To accomplish all of this resource and file sharing, the
|
||||
[Plan 9 Filesystem Protocol](https://en.wikipedia.org/wiki/9P_(protocol)) (also
|
||||
known as **9p**) was devised.
|
||||
|
||||
Within the last few years, a lot of people have begun to realise that 9p was
|
||||
really neat and started to use it in Linux. It is especially popular for
|
||||
sharing resources with virtual machines because of how lightweight it is as a
|
||||
protocol.
|
||||
|
||||
### What happened to Plan 9?
|
||||
Sadly because of the pressures of the capitalistic hellscape we all live under,
|
||||
Bell Labs changed hands and some of this research got pared down. Bell Labs was
|
||||
first spun off into AT&T Technologies which was then bought by
|
||||
Lucent that later merged with the French Alcatel to form Alcatel-Lucent that
|
||||
then got bought again by the undead corpse of Nokia. Standard murder-execution,
|
||||
er, merger-acquisition stuff.
|
||||
|
||||

|
||||
|
||||
The researchers were scattered like ashes to the
|
||||
wind, many of them finding homes at places like Google et. al. They took their
|
||||
work with them to the BSD, Linux, and Macintosh systems that they ended up
|
||||
working with at their new jobs. They ported a lot of the features from Plan 9
|
||||
to user spaces of other operating systems and even made kernel modules and
|
||||
extensions to add back some of the features they sorely missed.
|
||||
|
||||
*(See [#1](#no1) for more details on this.)*
|
||||
|
||||
## Why use a 9p rootfs?
|
||||
The most common way that developers boot their VMs is using disk images. This
|
||||
is "fine," but it introduces a lot of annoyances and inefficiencies.
|
||||
|
||||
Let's look at the latter first. When you are booting a virtual machine with a
|
||||
disk image you are literally telling your virtual machine software to emulate an
|
||||
entire computer motherboard *and* hard drive. This isn't something that can
|
||||
typically be accelerated with your host system's virtualisation features,
|
||||
either. Your virtual machine software (like QEMU, for instance) literally has
|
||||
to do the grunt work of emulating the behavior a motherboard's
|
||||
[controller chips](https://en.wikipedia.org/wiki/Intel_440BX),
|
||||
[buses](https://en.wikipedia.org/wiki/Bus_(computing)), and attached devices
|
||||
(like the drive serving up your disk image).
|
||||
|
||||
Let's also look at why booting a VM this way might be annoying for a developer.
|
||||
Because all of your VM's files are trapped in a disk image file this means that
|
||||
you have run an extra command to `mount` the disk image on some sort of loopback
|
||||
device. If you use a QEMU Qcow image you have to do an extra step of mapping
|
||||
the image to a
|
||||
[network block device](https://www.qemu.org/docs/master/tools/qemu-nbd.html).
|
||||
Some of these things may require administrative/`root` privileges without
|
||||
special configuration magic. Even worse, you often can't directly write to a
|
||||
running VM's filesystem without first turning off the virtual machine! This
|
||||
leads users down iSCSI, NFS, SMB, and SSH rabbit holes if they want to poke at
|
||||
the filesystems of running VMs. What if there was a better way?
|
||||
|
||||
With 9p filesystems, you can inject the filesystem directly into a mapped space of
|
||||
guest's kernel without the baggage of emulating block devices and running
|
||||
traditional filesystems designed for physical disks. You can directly modify
|
||||
the filesystems of a running VM in situ without any catastrophic consequences.
|
||||
You can even map the permissions of the shared filesystem to the user account
|
||||
running the VM.
|
||||
|
||||
Meaning on your VM you can have this on the mapped guest 9p filesystem:
|
||||
|
||||
```
|
||||
root@ubuntu-server:~# ls /
|
||||
total 32K
|
||||
drwxr-xr-x 1 1000 1000 266 Aug 7 05:15 .
|
||||
drwxr-xr-x 1 1000 1000 266 Aug 7 05:15 ..
|
||||
-rwxr-xr-x 1 1000 1000 6.9K Aug 7 05:10 arch-chroot
|
||||
lrwxrwxrwx 1 root root 7 Apr 22 2024 bin -> usr/bin
|
||||
drwxr-xr-x 1 root root 0 Feb 26 2024 bin.usr-is-merged
|
||||
drwxr-xr-x 1 root root 306 Aug 7 17:26 boot
|
||||
drwxr-xr-x 15 root root 3.7K Aug 8 17:59 dev
|
||||
drwxr-xr-x 1 root root 2.8K Aug 7 18:03 etc
|
||||
drwxr-xr-x 1 root root 0 Apr 22 2024 home
|
||||
lrwxrwxrwx 1 root root 7 Apr 22 2024 lib -> usr/lib
|
||||
drwxr-xr-x 1 root root 0 Apr 8 2024 lib.usr-is-merged
|
||||
lrwxrwxrwx 1 root root 9 Apr 22 2024 lib64 -> usr/lib64
|
||||
drwxr-xr-x 1 root root 0 Aug 7 05:00 media
|
||||
drwxr-xr-x 1 root root 0 Aug 7 05:00 mnt
|
||||
drwxr-xr-x 1 root root 0 Aug 7 05:00 opt
|
||||
dr-xr-xr-x 417 root root 0 Aug 8 17:59 proc
|
||||
drwx------ 1 root root 142 Aug 8 2025 root
|
||||
drwxr-xr-x 15 root root 500 Aug 8 17:59 run
|
||||
lrwxrwxrwx 1 root root 8 Apr 22 2024 sbin -> usr/sbin
|
||||
drwxr-xr-x 1 root root 0 Mar 31 2024 sbin.usr-is-merged
|
||||
drwxr-xr-x 1 root root 12 Aug 7 17:44 snap
|
||||
drwxr-xr-x 1 root root 0 Aug 7 05:00 srv
|
||||
dr-xr-xr-x 13 root root 0 Aug 8 17:59 sys
|
||||
drwxrwxrwt 1 root root 640 Aug 8 17:59 tmp
|
||||
drwxr-xr-x 1 root root 94 Aug 7 05:00 usr
|
||||
drwxr-xr-x 1 root root 124 Aug 7 17:44 var
|
||||
```
|
||||
|
||||
...and get this on your host machine:
|
||||
```
|
||||
targetdisk@vm-host:~$ ls 9p/
|
||||
total 32K
|
||||
drwxr-xr-x 1 targetdisk targetdisk 266 Aug 7 00:15 .
|
||||
drwxr-xr-x 1 targetdisk targetdisk 76 Aug 5 14:48 ..
|
||||
-rwxr-xr-x 1 targetdisk targetdisk 6.9K Aug 7 00:10 arch-chroot
|
||||
-rw------- 1 targetdisk targetdisk 7 Apr 22 2024 bin
|
||||
drwx------ 1 targetdisk targetdisk 0 Feb 26 2024 bin.usr-is-merged
|
||||
drwx------ 1 targetdisk targetdisk 306 Aug 7 12:26 boot
|
||||
drwx------ 1 targetdisk targetdisk 128 Aug 7 00:00 dev
|
||||
drwx------ 1 targetdisk targetdisk 2.8K Aug 7 13:03 etc
|
||||
drwx------ 1 targetdisk targetdisk 0 Apr 22 2024 home
|
||||
-rw------- 1 targetdisk targetdisk 7 Apr 22 2024 lib
|
||||
-rw------- 1 targetdisk targetdisk 9 Apr 22 2024 lib64
|
||||
drwx------ 1 targetdisk targetdisk 0 Apr 8 2024 lib.usr-is-merged
|
||||
drwx------ 1 targetdisk targetdisk 0 Aug 7 00:00 media
|
||||
drwx------ 1 targetdisk targetdisk 0 Aug 7 00:00 mnt
|
||||
drwx------ 1 targetdisk targetdisk 0 Aug 7 00:00 opt
|
||||
drwx------ 1 targetdisk targetdisk 0 Apr 22 2024 proc
|
||||
drwx------ 1 targetdisk targetdisk 142 Aug 8 12:59 root
|
||||
drwx------ 1 targetdisk targetdisk 44 Aug 7 00:06 run
|
||||
-rw------- 1 targetdisk targetdisk 8 Apr 22 2024 sbin
|
||||
drwx------ 1 targetdisk targetdisk 0 Mar 31 2024 sbin.usr-is-merged
|
||||
drwx------ 1 targetdisk targetdisk 12 Aug 7 12:44 snap
|
||||
drwx------ 1 targetdisk targetdisk 0 Aug 7 00:00 srv
|
||||
drwx------ 1 targetdisk targetdisk 0 Apr 22 2024 sys
|
||||
drwx------ 1 targetdisk targetdisk 478 Aug 8 12:59 tmp
|
||||
drwx------ 1 targetdisk targetdisk 94 Aug 7 00:00 usr
|
||||
drwx------ 1 targetdisk targetdisk 124 Aug 7 12:44 var
|
||||
```
|
||||
<a name="teal-deer">
|
||||
|
||||
## Installing Ubuntu Server to a 9p filesystem
|
||||
|
||||
### Get Ubuntu Server
|
||||
First, download the
|
||||
[Ubuntu Server installer ISO](https://ubuntu.com/download/server)
|
||||
from [Ubuntu's website](https://ubuntu.com/).
|
||||
|
||||
### Start the VM
|
||||
Make a directory you'd like to export as a `9p` filesystem. For the purposes of
|
||||
this demonstration, we'll call our directory `ubuntu-9p`:
|
||||
```bash
|
||||
mkdir ubuntu-9p
|
||||
```
|
||||
|
||||
Now you can run your installer VM with your Ubuntu ISO attached and your 9p
|
||||
filesystem mapped and exported! You'll need to substitute the path for
|
||||
`UBUNTU_ISO` to the path of your downloaded Ubuntu Server ISO. I threw on some
|
||||
extra flags to enable some of the extra sandboxing features of QEMU.
|
||||
|
||||
Run the following on your host:
|
||||
```bash
|
||||
qemu-system-$(uname -m) \
|
||||
-cpu max \
|
||||
-enable-kvm \
|
||||
-smp $(nproc) \
|
||||
-nodefaults \
|
||||
-no-user-config \
|
||||
-nographic \
|
||||
-chardev stdio,id=virtcons0 \
|
||||
-device virtio-serial-pci \
|
||||
-device virtconsole,chardev=virtcons0 \
|
||||
-m 8G \
|
||||
-net user,hostfwd=tcp::2221-:22 \
|
||||
-net nic \
|
||||
-device virtio-9p-pci,id=fs0,fsdev=fsdev-fs0,mount_tag=fs0 \
|
||||
-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
|
||||
-drive file=UBUNTU_ISO,media=cdrom,id=virtiso1 \
|
||||
-fsdev local,security_model=mapped,id=fsdev-fs0,multidevs=remap,path=ubuntu-9p
|
||||
```
|
||||
|
||||
This will start a virtual machine with the local directory `ubuntu-9p` mapped to
|
||||
a 9p filesystem on the guest named `fs0`.
|
||||
|
||||
### Get to a shell
|
||||
After a little wait the VM should boot into the text-mode Ubuntu installer
|
||||
interface. At the time of writing the Ubuntu installer doesn't support
|
||||
installing to systems without block devices. This means we will have to install
|
||||
by other means in a shell on our booted install media.
|
||||
|
||||
Select `Enter shell` from the `[ Help ]` menu.
|
||||
|
||||
```
|
||||
================================================================================
|
||||
Serial ┌──────────────[ Help ]┐
|
||||
=======================================================│ Help on this screen │=
|
||||
│ Keyboard shortcuts │
|
||||
As the installer is running on a serial console, it h│ Enter shell │
|
||||
mode, using only the ASCII character set and black an│ View error reports │
|
||||
├──────────────────────┤
|
||||
If you are connecting from a terminal emulator such a│ About this installer │
|
||||
supports unicode and rich colours you can switch to "│ Help on SSH access │
|
||||
unicode, colours and supports many languages. ├──────────────────────┤
|
||||
│ Toggle rich mode │
|
||||
You can also connect to the installer over the networ└──────────────────────┘
|
||||
allow use of rich mode.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[ Continue in rich mode > ]
|
||||
[ Continue in basic mode > ]
|
||||
[ View SSH instructions ]
|
||||
```
|
||||
|
||||
If during this install process you want a bigger terminal you can `exit` the
|
||||
shell and select `Help on SSH access`. You'll get the name of the `installer`
|
||||
account and a password that was randomly-generated when you booted the Ubuntu
|
||||
live installer media.
|
||||
|
||||
```
|
||||
┌────────────────────────── Help on SSH access ──────────────────────────┐
|
||||
│ │
|
||||
│ It is possible to connect to the installer over the network, which │
|
||||
│ might allow the use of a more capable terminal and can offer more │
|
||||
│ languages than can be rendered in the Linux console. │
|
||||
│ │
|
||||
│ To connect, SSH to installer@10.0.2.15. │
|
||||
│ │
|
||||
│ The password you should use is "Qhs_3:@C^#H'nw`4rd6%". │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ [ Close ] │
|
||||
│ │
|
||||
└────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
In another terminal on your host machine `ssh` like so:
|
||||
```bash
|
||||
ssh -p 2221 installer@127.0.0.1
|
||||
```
|
||||
|
||||
If you run the VM multiple times, you might have to edit your
|
||||
`~/.ssh/known_hosts` file to edit the host fingerprint entry for the `127.0.0.1:2221`
|
||||
host.
|
||||
|
||||
### Mount your 9p filesystem
|
||||
Now that you have a working shell, you'll need to mount the 9p filesystem:
|
||||
```
|
||||
root@ubuntu-server:/# mount -t 9p -o trans=virtio fs0 /mnt
|
||||
```
|
||||
|
||||
### Bootstrap the base Ubuntu system
|
||||
With the destination 9p filesystem mounted, it's now time to bootstrap a base
|
||||
Ubuntu installation to it. The Ubuntu Server installation media at the time of
|
||||
writing does not include the `debootstrap` tool so we'll have to install it.
|
||||
|
||||
First, update the package lists:
|
||||
```
|
||||
root@ubuntu-server:/# apt update
|
||||
```
|
||||
|
||||
Now, install the `debootstrap` tool:
|
||||
```
|
||||
root@ubuntu-server:/# apt install debootstrap
|
||||
```
|
||||
|
||||
It's time to begin bootstrapping our base system. You'll need the shortened
|
||||
codename of Ubuntu release you'd like to install. For instance, if
|
||||
you were installing Ubuntu 20.04 "Noble Numbat," you'd use the short name
|
||||
`noble`.
|
||||
*(See [#2](#no2) for more details.)*
|
||||
|
||||
Bootstrap the new system with the `debootstrap` tool like so:
|
||||
```
|
||||
root@ubuntu-server:/# debootstrap noble /mnt
|
||||
```
|
||||
|
||||
### Change root to the target system
|
||||
For the rest of the installation process, the rest of the steps will need to be
|
||||
performed from a shell on the target system we just bootstrapped. To do this,
|
||||
we'll need to pass some pseudo-filesystems on from our live installation
|
||||
environment and execute the `chroot` command to "change root" into the target
|
||||
that lives on our 9p filesystem.
|
||||
|
||||
First, let's pass over the live environment's pseudo-filesystems as `bind`
|
||||
mounts:
|
||||
```
|
||||
root@ubuntu-server:/# mount -o bind /proc /mnt/proc
|
||||
root@ubuntu-server:/# mount -o bind /dev /mnt/dev
|
||||
root@ubuntu-server:/# mount -o bind /dev/pts /mnt/dev/pts
|
||||
root@ubuntu-server:/# mount -o bind /sys /mnt/sys
|
||||
```
|
||||
|
||||
Now change root to the 9p filesystem:
|
||||
```
|
||||
root@ubuntu-server:/# chroot /mnt /usr/bin/bash
|
||||
```
|
||||
|
||||
### Installing a kernel
|
||||
The earlier `debootstrap` command line installed most of the components of a
|
||||
working system, but not quite all of them. The next thing you'll need to do is
|
||||
install a Linux kernel. Later on you'll be passing this kernel and its
|
||||
associated initial RAM disk image as command-line flags to QEMU as `-kernel` and
|
||||
`-initrd`, respectively.
|
||||
|
||||
We're going to pick the `linux-virtual` package here for a smaller footprint on
|
||||
the host machine:
|
||||
```
|
||||
root@ubuntu-server:/# apt install linux-virtual
|
||||
```
|
||||
|
||||
### Enable 9p kernel modules
|
||||
To allow the installed system to boot properly, you'll need to add 9p kernel
|
||||
modules to the initial RAM disk image. The following lines need to be appended
|
||||
to the end of `/etc/initramfs-tools/modules`:
|
||||
```
|
||||
9p
|
||||
9pnet
|
||||
9pnet_virtio
|
||||
```
|
||||
|
||||
You can add them with an editor like `vi` or with shell I/O redirects like so:
|
||||
```
|
||||
root@ubuntu-server:/# cd /etc/initramfs-tools
|
||||
root@ubuntu-server:/etc/initramfs-tools# echo 9p >> modules
|
||||
root@ubuntu-server:/etc/initramfs-tools# echo 9pnet >> modules
|
||||
root@ubuntu-server:/etc/initramfs-tools# echo 9pnet_virtio >> modules
|
||||
```
|
||||
|
||||
The lines added to `/etc/initramfs-tools/modules` tell Ubuntu's scripts to
|
||||
include the `9p`, `9pnet`, and `9pnet_virtio` kernel modules in the initial RAM
|
||||
disk image. With that done, update the initial RAM filesystem:
|
||||
```
|
||||
root@ubuntu-server:/# update-initramfs -u
|
||||
```
|
||||
|
||||
### Enable DHCP on boot
|
||||
To get internet access when your VM boots, you'll need to configure a network
|
||||
interface. The VM can automatically get an IP address and a gateway at with
|
||||
DHCP enabled on its virtual network interface (`ens2` on my VM). Ubuntu has
|
||||
this ~~terrible~~ thing called [netplan](https://netplan.io/) in its
|
||||
base installation that can be used to configure network interfaces. Since it's
|
||||
already here, we might as well use it.
|
||||
|
||||
Get the name of the virtual Ethernet interface with the `ip` command:
|
||||
```
|
||||
root@ubuntu-server:/# ip a
|
||||
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
|
||||
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
|
||||
inet 127.0.0.1/8 scope host lo
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 ::1/128 scope host noprefixroute
|
||||
valid_lft forever preferred_lft forever
|
||||
2: ens2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
|
||||
link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff
|
||||
altname enp0s2
|
||||
inet 10.0.2.15/24 metric 100 brd 10.0.2.255 scope global dynamic ens2
|
||||
valid_lft 85077sec preferred_lft 85077sec
|
||||
inet6 fec0::5054:ff:fe12:3456/64 scope site dynamic mngtmpaddr noprefixroute
|
||||
valid_lft 86078sec preferred_lft 14078sec
|
||||
inet6 fe80::5054:ff:fe12:3456/64 scope link
|
||||
valid_lft forever preferred_lft forever
|
||||
```
|
||||
|
||||
Your network interface should be named something like `ens2`. With that known,
|
||||
tell the `netplan` command to enable DHCPv4 on the interface like so:
|
||||
```
|
||||
root@ubuntu-server:/# netplan set --origin-hint ens2 ethernets.ens2.dhcp4=true
|
||||
```
|
||||
|
||||
This will create a YAML file with the `.yaml` extension in `/etc/netplan` that
|
||||
has `ens2` somewhere in the name. Hints are merely suggestions, after all.
|
||||
|
||||
### Set root password
|
||||
There's one last thing you need to do before you can enjoy your newly-installed
|
||||
VM: set a `root` password! You can do it by invoking the `passwd` command with
|
||||
no arguments:
|
||||
```
|
||||
root@ubuntu-server:/# passwd
|
||||
New password:
|
||||
Retype new password:
|
||||
passwd: password updated successfully
|
||||
```
|
||||
|
||||
With the password set, you may now `exit` the changed-root shell and `poweroff`
|
||||
the installer VM.
|
||||
|
||||
## Enjoying your VM
|
||||
You can now boot your new VM with the following command:
|
||||
```
|
||||
qemu-system-$(uname -m) \
|
||||
-cpu max \
|
||||
-enable-kvm \
|
||||
-smp $(nproc) \
|
||||
-nodefaults \
|
||||
-no-user-config \
|
||||
-nographic \
|
||||
-chardev stdio,id=virtcons0 \
|
||||
-device virtio-serial-pci \
|
||||
-device virtconsole,chardev=virtcons0 \
|
||||
-m 8G \
|
||||
-net user,hostfwd=tcp::2222-:22 \
|
||||
-net nic \
|
||||
-device virtio-9p-pci,id=fs0,fsdev=fsdev-fs0,mount_tag=fs0 \
|
||||
-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
|
||||
-kernel ubuntu-9p/boot/$(<vmlinuz) \
|
||||
-append "earlyprintk=ttyS0 root=fs0 rw rootfstype=9p rootflags=trans=virtio,version=9p2000.L,msize=5000000,cache=mmap,posixacl console=ttyS0" \
|
||||
-initrd ubuntu-9p/boot/$(<initrd.img) \
|
||||
-fsdev local,security_model=mapped,id=fsdev-fs0,multidevs=remap,path=ubuntu-9p
|
||||
```
|
||||
|
||||
After a little wait, you should be greeted with a text-mode `getty` login prompt.
|
||||
Log into the `root` account with the password you set earlier.
|
||||
|
||||
### Logging with SSH
|
||||
If you would like to log in with SSH, you'll need to install the OpenSSH server
|
||||
software and configure it. Install it like so:
|
||||
```
|
||||
root@ubuntu-server:~# apt install openssh-server
|
||||
```
|
||||
|
||||
The Ubuntu install scripts for the OpenSSH server package should enable the
|
||||
daemon automatically, but you won't be able to get in yet. To do that, you'll
|
||||
need to copy a public key from your host so you can log in. If you don't
|
||||
already have a key, you can make one with `ssh-keygen`.
|
||||
|
||||
Once you have a key that you'd like to use, make a place to put it on the guest
|
||||
VM:
|
||||
```
|
||||
root@ubuntu-server:~# mkdir .ssh
|
||||
root@ubuntu-server:~# chmod 700 .ssh
|
||||
root@ubuntu-server:~# touch .ssh/authorized_keys
|
||||
root@ubuntu-server:~# chmod 600 .ssh/authorized_keys
|
||||
```
|
||||
|
||||
In another terminal on your host machine, add the public key you'd like to use to
|
||||
the end of the `authorized_keys` file like so:
|
||||
```
|
||||
targetdisk@vm-host:~$ cat ~/.ssh/id_ed25519.pub >> ubuntu-9p/root/.ssh/authorized_keys
|
||||
```
|
||||
|
||||
Now you can log in to the VM with the following command:
|
||||
```bash
|
||||
ssh -p 2222 root@127.0.0.1
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
That's it! You now have a working minimal Ubuntu server installation that you
|
||||
can use and abuse from within and without, thanks to the Plan 9 filesystem!
|
||||
You'll probably want to save the long QEMU command to a shell script so that
|
||||
you can quickly boot up your VM later.
|
||||
|
||||
## SEE ALSO
|
||||
1. <a name="no1">[Why did Plan 9's creators give up on Plan 9?](https://fqa.9front.org/fqa0.html#0.2.3)
|
||||
via [9front](https://9front.org)
|
||||
2. <a name="no2">[Ubuntu Version History](https://en.wikipedia.org/wiki/Ubuntu_version_history) via [Wikipedia](https://wikipedia.org)
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue