Server Monitor in a VM

Background

My server rack already has a hardwired keyboard and 4 port KVM attached, but I wanted to be able to quickly view my server’s console over the network. Solutions for this type of device exist, but they can be several hundred dollars, especially while Raspberry Pis are in such high demand. I have several hypervisors just sitting around with available resources, why can’t one of them show me the remote screen? I had difficulty finding an existing (cheap) tool for this task, so instead I designed my own.

Hardware

To complete this project, you will need some hardware, however it is considerably cheaper than what a Pi-KVM would cost you.

  1. HDMI to USB Capture Card ~$20
    • I used a cheap solution from Amazon, but many will work, likely including PCI-e if you have one already. https://www.amazon.com/gp/product/B0C5VVD89N
  2. (Optional) HDMI Splitter ~$15
    • Since I already had a local monitor on my server rack, I picked up a cheap HDMI splitter to split the output from the KVM switch to my local monitor and also to the capture card. Again, any HDMI switch will work; I used this one: https://www.amazon.com/gp/product/B0BXWZNQGD
  3. A hypervisor with a free USB port

Hardware

Software

My hypervisor is ESXi, but this should work in any hypervisor in which you can add a local USB device to a VM.

Download Ubuntu Desktop

You will need a Linux environment with a Desktop environment. I tried installing Ubuntu Server and adding the X components, but it wouldn’t recognize the USB capture card correctly when I did this. You could also try using any other Linux distro with a desktop environment. In my case, I went with Ubuntu 22.04 LTS: https://ubuntu.com/download/desktop

Create a new VM

In your Hypervisor, create a new VM. I’ve experimented with settings, and this seemed to be adequate for my stream needs:

  • 2 vCPU
  • 4 GB Memory
  • 30 GB HDD
  • Video Card:
    • 1 display
    • Video memory: 64 MB
    • 3D Graphics: Disabled
    • 3D Renderer: Automatic
  • USB Device: USB Capture Card (called “MACROSILICON USB3 VIDEO”)

Install Ubuntu Desktop as you normally would.

Installing Software

Once you have Ubuntu up and running, you can connect via SSH or open a Terminal via your hypervisor’s Console.

First things first, update your packages:

sudo apt update && sudo apt upgrade -y

Install ustreamer (https://github.com/pikvm/ustreamer):

sudo apt install ustreamer

If you want a firewall enabled, allow TCP/8080, (optionally TCP/22), and enable UFW:

# Allow TCP/8080 for the stream
sudo ufw allow 8080/tcp
# Optional - Allow TCP/22 for SSH
sudo ufw allow ssh
# Start UFW firewall and enable it for system startup
sudo ufw enable

Test ustreamer is functional:

sudo ustreamer --host=0.0.0.0 --port=8080 --persistent --tcp-nodelay --quality=60 --resolution=1280x720 --slowdown --desired-fps 10

The console should now be in-use as it runs ustreamer. On a separate machine, try to connect to http://yourVmIpAddress:8080/stream. You should see the video output of whatever is connected to your HDMI Capture card. Example: My TrueNAS physical server’s output

Feel free to play around and tweak the configuration options for ustreamer. For example, if you gave your system more resources, you might be able to bump up the resolution or fps. In my situation, the above settings are the perfect middle ground between quick refreshes and not taxing my system resources. When connected to my stream, the VM only utilizes about 14% of the allocated resources. When I used higher resolution or more fps, ustreamer often took 100% of system resources.

Running ustreamer as a service

In order to run ustreamer automatically when the VM boots up, you should run it as a service. Below, I have configured the system to allow multiple instances of ustreamer if desired (would require additional capture cards).

First thing.. If you still have ustreamer running, stop it with Ctrl + C.

Now, add a ustreamer user so you don’t need to run this as root.

Create a video group for ustreamer

sudo usermod -a -G video ustreamer

Create the service file:

In this file, paste the following. If you tweaked your ustreamer settings, modify them here.

[Unit]
Description=uStreamer service
After=network.target
[Service]
Environment="SCRIPT_ARGS=%I"
User=ustreamer
ExecStart=/usr/bin/ustreamer --process-name-prefix ustreamer-%I --log-level 0 --device /dev/video%I --device-timeout=8  --quality 60 --resolution 1280x720 --desired-fps=10 --slowdown --host=0.0.0.0 --port=808%I --static /var/www/html/ustreamer-%I/
[Install]
WantedBy=multi-user.target

What’s happening:

  • Description: Creating a service called uStreamer service
  • After: It will start after the network has come up
  • Environment: The service will take an input (a number) to denote which ustreamer service we’re running and which corresponding port it will use.
  • User: It will run as ustreamer
  • ExecStart: The start command.
    • The process will have a prefix with corresponding ustreamer number
    • The device we’re using corresponds with the argument passed. The USB device will either be mounted to /dev/video0 or /dev/video1. Mine is always /dev/video0.
    • Quality: set the quality level of ustreamer
    • Resolution: set your desired resolution
    • FPS: set your desired FPS. Remember.. this isn’t a video game, 10 FPS is plenty for text based tools. You can increase this if you think you’ll be controlling the system remotely via mouse.
    • Host: ustreamer will listen on all network interfaces
    • Port: 808%I means we will listen on 8080 when you pass “0” as the argument later.

Save the file. Test the service startup by running:

Try to connect to your server on port 8080 again. If for some reason your VM mounted the USB Capture Card to /dev/video1, then your command would use “[email protected]” instead.

If everything worked, then enable your service to start on boot:

Conclusion

And that’s it!

If you have a HDMI KVM switch like I’m using, then you can plug in a wireless mouse/keyboard combo to the KVM switch, and you’ll be able to control the server wirelessly (assuming you’re in wireless range of the server).

So far, I’ve been able to update my servers’ UEFI and resolve network problems when the server wouldn’t boot correctly.

Possible Improvements

There are two improvements that I will look into in the future.

It might be possible to send commands from the VM/hypervisor to the server. Pi-KVM and other systems do this using a USB to UART controller. It might also be possible to use a “USB On-The-Go” cable.

You could put ustreamer behind nginx to enable SSL. This should be trivial, and has been documented by the ustreamer devs: https://github.com/pikvm/ustreamer#nginx

Leave a Comment