Skip to content
Profile
E. Burgos
September 30, 2026By Esteban Burgos7 min read

DisplayAds: the real architecture behind a digital signage SaaS that reaches all the way to the hardware

#SaaS#arquitectura de software#IoT#NestJS#Spec-Driven Development
DisplayAds: the real architecture behind a digital signage SaaS that reaches all the way to the hardware

I'm Esteban Burgos, Forward Deployed Engineer, and over the past few months I built DisplayAds, a digital signage SaaS that is now in production at displayads.com.ar. It's not a prototype or a half-finished side project: it's a complete system that goes from the browser all the way to a Raspberry Pi connected to a screen in a physical store.

I'm writing this post because I think there's little honest literature on how you build a product that crosses the border between software and hardware when the team is a single person. Here's the full journey, with what the product does, the architecture decisions, the technologies, and how I kept order in a project that ended up having 41 data models, 31 controllers, 117 Spec-Driven Development specs, and more than 680 test files.

The 8 pieces that make up DisplayAds

When someone asks me "what is DisplayAds?", the short answer is "digital signage." The long answer is eight pieces that have to coordinate without failing:

  1. Real-time NestJS API: the core of the system. It manages authentication, content, playback scheduling, and bidirectional communication with the devices. With 41 data models and 31 controllers, it's the heaviest piece of the stack, and the one that concentrates the most tests.
  2. Webapp: the interface where customers upload content, organize playlists, and configure screens.
  3. Backoffice: the internal panel for operating the business: support, device monitoring, account management, and billing.
  4. Landing: the commercial entry point, currently visible at displayads.com.ar.
  5. Docs: technical documentation designed both for the internal team and for future integrations.
  6. Raspberry Pi agent: the software that physically runs on each screen.
  7. Android app: a hardware alternative for customers who already have a Smart TV or an Android device instead of a Raspberry Pi.
  8. Deployment and observability pipeline: the layer that connects everything above and allows shipping changes without breaking devices that are running, unsupervised, in a store hundreds of kilometers away.

None of these pieces is optional. If the backoffice fails, there's no support. If the agent fails, there's a black screen in a real store. Designing this as eight separate but coordinated systems was the most important architecture decision of the project.

The device: offline playback with local caching

A digital signage screen can't depend on the store's internet connection always being available. Routers restart, internet providers fail, and none of that can translate into a black screen in front of a customer.

That's why the agent running on each Raspberry Pi 5 is written in Python and solves playback with two key pieces: mpv as the video playback engine and SQLite as the local cache. The device downloads scheduled content in advance, saves it locally, and plays from that cache without depending on the API being reachable at the exact moment of playback. The internet connection is used to sync new content and report status, not to play content.

The Android app, written in Kotlin, follows the same principle but runs in kiosk mode: the screen stays locked into the DisplayAds application, with no access to the rest of the operating system, something essential when the device is in a public space without technical supervision.

This pattern—offline-first at the edge, with opportunistic synchronization—is probably the decision that took the most design time, because any error there shows up directly on a physical screen, and there are no console logs to debug on the spot.

Remote updates with autonomous rollback

The second hard decision was: how do I update software running on a physical device without guaranteed direct remote access, and without a deployment error leaving the screen unusable?

The answer was to build a remote update system where each device validates the new version before committing to it. If the update doesn't pass the expected validations on the hardware itself, the device automatically reverts to the last known stable version, with no human intervention. This was validated on real hardware, not in a simulated environment, because the typical failures of an embedded device (power outages mid-write, degraded SD memory, unexpected restarts) only show up when the software runs under real field conditions.

For a CTO evaluating building something similar, this is probably the piece that gets underestimated the most at first: the cost of a poorly designed remote update isn't a bug, it's a fleet of bricked devices that have to be visited physically, one by one.

The technologies behind each piece

Each piece uses the tool that best solves its problem, inside an Nx monorepo with pnpm that keeps them coordinated:

  • API: NestJS 11 with REST and WebSocket, Prisma on PostgreSQL 16 with pgvector, Redis with Bull for queues, and Socket.io with a Redis adapter for real-time communication with the devices.
  • Webapp and backoffice: React 19.
  • Landing: Next.js with server-side rendering.
  • Device agent: Python on Raspberry Pi 5, with mpv controlled over IPC and SQLite to run offline.
  • Android app: Kotlin in kiosk mode (Device Owner).
  • AI: Gemini as the main provider with fallback to OpenRouter, plus an in-house RAG engine on pgvector reused by the landing, the docs, and the operations agent.
  • Continuous delivery: 8 CI/CD workflows that deploy the API, the landing, and the frontends, ingest the docs into the RAG, release the Android app, and validate the SDD process.

No technology is there because it's trendy: each one solves a concrete product constraint, from offline playback to real-time communication with the whole fleet.

How Spec-Driven Development organized 117 specs in a one-person product

The question other tech leads ask me most often when they see the complexity of DisplayAds is: "how do you keep this organized if you're a single person?" The answer is Spec-Driven Development (SDD).

Instead of writing code and documenting afterward—or worse, never documenting—every relevant piece of system functionality is specified before being implemented: what problem it solves, what contracts it exposes, what edge cases it has to cover. In DisplayAds this translated into 117 specs that organize the system's 8 pieces, its 41 data models, and its 31 controllers, and that serve as the source of truth when you need to touch code written weeks ago without having all the context fresh in your head.

That discipline is also the reason the project ended up with more than 680 test files: each spec defines expected behavior, and that expected behavior naturally turns into test cases. It's not coverage for coverage's sake, it's coverage born from having thought through the problem before writing the solution.

Each SDD cycle also records how much every AI agent consumes: model, effort level, and input and output tokens. With that cost telemetry I know what each feature cost to build and where a cheaper model does the job without lowering quality. In DisplayAds that means 117 specs and 104 cycles with that traceability.

For a one-person team, SDD isn't a methodological luxury: it's what allows you to build with the same rigor as a team of ten, without sacrificing speed or losing the thread as the system grows.

Closing

DisplayAds is a concrete example that you can build a product that crosses software and hardware, with the seriousness of a team project, without sacrificing quality for speed. Clear architecture, offline-first on the device, reliable remote updates, technologies chosen for real constraints, and a method that organizes complexity instead of hiding it.

If you're evaluating building something similar—a product with physical devices, a real-time API, or you simply need to bring order to a project that's growing faster than your team can document—I invite you to visit https://estebanburgos.com.ar/servicios and let's talk about your case.

Written by Esteban Burgos
ESTEBAN BURGOS

About the author

ESTEBAN BURGOS · Forward Deployed Engineer

I build software end to end for startups and companies: websites, platforms and AI solutions. I write about what I learn on real projects.

See my background

Want something like this for your company?

Tell Tuki about your idea and get a price range in your currency within minutes.

Keep reading

Comments

Be the first to comment.