Self-hosting the TRMNL server with Terminus, what it would cost and why I'm putting it off
Right now my e-ink panels depend on the TRMNL cloud and the 20 dollars for the Developer Edition. Terminus exists, the official BYOS server in Ruby that replaces that server with one of your own. How I'd set it up in Dokploy, what dependencies it pulls in, and why the sensible call today is to leave it on standby, with the plan written down for when the time comes.

If you got here from the post about the four TRMNL panels on my desk, you already know I have four e-ink displays fed by a FastAPI app running on my own VPS. What I didn't mention is that, even though almost everything in the setup is mine, there's still one part I don't control. The display talks to the TRMNL cloud to find out which plugins are assigned to it, gets the already rendered images from there, and syncs every fifteen minutes with an account that lives on usetrmnl.com. I pay twenty dollars to unlock the Developer Edition, which lets me use Private Plugins (the ones that hit my endpoints), and in exchange for that fee I get access to the cloud server and the public catalog.
It works really well. But the obvious question, especially coming from someone who already has CrowdSec, Pangolin, Umami, Infisical, and a custom FastAPI app running on the same VPS, is this one. What if I bring that last piece home too? The project that makes that possible already exists. It's called Terminus, it's maintained by the TRMNL team itself as the official BYOS (Bring Your Own Server) reference, and it's on GitHub under the MIT license. I've been looking through it carefully this week. The short version is that yes, I could do it, and pretty quickly, but today it isn't worth it. This post is the exercise of thinking it through from end to end so I don't have to do it again when the time comes.
What exactly needs replacing
The TRMNL cloud does three things my Private Plugins don't. The first is the plugin catalog, that web screen where you define the polling URL, headers, and Liquid markup. If I bring it home, I host that screen myself. The second is rendering Liquid into an e-ink image, because the TRMNL doesn't render HTML on-device, it receives a black and white PNG ready to display. The third is the endpoint the device queries to say "I'm device X, what should you send me now?", along with the whole authentication, sleep, and wake cycle.
Those three pieces together are what Terminus implements. The stack is Ruby with Hanami as the web framework, Postgres for the catalog, Valkey (the open fork of Redis maintained since Redis moved to the AGPL) with Sidekiq for background jobs, ImageMagick to turn rendered HTML/SVG into the e-paper image, and htmx for the management interface. It ships with an official compose.yml, Raspberry Pi and Kubernetes docs, and a "deploy to Render" button for anyone who doesn't want to complicate things. It's in beta (pre-1.0), has around 550 stars and 2200 commits, and it's actively maintained.
The detail I liked most while looking into it is that the device doesn't need new firmware. To point it at your own server, you just go into the device's WiFi portal, open Advanced, check Custom Server, and enter the Terminus URL. Going back is trivial, leave it blank and it goes back to talking to the default cloud. That changes the conversation quite a bit, because the cost of trying BYOS isn't flashing anything, it's just configuration.
How I'd set it up inside Dokploy
If I decided to go for it, the path would be the same as with any other app on the VPS. New project in Dokploy, point it at the public usetrmnl/terminus repo, build from the compose.yml. I already have Postgres deployed for Umami, Infisical, and other things, so I'd create a new database in that same instance instead of spinning up another one. I'd add Valkey as a separate service, with a volume to persist the Sidekiq queues. ImageMagick comes in the Terminus image itself, so it doesn't need extra infrastructure. The management frontend, which is pure htmx, would be served from that same container.
The external domain would be something like trmnl-server.josemanuelortega.dev, behind Traefik with its Let's Encrypt certificate. And here's the detail that matters, the TRMNL on my LAN shouldn't need to go out to the Internet to talk to Terminus. The sensible option is to expose it only on the internal network, with a VPS IP over WireGuard or over the Tailscale I already use at home, or even with an internal domain served by my local DNS. If I leave the device talking to a public domain, I gain the ability to move it between networks without reconfiguring it, but I lose isolation. I'd probably keep it private at first and only expose it if I needed to.
My current four Private Plugins aren't Terminus code, they're configuration. Each plugin is URL + headers + Liquid. Migrating them would mean opening the Terminus dashboard, creating four plugins with the same parameters, and pasting in the same markup that now lives in the usetrmnl.com UI. The FastAPI endpoints don't change because they're still the same ones, the only thing that changes is who's doing the asking. Terminus would become the client that the TRMNL cloud is today.
The lighter alternatives
Terminus is the flagship implementation, but not the only one. The team itself has byos_sinatra, a much smaller DIY Ruby version with Sinatra, no Postgres, no Sidekiq, meant for people who want to hack on the server more than manage it. The community maintains larapaper in Laravel for people in the PHP world, and there's a byos_node_lite in Node.js that's the closest thing to "a FastAPI but for BYOS". If my priority were not having to learn Hanami and keeping the whole stack in a language I already know, the Node path would be the first candidate. If the priority is having the official catalog and firmware version compatibility without having to think about it, Terminus.
The other thing to keep in mind is who you're depending on. Terminus is maintained by the TRMNL team, so the odds that it keeps working with future firmware versions are as good as they can be. Community implementations depend on how active their maintainer is. For something that's stuck to a screen on my desk all day, that factor matters.
What you gain and what you lose
What you gain by self-hosting is independence. If TRMNL changes the pricing model, shuts down the Developer Edition, or has an outage, my panels keep working because the entire data path runs through machines I control. I also gain the ability to change server behavior (for example, different refresh frequencies per plugin, no-update windows at night, or my own endpoint to force a refresh from my Telegram bot). And not least, I gain the option to add more devices without paying for more subscriptions.
What you lose is exactly the cloud network effect. The community plugin marketplace disappears because I'm no longer connected to the central catalog, and the prebuilt plugins (calendar, weather, quote of the day) would have to be ported or rewritten one by one. Firmware updates still come from TRMNL, but now it's on me to read the Terminus release notes and make sure my version supports the new firmware version on the device. And then there's the normal operations risk, one more thing to monitor, one more thing whose backups I need to plan, one more thing on my Overview panel to keep an eye on.
Why now isn't the time
The sensible call today is not to migrate. The Developer Edition was a one-time 20 dollar payment when I bought the display, an amount I barely notice anymore. My Private Plugins are working, they're one hundred percent my code, and the only dependency is that the TRMNL server replies. I have no evidence that server is unreliable or that the pricing model is going to change. If either of those happened tomorrow, the plan is already written down and setting up Terminus would take me a weekend at most. All the technical learning is already thought through in this post, and migrating the plugins is four copy-pastes.
There's also a softer argument that I rarely include in this kind of analysis, but it matters. Every piece you self-host is another piece that breaks at the wrong time. On a Saturday afternoon I'd rather not be reminded that Valkey is swapping. For this use case, the TRMNL cloud does the job, and the sensible thing is to leave it there while it keeps doing the job. The question isn't "can I self-host it?" but "is the cost of continuing to pay higher than the cost of maintaining one more service?" Today, no.
When I'd pull the trigger
The plan activates if any of these happen. If TRMNL shuts down or raises the price of the Developer Edition above what I pay today. If the cloud starts giving me noticeable latency and it shows up on the display. If I want a device that never leaves my LAN, for example one exposed to guests at home. If I end up needing more than five or six active panels and want to manage them with my own catalog. Or if at some point in the future I start wanting to change things in the firmware or the protocol, because at that point I really do need to own the other end of the conversation.
In the meantime, this stays as a note to myself, written down in the place where I know I'll find it. If the situation changes in six months, I'll come back to this post, write the follow-up with the real implementation, and link the logs from that first weekend here. Today the system does what it's supposed to do and lets me keep moving on other fronts.

Jose, author of the blog
QA Engineer. I write out loud about automation, AI and software architecture. If something here helped you, write to me and tell me about it.
Leave the first comment
What did you think? What would you add? Every comment sharpens the next post.
If you liked this

Un TRMNL para las alertas del VPS, polling o webhook
Estoy valorando llevar las alertas de mi infraestructura a una pantalla de e-ink TRMNL. Comparo los dos caminos posibles, polling con un endpoint JSON detrás de Cloudflare Access y webhook desde los timers que ya tengo.

OpenClaw para testing y QA: automatiza lo que antes hacías a mano
OpenClaw no solo sirve para verificar integridad: regresión visual, monitorización de endpoints, análisis de logs, smoke tests post-deploy y auditoría de seguridad continua. Casos de uso reales para testing y QA.

Cómo montamos la infraestructura con Dokploy (y por qué dejamos Vercel)
Un VPS, Docker, Traefik y Dokploy. Así alojamos el blog y diez proyectos más. Por qué dejamos Vercel, por qué elegimos Dokploy sobre Coolify y qué ganamos y perdimos en el camino.