Skip to content

Personal

Proxmox Desk Display

A physical desk display for a Proxmox homelab, split into a Go bridge that holds all the credentials and an ESP32 firmware that holds none: a product decision, not a technical limitation.

May 2026Infrastructure

A small screen on a desk showing what a Proxmox homelab is doing. It is two products sharing one HTTP contract: a Go service that talks to Proxmox and normalises everything it knows, and an ESP32 firmware that renders nineteen screens and never learns what Proxmox is.

Bridge
Go, polls Proxmox and serves a versioned JSON schema
Device
LILYGO T-Display-S3, two buttons, 19 screens
Contract
proxmox-desk-display.v1 over bearer-authenticated HTTP
Repository
VincenzoImp/proxmox-desk-display
VincenzoImp/proxmox-desk-displayPhysical Proxmox homelab status display: a Go bridge and ESP32 firmware sharing one protocol.Go

The problem

The obvious build is to have the microcontroller call the Proxmox API directly. The repository’s architecture note argues against that and names the costs: a Proxmox token would live on the device, TLS and certificate rotation are harder on embedded firmware, an API change would require reflashing every device, and debugging a display is far worse than opening a page in a browser. Its conclusion is the sentence worth keeping: the bridge-first design is a product-quality choice, not a technical limitation.

What I built

The bridge polls one or more Proxmox installs, normalises roughly forty API paths into a versioned display schema, caches it, and serves three tiers: a compact payload for the device, a bounded detail payload with explicit caps, and an unbounded inventory for debugging. It has an admin UI, a mock mode that serves fake data so the firmware can be developed with no Proxmox at all, and four TLS modes including certificate-fingerprint pinning, the right answer to Proxmox’s self-signed certificates, rather than disabling verification.

The firmware connects through a captive portal on first boot, stores only the bridge URL and a display token, and drives nineteen drill-down screens from two buttons.

Hard parts

Degrading per endpoint rather than per refresh. Proxmox installs differ by edition, version and the permissions of the token you were given. When an endpoint is refused, the affected host carries a data warning and everything else still renders, so a missing subsystem costs one panel rather than the whole display.

Bounded payloads, because the client is a microcontroller. The detail endpoint caps every list explicitly, and the architecture note states the rule: when a screen needs more, add a query shape to the bridge rather than make the ESP32 parse the full inventory. The device is not allowed to become the place where the data is filtered.

Actually running out of memory. One commit raises the loop task’s stack to 32 KB and shrinks the JSON document from 48 KB to 32 KB in the same change. That is the constraint the whole protocol design exists to respect, arriving in person.