TAIL OSv0.9.0

Quick Start

See TAIL OS run on a Linux machine with QEMU — no board, no toolchain, no build. Every command from step 2 on was run against the published image, with Ubuntu 24.04's QEMU 8.2 and with QEMU 10.2, and the output shown is what it printed.

You need a Linux machine with at least 2 CPUs and enough free disk for QEMU and TAIL OS. On Ubuntu 24.04 the QEMU packages below take 580 to 970 MiB, depending on what is already installed; TAIL OS takes about 300 MiB more, or about 600 MiB if you also follow Without the launcher, which keeps its own copy.

QEMU emulates the Raspberry Pi 3's four CPU cores, so the timings on this page are for a machine with 4 or more CPUs (nproc counts them). With 3 it is only a little slower. With 2, everything works but runs far slower, as noted below. With 1, the boot stops early; in our test it had not reached the shell after 15 minutes.

1. Install QEMU

sudo apt-get update
sudo apt-get install -y qemu-system-arm qemu-utils

2. Boot TAIL OS

Download the launcher, then run it:

curl -O https://tail-os.com/downloads/run_tailos_qemu.sh
chmod +x run_tailos_qemu.sh
./run_tailos_qemu.sh

The launcher downloads the kernel and data disk into ~/.cache/tailos/, checks each against the published SHA256SUMS, and boots them. The first run downloads about 15 MiB, which unpacks to about 263 MiB in the cache. It shows no progress while it does: after fetching https://tail-os.com/downloads/tail_disk.img.gz nothing more appears until the download, unpacking and check are done. Later runs start from the cache, which the launcher replaces only when a new image is published, so booting again is just ./run_tailos_qemu.sh. Each run still fetches the small SHA256SUMS to check for a new image, so it needs the network.

With 4 or more CPUs, boot takes a few seconds. With 2 it takes one to four minutes, and the log can stop for a minute or more at a time, longest just before the line ending FAT File System Server initialized; it has not hung.

Before the shell starts, the launcher prints a few lines of its own, then TAIL OS prints a banner beginning TailOS aarch64 / raspi3b build and about 45 lines of boot log, each tagged [STRT], [ OK ] or [INFO]. The two [INFO] lines where the wifi and bt expander did not acknowledge are expected: QEMU's emulated firmware ignores that request. Lines from different services can arrive out of timestamp order. When the shell starts you see:

TAIL OS Shell (tsh)
Type 'exit' to quit, 'help' for built-in commands.

/$

To leave QEMU at any point, press Ctrl-A, then a lowercase x. QEMU prints QEMU: Terminated and you are back at your own shell.

3. Look around

With 2 CPUs the shell is slow: typed text can take several seconds to appear, and each command below takes from several seconds to over ten minutes to finish. Wait for the /$ prompt before typing the next command; text typed while a command is still running appears twice.

The shell

/$ help
Built-in commands:
  exit [code]    - Exit the shell (optionally with exit code)
  cd [dir]       - Change current working directory
  pwd            - Print current working directory
  echo [-n] args - Print arguments ($VAR expanded)
  export K=V     - Set (and export) an environment variable
  cat <file...>  - Display file contents
  clear          - Clear the terminal screen
  history        - Show command history
  mkdir <dir...> - Create directories
  touch <file..> - Create empty files
  test EXPR      - Evaluate a conditional expression
  [ EXPR ]       - Same as 'test', with a closing ']'
  true / false   - Yield success / failure status
  help           - Show this help message

Scripting: 'tsh <file> [args]' runs a script; if/while/for, &&, ||,
';', $?, $1..$@ and '#!/rfs/tsh' shebangs are supported.

External commands are executed as separate processes.

The filesystem

/$ ls /
etc       hello.py  usr
/$ ls /usr/bin
bluetoothctl    isrfat          netstat         route           waitfor
cp              kill            ping            shutdown        wcetdump
dhcp            list-root-long  ps              sleep           which
fetch           logview         python          time            wifi
find            ls              reboot          top
grep            mv              rm              vi

The processes

/$ ps
PID    PPID   STATE      THREADS  NAME
0      -      LOADING    0        startup
1      -      RUNNING    11       kernel
2      -      RUNNING    2        uart_pl011
3      -      RUNNING    3        log_server
4      -      RUNNING    1        pipe_server
5      -      RUNNING    2        dwc2_rpi3
6      -      RUNNING    4        network
8      -      RUNNING    5        gpio_rpi3
9      -      RUNNING    1        sd_sdhost_rpi3
10     -      RUNNING    1        read_only_file_system_server
11     -      RUNNING    1        fat_file_system_server
13     -      RUNNING    1        tsh
14     13     RUNNING    1        ps

This is what a microkernel looks like from the inside. The serial driver, the USB and SD card drivers, both filesystems and the network stack are not part of kernel; each is a process of its own, listed here beside the shell.

Python

/$ python /hello.py
hello from tailos

python prints nothing for a few seconds before its line, and with 2 CPUs for many minutes (from 6 to about 15 in our runs, the longer when the two CPUs were separate physical cores). It has not hung.

The network

/$ ping -c 3 10.0.2.2
PING 10.0.2.2 (10.0.2.2)
reply from 10.0.2.2: seq=0 time=7.199 ms
reply from 10.0.2.2: seq=1 time=3.441 ms
reply from 10.0.2.2: seq=2 time=2.982 ms
--- 10.0.2.2 statistics ---
3 sent, 3 received, 0% lost

10.0.2.2 is QEMU's gateway on the emulated network. The replies travel through the guest's USB network device, its driver and the network process above. The times differ on every run. With 2 CPUs a reply can miss ping's 1-second timeout: that probe prints no reply: seq=N, and the last line counts it as lost.

Without the launcher

If you would rather not run a script, the same steps by hand. Run them in an empty directory: they keep no cache, the files land there, gunzip stops and asks before replacing files an earlier run left behind, and it unpacks the disk image to 256 MiB.

mkdir tailos-manual && cd tailos-manual
curl -fsSLO https://tail-os.com/downloads/tail_qemu.rfs.gz
curl -fsSLO https://tail-os.com/downloads/tail_disk.img.gz
curl -fsSLO https://tail-os.com/downloads/SHA256SUMS
gunzip tail_qemu.rfs.gz tail_disk.img.gz
sha256sum -c --ignore-missing SHA256SUMS

qemu-system-aarch64 \
    -M raspi3b \
    -kernel tail_qemu.rfs \
    -serial mon:stdio \
    -display none \
    -drive file=tail_disk.img,format=raw,if=sd \
    -netdev user,id=tailnet0 \
    -device usb-net,netdev=tailnet0

-M raspi3b emulates a Raspberry Pi 3B. The last two flags give the guest a USB network device behind QEMU's user-mode networking, which needs no special permissions on your machine; without them the guest boots with no network.

Where next

Generated from doc/quick_start_public.md in the TAIL OS repository.