My Pixel 9a hit the floor screen-first about a month ago. I was riding a Luup scooter in Tokyo, and with my “notebook-type” case the phone would not seat itself properly in the phone holder that came with the scooter. As such, my pixel was practically defenseless when it fell out of the holder and landed on the asphalt face down, sliding multiple meters before coming to a half. The display still worked, but cracks damaged the screen and the casing around the display area. Replacing the screen would cost pretty much the same amount as simply buying a new one, so I took this opportunity to get myself a new Pixel phone instead. My backup phone was on the way, and the obvious next step was to factory-reset this one and toss it in a drawer. Instead, I spent an afternoon turning it into a Linux home server with a battery-backed UPS built in – and I’d recommend it to anyone in the same boat.
This is the build log: how I unlocked KVM on a stock Pixel 9a, ran Debian 13 in QEMU under Termux, wired up autostart so the VM comes back on its own after a reboot, and turned the whole thing into something I’d trust to host a simple productivity webservice (more on that in a future post). It took me about a half day of focused work, including the dead ends. The phone now uses maybe five watts at idle and stays online through power blips because it’s literally running off its own battery.
If you have a working-but-shelved Pixel (or any Android phone, for that matter) sitting around and the same itch I had, here’s the whole process I took to get it all working.
Why a phone, and not a Raspberry Pi?
This is the obvious question, and the honest answer is: a Raspberry Pi 4 with a UPS HAT is cheaper if you don’t already own a phone. I’m not going to pretend otherwise. The reason to build a phone server is that you already have the phone, and three other things follow from that.
The battery is a free UPS. This is the killer feature for me. A Pi 4 going dark every time my power flickers is genuinely annoying – DNS resolution drops, Home Assistant dashboards go red, and I have to wait for the SD card to boot back up. The Pixel 9a’s battery is good for hours of unattended operation. When battery optimization or other settings are applied (for example, charging when it reaches 65% and stopping when it reaches 80%), not only does it prolong the battery life, but it also minimizes the chances of battery bloat from the overcharging of lithium-ion batteries.
The hardware is overpowered for the job. The Tensor G4 in the Pixel 9a has one Cortex-X4, three A720s, and four A520s. Eight cores, 8 GB of RAM, 128 GB of fast UFS storage. That’s a fundamentally better spec sheet than a Pi 4, and it’s already paid for.
The power draw is tiny. I haven’t put a meter on it, but Pixel-class SoCs typically draw 3–5 W idle and around 8–10 W under sustained load. A Pi 4 is in the same ballpark. So we’re not winning on efficiency, but we’re not losing either.
Now the honest pushback. Magisk root and the kernel command-line tweak you need for KVM both break SafetyNet and hardware attestation, so banking apps, Google Wallet, and similar are out. If this phone has to double as your wallet, this guide isn’t for you. Android OTAs may also clobber the boot command-line you set, requiring a quick fastboot fix-up. And Google’s appsearch service relies on the protected-KVM mode you’ll be disabling – losing it doesn’t matter for a headless server, but it’s the one piece of native-Android collateral.
If you’ve got an unused rooted phone, want a quiet always-on server with built-in power resilience, and you’re comfortable in a shell, this build is for you.
What you end up with
Here’s the finished stack, from outside-in:
+-------------------------------------------------------------------------------+
| Pixel 9a (Android 16, Tensor G4, 8 GB RAM, 128 GB) |
| +--------------------------------------------------------------------------+ |
| | Magisk root | Termux | Termux:Boot | Termux:API | |
| | | |
| | ~/.termux/boot/start-vm.sh (on BOOT_COMPLETED) | |
| | | | |
| | v su -c | |
| | +-----------------------------------------------------------+ | |
| | | qemu-system-aarch64 -accel kvm -cpu host | | |
| | | +---------------------------------------------------+ | | |
| | | | Debian 13 arm64 (kernel 6.12) | | | |
| | | | 6 vCPU / 5.5 GB / 85 GB qcow2 | | | |
| | | | cloud-init | systemd | Docker | | | |
| | | | | | | |
| | | | Pi-hole / Caddy / Uptime Kuma | | | |
| | | +---------------------------------------------------+ | | |
| | +-----------------------------------------------------------+ | |
| +--------------------------------------------------------------------------+ |
| hostfwd: :2222 :53 :80 :443 :3001 |
+-------------------------------------------------------------------------------+
|
home Wi-Fi 192.168.1.0/24
|
router -- 80/443 --> internet
The relevant specs and the host-port forwards I ended up with:
| Layer | What’s there |
|---|---|
| Host | Pixel 9a, Android 16, 8-core Tensor G4, 8 GB RAM, 128 GB UFS |
| Hypervisor | QEMU 10.x qemu-system-aarch64 + KVM in nVHE mode, virt machine |
| Guest | Debian 13 (Trixie) generic-cloud arm64, kernel 6.12.x |
| Allocated | 6 vCPU / 5.5 GB RAM / 85 GB qcow2 (sparse) |
| Runtime | Docker 29.5 + Compose v5.1, installed by cloud-init on first boot |
End state: from the moment I push the power button, the VM is reachable on the LAN in 70 to 90 seconds, with no human input. The phone screen stays off.
Prerequisites
You need all of this before you start. I’m not going to walk through the prereqs themselves – there are good guides for each – but if you skip any of them the rest of the article won’t work.
- A phone (duh). I tested on a Pixel 9a (Tensor G4). Pixel 6 and later should also work; pre-Tensor Pixels don’t have the KVM hooks. Android 13 or newer.
- Bootloader unlocked. If this is a phone you might want for daily use again, think about this carefully – re-locking wipes the device.
- Magisk installed and working. The Termux user is going to need
su. Magisk’s stable channel is fine. - Termux from F-Droid. This matters: the Play Store Termux is frozen at v0.118 and
pkg installis broken on it. Get Termux from f-droid.org/packages/com.termux. Termux:Boot and Termux:API add-ons must come from the same source (more on that in the gotchas section – this one bit me). - A Windows/Mac/Linux PC with
adbandfastboot. Used for one fastboot command. Google’s platform-tools zip is the canonical source. - An SSH keypair on the workstation you’ll be administering the VM from. The public key gets baked into the VM at first boot.
One non-obvious thing: by the time you start, the Magisk app on the phone should show your Termux as a granted superuser (or be configured to grant on request). I’d open a Termux session, run su once interactively, and accept the Magisk prompt – that primes the policy and saves you from a silent failure later when the autostart script calls su from a non-foreground context.
The setup, step by step
Step 1 – Unlock pKVM (one-time, from your PC)
Modern Pixels boot with a hypervisor mode called pKVM – protected KVM. It runs underneath Android and is meant for Android’s own use (the isolated_storage feature in appsearch, eventually full virtualization for Android 16’s terminal). In its default protected mode, userspace can see /dev/kvm exists but can’t open it. We need the kernel in nVHE mode instead, which exposes KVM to userspace the way a regular Linux box does.
Pixel bootloaders accept extra command-line tokens via fastboot. So we enable USB debugging on the phone, plug it into the PC, and:
adb reboot bootloader
fastboot oem pkvm enable
fastboot oem cmdline add kvm-arm.mode=nvhe
fastboot oem cmdline show # sanity-check: kvm-arm.mode=nvhe must be present
fastboot reboot
After the phone is back up, in Termux:
su -c "cat /proc/cmdline" | tr ' ' '\n' | grep kvm-arm.mode
You should see kvm-arm.mode=nvhe. You may also see kvm-arm.mode=protected ahead of it – the kernel takes the last value, so nVHE wins.
This command-line addition survives normal reboots. It can be undone with fastboot oem cmdline del kvm-arm.mode=nvhe. It may get wiped by an Android OTA – re-run the add command if KVM stops working after a system update.
Step 2 – Make /dev/kvm actually reachable (and the SELinux trap)
This is the first place the documented procedure stopped working for me, and I want to walk through what’s happening because it’s a tell for how a lot of Android-root operations behave.
After Step 1, ls -l /dev/kvm shows crw-rw-rw- – discretionary access control says anyone can open it. But from a regular Termux shell:
$ qemu-system-aarch64 -accel kvm ...
qemu-system-aarch64: Could not access KVM kernel module: Permission denied
qemu-system-aarch64: failed to initialize kvm: Permission denied
The DAC says yes; the open is failing anyway. That’s SELinux. Android’s mandatory access control runs alongside POSIX permissions, and the untrusted_app domain – which is what Termux runs as – has no rule allowing it to open /dev/kvm. You can confirm this by repeating the same QEMU invocation under su:
$ su -c "/data/data/com.termux/files/usr/bin/qemu-system-aarch64 -machine virt,accel=kvm,gic-version=3 -cpu host -smp 1 -m 256 -display none -monitor stdio -serial null"
(qemu) info kvm
kvm support: enabled
Same binary, same flags, different SELinux context – KVM works. The Magisk su drops you into a domain that’s allowed to touch /dev/kvm.
You could also fix this with a magiskpolicy SELinux rule that lets untrusted_app open the device directly, but I’d argue against it. Running QEMU under su is the simpler answer, it carries forward to autostart cleanly, and as a bonus the QEMU process is also allowed to bind to privileged ports (53, 80, 443) which it can’t do as the Termux UID.
One pitfall: some older guides recommend tsu. Don’t. Modern Termux ships su in $PREFIX/bin with the -c "command" sudo-style invocation. tsu is outdated and pulls in extra fragility for no benefit.
Step 3 – Install the Termux packages
pkg update
pkg install qemu-system-aarch64-headless qemu-utils wget xorriso openssh sshpass
sshpass is for the brief diagnostic window when you might need to log into the VM by password before key auth is sorted; you can drop it later if you want.
Step 4 – Build the VM disk and a cloud-init seed
Debian publishes ready-to-boot cloud images for arm64. We grab one, grow the disk, and write a NoCloud “seed” with our SSH key and Docker install instructions.
mkdir -p ~/qemu && cd ~/qemu
# Debian 13 generic-cloud arm64, around 340 MB compressed
wget https://cloud.debian.org/images/cloud/trixie/latest/debian-13-genericcloud-arm64.qcow2 \
-O debian.qcow2
# Grow to 85 GB sparsely - real disk usage stays small until written
qemu-img resize debian.qcow2 85G
# Writable copy of the EDK2 UEFI variables file
cp $PREFIX/share/qemu/edk2-arm-vars.fd ./vars.fd
chmod 644 vars.fd
Now the cloud-init data. Two files: meta-data sets the instance identity, user-data describes the first-boot configuration (take a good look at the commands below and replace some placeholder variables with values for your situation).
# ~/qemu/meta-data
instance-id: pixel-server-1
local-hostname: pixel-server
# ~/qemu/user-data
#cloud-config
hostname: pixel-server
manage_etc_hosts: true
ssh_pwauth: true # flip to false after key auth is verified
disable_root: true
users:
- name: you # your handle
groups: [sudo, docker]
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
lock_passwd: false
plain_text_passwd: <choose-a-strong-password-and-rotate-soon>
ssh_authorized_keys:
- "ssh-rsa AAAA...YOUR_PUBLIC_KEY... you@your-workstation"
package_update: true
package_upgrade: false # set true once you trust the boot path
packages:
- ca-certificates
- curl
- gnupg
- vim
- htop
- tmux
runcmd:
- [bash, -c, "install -m 0755 -d /etc/apt/keyrings"]
- [bash, -c, "curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc"]
- [bash, -c, "chmod a+r /etc/apt/keyrings/docker.asc"]
- [bash, -c, "echo 'deb [arch=arm64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian trixie stable' > /etc/apt/sources.list.d/docker.list"]
- [apt-get, update]
- [apt-get, install, -y, docker-ce, docker-ce-cli, containerd.io, docker-buildx-plugin, docker-compose-plugin]
- [usermod, -aG, docker, you]
- [systemctl, enable, --now, docker]
final_message: "cloud-init done after $UPTIME seconds."
Build the seed ISO:
xorriso -as mkisofs -output seed.iso -volid CIDATA -joliet -rock user-data meta-data
One thing to internalise about cloud-init: it only re-runs the configuration phases when it sees a new instance-id. If you boot the same disk twice with the same seed, cloud-init notices it has already provisioned pixel-server-1 and short-circuits. If you change user-data mid-iteration, bump the instance-id in meta-data, rebuild the seed, and reboot the VM. Save yourself the confusion I went through.
Step 5 – The QEMU launch script
This is ~/qemu/run.sh. It’s invoked as root (via su -c) by the autostart script in the next step.
#!/data/data/com.termux/files/usr/bin/sh
set -eu
PREFIX=/data/data/com.termux/files/usr
HOMEDIR=/data/data/com.termux/files/home
VMDIR="$HOMEDIR/qemu"
# su drops into Android's /system/bin/sh - reset PATH so Termux binaries resolve
export PATH="$PREFIX/bin:$PREFIX/bin/applets:/system/bin:/system/xbin"
export LD_LIBRARY_PATH="$PREFIX/lib"
export TMPDIR="$PREFIX/tmp"
export QEMU_AUDIO_DRV=none
exec "$PREFIX/bin/qemu-system-aarch64" \
-name pixel-server \
-machine virt,gic-version=3,accel=kvm \
-cpu host \
-smp 6 \
-m 5632 \
-drive if=pflash,format=raw,readonly=on,file="$PREFIX/share/qemu/edk2-aarch64-code.fd" \
-drive if=pflash,format=raw,file="$VMDIR/vars.fd" \
-drive if=virtio,format=qcow2,file="$VMDIR/debian.qcow2",cache=writeback,discard=unmap \
-drive if=virtio,format=raw,file="$VMDIR/seed.iso",readonly=on \
-device virtio-rng-pci \
-device virtio-net-pci,netdev=n0 \
-netdev user,id=n0,hostfwd=tcp:0.0.0.0:2222-:22,hostfwd=tcp:0.0.0.0:53-:53,hostfwd=udp:0.0.0.0:53-:53,hostfwd=tcp:0.0.0.0:80-:80,hostfwd=tcp:0.0.0.0:443-:443,hostfwd=tcp:0.0.0.0:3001-:3001 \
-nographic \
-serial mon:stdio \
-pidfile "$VMDIR/qemu.pid"
Quick tour of the non-obvious flags:
-machine virt,gic-version=3,accel=kvm– QEMU’svirtboard (the standard AArch64 paravirt platform) with the v3 generic interrupt controller.accel=kvmrequires/dev/kvmusable; this is why we’re undersu.-cpu host– expose the host’s CPU features to the guest. On Tensor G4 you get the full ARMv9.0 feature set, which lets the guest kernel pick faster code paths.-smp 6 -m 5632– six vCPUs, 5.5 GB RAM. Leaves the host Android with two cores and roughly 2 GB for everything else; tune down if you see OOM kills.- Two pflash drives – EDK2 firmware (read-only) and a writable variables file. UEFI on AArch64 needs both.
virtioeverywhere – block, net, RNG. virtio is the fast path;e1000emulation would work but you’d be giving up serious throughput.hostfwd=tcp:0.0.0.0:2222-:22– SLIRP user-mode networking forwards from the phone’s listening port to the guest’s port. Binding to0.0.0.0makes the port reachable from other LAN hosts; binding to127.0.0.1would make it phone-local only.-serial mon:stdio– multiplex the QEMU monitor and the guest’s serial console onto stdio. When we redirect stdio to a logfile, the entire boot trace ends up invm.log, which is invaluable when debugging.
Make it executable: chmod +x ~/qemu/run.sh.
Step 6 – First boot and verification
Launch it detached, sending stdio to a logfile so the boot trace is captured even when the SSH session dies:
su -c "nohup $HOME/qemu/run.sh > $HOME/qemu/vm.log 2>&1 < /dev/null &"
Then watch ~/qemu/vm.log grow. You’ll see EDK2 firmware messages (some “Image type X64 can’t be loaded on AARCH64” errors at the very top – those are cosmetic, ignore them), then GRUB, then the Linux kernel banner, then systemd booting, then a long cloud-init phase. On my box the runcmd block (which is doing apt-get update and pulling Docker) took 69 seconds the first time. After that you should see:
Debian GNU/Linux 13 pixel-server ttyAMA0
pixel-server login:
From a LAN machine that has your SSH private key:
$ ssh -p 2222 you@192.168.1.100
$ uname -a
Linux pixel-server 6.12.x-cloud-arm64 #1 SMP Debian ... aarch64 GNU/Linux
$ docker run --rm hello-world
Hello from Docker!
That’s the milestone. KVM-accelerated Debian, Docker, on a phone, reachable from your LAN. Everything from here is gravy.
Step 7 – Autostart with Termux:Boot
Right now, the VM only runs if you SSH in and launch it. To make it survive a phone reboot, three pieces need to line up: Termux:Boot fires a script on BOOT_COMPLETED; Termux:API enables a real Android wake-lock so the SoC doesn’t go to sleep mid-VM; and Magisk’s policy table has a standing allow for Termux’s UID so the autostart’s su -c doesn’t sit waiting for a prompt no one will see.
The autostart script – ~/.termux/boot/start-vm.sh – looks like this:
#!/data/data/com.termux/files/usr/bin/sh
PREFIX=/data/data/com.termux/files/usr
VMDIR=/data/data/com.termux/files/home/qemu
export PATH="$PREFIX/bin:$PREFIX/bin/applets:/system/bin:/system/xbin"
# Hold a wake-lock to defeat Doze
"$PREFIX/bin/termux-wake-lock" 2>/dev/null || true
# Wait for Wi-Fi DHCP to settle - port-forwards bind right away
sleep 25
# Rotate the previous log
[ -f "$VMDIR/vm.log" ] && mv "$VMDIR/vm.log" "$VMDIR/vm.log.prev"
# Launch QEMU as root, detached, logging to vm.log
exec su -c "nohup $VMDIR/run.sh > $VMDIR/vm.log 2>&1 < /dev/null &"
The 25-second sleep is empirical: Termux:Boot fires very early, and if the script races ahead before the radios are up, QEMU’s hostfwd binding succeeds but the LAN reachability is flaky for a few seconds. Twenty-five is generous; you can tune it down.
Make the script executable. Then install the two Termux add-ons (matching the signing source of your Termux – see the gotchas section if this trips you up) and open each app once after install. That’s important: Android won’t deliver BOOT_COMPLETED to an app that’s never been launched, and Termux:API’s permissions take effect when it first runs.
Final piece: battery whitelist. Settings → Apps → Termux → App battery usage → Unrestricted. Repeat for Termux:Boot and Termux:API. Without this, Doze will eventually kill the wake-lock and the VM along with it.
Step 8 – The reboot test
adb reboot the phone. Then, from a LAN host, poll for the VM:
until ssh -o ConnectTimeout=5 -p 2222 you@192.168.1.100 'uptime'; do sleep 5; done
On my Pixel 9a this completes between 70 and 90 seconds after the reboot command, every time. The phone screen stays off. There is something quietly satisfying about watching that loop succeed without you having touched anything.
Gotchas I hit (and you’ll thank me for)
These are the failures I burned time on. None of them are in the canonical guides, and several of them produce error messages so specific that I’m betting you’ll arrive on this article by Googling for one of them. Hi.
/dev/kvm is 0666 but I still get “Permission denied”
This is SELinux, not POSIX permissions. The Termux untrusted_app domain has no rule for opening /dev/kvm and the open fails with EACCES regardless of file mode. Run QEMU under su -c. Don’t bother chmod-ing or writing magiskpolicy rules – running as root is simpler and you’ll need it for the privileged-port forwards anyway.
INSTALL_FAILED_SHARED_USER_INCOMPATIBLE when installing Termux:Boot
I hit this one cold. The full error: Reconcile failed: Package com.termux.boot has no signatures that match those in shared user com.termux. Translation: all three Termux apps share an Android sharedUserId, which means they must be signed by the same key. F-Droid signs with one key; GitHub releases sign with another (the file name has +github.debug to distinguish). Mix the two and Android refuses to install.
Fix: download Termux:Boot and Termux:API from the same source as your main Termux. If your Termux is from F-Droid (the recommended path), grab Termux:Boot from f-droid.org/packages/com.termux.boot and Termux:API from f-droid.org/packages/com.termux.api. The filenames will be the bare package name plus a version code, no +github suffix.
The VM ran fine, then died a few minutes after I disconnected SSH
This is Doze. Android’s deep idle suspends the SoC; KVM is suspended along with it; whatever was in flight dies on the next scheduled event. The signal that this is your problem: ~/qemu/vm.log stops mid-systemd-boot or mid-cloud-init, with no error and no exit message – it just stops.
The fix is layered. First, termux-wake-lock must actually hold a wake-lock. That requires Termux:API installed. Without Termux:API, the termux-wake-lock binary in $PREFIX/bin is a thin client that silently no-ops because the API service it talks to doesn’t exist. Second, the three Termux apps must each be on “Unrestricted” battery; otherwise Doze can revoke the lock. You can confirm the lock is actually held from Termux:
su -c "dumpsys power | grep termux:service-wakelock"
# expect a PARTIAL_WAKE_LOCK line with ACQ=...
cloud-init didn’t run my packages / runcmd
If your cloud-init “final” stage finishes in under twenty seconds, your runcmd didn’t run, because apt-get update alone takes longer. The usual cause is that the disk has already been provisioned with the same instance-id, so cloud-init treats it as a re-boot of an existing instance and skips the once-per-instance modules.
Edit meta-data, bump instance-id: to a new value (pixel-server-2, pixel-server-3, …), rebuild the seed with xorriso, and restart the VM. Cloud-init will see the new ID and re-run everything.
qemu-system-aarch64: cannot create PID file: Try again
A previous QEMU process left a qemu.pid behind that’s still locked. The file is owned by root because run.sh runs under su, which means rm -f ~/qemu/qemu.pid from the Termux user is a silent no-op (no error, no file removed). Use su -c "rm -f ~/qemu/qemu.pid". While you’re at it, su -c "pkill -9 -f qemu-system-aarch64" to make sure no orphan is still around.
Failed to get “write” lock on the qcow2
Another QEMU process has the image open. This bit me when I thought I’d killed the previous VM but actually hadn’t – pgrep with the wrong filter was hiding it. Always confirm with su -c "ps -A | grep qemu" before relaunching.
After an Android OTA, KVM stops working
System OTAs can wipe the oem cmdline additions. Reboot to bootloader, re-run fastboot oem cmdline add kvm-arm.mode=nvhe, and you’re back. Worth adding to your post-OTA checklist.
Results: what does this actually do?
Numbers from the running system, measured rather than estimated where I can:
- Power-on to VM reachable: 70–90 seconds. Reproducibly. Most of that is Android’s own boot; the VM itself is up in about 30 seconds from the moment QEMU launches.
- VM idle RAM use: ~600 MB with Docker installed but no containers running. Plenty of headroom under the 5.5 GB allocation.
- Docker round-trip:
docker run --rm hello-worldcompletes in under two seconds.docker compose upon a typical multi-service stack is fast – virtio block I/O is not the bottleneck. - Power draw: I haven’t put a USB meter on it yet, but Tensor G4 SoCs are typically 3–5 W idle and 8–10 W under load. The phone runs warm but not hot.
- UPS behaviour: tested by yanking the USB-C cable. The VM didn’t notice. Battery showed about 1% drop per hour of idle. With Magisk’s automated charging-control modules you can pin the battery at 80% to extend its useful life – recommended for a 24/7 device.
Things I’d do differently next time
Have the Termux:Boot and Termux:API APKs downloaded before I start. The signing-key dance ate the most time during my build, and most of it was avoidable. If I’d grabbed the matching F-Droid APKs at the same time I installed Termux, I’d have saved an iteration.
Snapshot the qcow2 immediately after Docker installs cleanly. qemu-img snapshot -c clean-docker ~/qemu/debian.qcow2. Rolling back to a known-good state is one command. Cheap insurance against future experiments going sideways.
Set up monitoring before declaring done. Uptime Kuma watching itself, an ICMP probe to the gateway, and a simple webhook to email or push when the VM is unreachable. I delayed this and immediately regretted it the first time I tried to debug a flaky reboot.
Pin exact APK versions in a runbook. Termux:API in particular changes wake-lock behaviour subtly between versions. Knowing what worked is useful when something later doesn’t.
Honest “should you do this?”
Yes if: you have a working-but-shelved rooted Pixel, you’d find a battery-backed quiet always-on server useful, you’re comfortable in a shell, and the loss of banking/attestation apps on this phone is not a problem.
No if: you’d have to buy the phone fresh for this (a Pi 4 with a UPS HAT is cheaper and better documented), you depend on this phone for SafetyNet-gated apps, or you want any kind of vendor support when things go wrong.
If you build a variant of this – different phone, different distro, different services – I’d genuinely like to hear about it in the comments. Especially anything that improves the autostart reliability story, which is the single most fragile part.