Mission Control is a Laravel 12 application that spins up temporary QEMU virtual machines, exposes them via WebSocket, and renders them using noVNC - all inside your browser.
It was built to power the Retro Rocket OS demo, but it works for any QEMU-compatible OS.
No systemd. No root. No Docker required. Just Laravel, QEMU and a web server.
- Linux host
- PHP 8.3+
- Composer
- Node 18+
- MySQL / MariaDB
- QEMU 10.1
- Apache 2.4 with WebSocket proxy support
Mission Control must not run as root or run as www-data
Create a dedicated user:
sudo adduser missionctl
sudo usermod -aG kvm missionctlYour web PHP-FPM pool should run as this user. This user must be able to execute QEMU, and have write access to storage/.
git clone https://github.com/brainboxdotcc/mission-control.git
cd mission-controlcomposer install
npm install
npm run buildcp .env.example .env
php artisan key:generateExample .env:
APP_NAME="Mission Control"
APP_ENV=production
APP_DEBUG=false
APP_URL=https://try.example.com
# How many seconds a user may stay connected to a VM in total
MISSION_CONTROL_HARD_SECONDS=1800
# How many seconds a user may remain idle
MISSION_CONTROL_IDLE_SECONDS=120
MISSION_CONTROL_BASE_IMAGE=/home/missionctl/base.img
MISSION_CONTROL_OVMF_CODE=/usr/share/OVMF/OVMF_CODE.fd
MISSION_CONTROL_QEMU_BIN=/usr/bin/qemu-system-x86_64
MISSION_CONTROL_QEMU_IMG_BIN=/usr/bin/qemu-img
MISSION_CONTROL_APP_NAME="Retro Rocket OS Demo"
MISSION_CONTROL_TAGLINE="Try it now - Play with the OS in your browser"
MISSION_CONTROL_OS_NAME="Retro Rocket"
MISSION_CONTROL_TITLE_LINE="Take Retro Rocket for a spin ๐"
MISSION_CONTROL_URL="https://retrorocket.dev"
MISSION_CONTROL_LOGO="/img/logo.webp"
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_DATABASE=try
DB_USERNAME=try
DB_PASSWORD="xxxxxxx"Ensure your database is created and accessible as in your .env and run:
php artisan migrate
php artisan db:seedAdd a cron entry (crontab -e) for your application user:
* * * * * cd /path/to/mission-control && php artisan schedule:run >> /dev/null 2>&1Mission Control builds the QEMU command safely from structured configuration.
It always attaches a per-session overlay disk, binds VNC and WebSocket to localhost and uses OVMF firmware (UEFI)
Configuration is controlled through environment variables.
Mission Control supports three boot modes.
| Mode | Description |
|---|---|
| disk | Boot from overlay HDD (default) |
| cdrom | Boot from ISO |
| usb | Boot from USB image |
Overlay disks are always attached. Boot mode only changes device priority.
MISSION_CONTROL_QEMU_BOOT_MODE=disk
MISSION_CONTROL_BASE_IMAGE=/home/missionctl/retro-rocket.imgThe overlay disk is created from the base image and used as the primary boot device.
MISSION_CONTROL_QEMU_BOOT_MODE=cdrom
MISSION_CONTROL_QEMU_CDROM_IMAGE=/home/missionctl/debian.isoBehaviour:
- ISO attached as CD device
- Overlay disk remains attached
- CD receives boot priority
- Overlay becomes install target
Suitable for Linux installers or live systems.
MISSION_CONTROL_QEMU_BOOT_MODE=usb
MISSION_CONTROL_QEMU_USB_IMAGE=/home/missionctl/os-usb.img
MISSION_CONTROL_QEMU_USB_FORMAT=rawBehaviour:
- USB mass-storage device attached
- Overlay disk remains attached
- USB device receives boot priority
Useful for prebuilt appliance-style OS images.
Machine configuration:
MISSION_CONTROL_QEMU_MACHINE=q35
MISSION_CONTROL_QEMU_ACCEL=kvm
MISSION_CONTROL_QEMU_CPU=host
MISSION_CONTROL_QEMU_SMP=4
MISSION_CONTROL_QEMU_MEM_MB=2048Networking (default user-mode NAT):
MISSION_CONTROL_QEMU_NET_ENABLED=true
MISSION_CONTROL_QEMU_NET_MODE=user
MISSION_CONTROL_QEMU_NET_NIC=e1000Logging:
MISSION_CONTROL_QEMU_DEBUGCON_ENABLED=true
MISSION_CONTROL_QEMU_INTERNAL_LOG_ENABLED=true
MISSION_CONTROL_QEMU_INTERNAL_LOG_FLAGS=guest_errorsFor advanced use cases.
Arguments must be provided as a JSON array:
MISSION_CONTROL_QEMU_EXTRA_ARGS_JSON=["-display","none"]The extra arguments must be avalid JSON array of strings and cannot override internal arguments such as overlay disks.
you must enable these required modules for websocket proxy support:
a2enmod proxy
a2enmod redirect
a2enmod proxy_wstunnelMission Control includes several Artisan commands:
php artisan app:reap
Performs automated cleanup tasks
php artisan app:stat
Useful for quick operational checks.
php artisan app:leases
Shows currently active VM leases.
php artisan app:kill {leaseId}
Immediately terminates a running session.
php artisan app:slotkill {slot}
Kills whichever lease is currently running in a given VM slot.
php artisan app:slots:set {count}
Ensures the system contains slots 1..count.
This command only creates missing slots and does not delete existing rows. Historical lease records remain intact.
Mission Control is deliberately simple and VPS-friendly. It does not rely on systemd units, background supervisors, container orchestration, or root-level services. There is no daemon managing lifecycle events behind the scenes.
Everything runs directly under your application user and is managed by Laravel itself. Mission Control is also not a static-site project. Because it launches QEMU locally and proxies WebSocket traffic, it requires a real Linux host capable of executing system binaries.
If you need a purely client-side emulator, this project is not designed for that.