Dockerized Hardware Simulation for Angular 20+: Kiosks, Scanners, and Printers in Dev/CI to Reproduce Defects Fast

Dockerized Hardware Simulation for Angular 20+: Kiosks, Scanners, and Printers in Dev/CI to Reproduce Defects Fast

What we ran at a major airline kiosks (in Docker) so engineers could reproduce airport-only bugs on laptops and in CI—without waiting for a gate agent.

We stopped flying engineers to the hardware lab by making the hardware come to them—in Docker, on every PR.
Back to all posts

If you’ve ever chased a kiosk-only bug at 5 a.m. in an airport, you know why we simulate hardware. at a major airline, we packaged our card readers, printers, and scanners into Docker, wired them to the Angular app over WebSockets/HTTP, and ran the whole thing in GitHub Actions. Defect reproduction dropped from days to minutes.

This playbook shows how to emulate peripherals with Docker for Angular 20+ apps—complete with Signals/SignalStore modeling, CI integration (GitHub Actions/Jenkins/Azure DevOps), and the record/replay flows I’ve shipped across airlines, media, and retail.

If you’re planning 2025 roadmaps and need an Angular consultant to stabilize kiosk or device-heavy apps, this is how I’d set you up—fast, measurable, and safe.

The 5 a.m. kiosk bug—and why we Dockerized devices

a major airline reality check

at a major airline, our Angular kiosk UI kept failing only at the airport. No one could reproduce the jammed-printer path or flaky scanners on dev laptops. Ops logs were noisy, and traveling to a gate wasn’t scalable.

We built Dockerized emulators for card readers, barcode scanners, and thermal printers. Engineers could spin up the entire device stack in 10 seconds, trigger failure states, and run Cypress against it locally and in CI. Repro time fell from days to minutes; flake rate dropped under 2%.

  • Gate-only failures

  • Paper-out printer states

  • USB flakiness when hubs power cycle

What this article covers

You’ll get a reference architecture, code snippets, CI config, and a metrics checklist to keep the system honest. If you need help, hire an Angular developer who’s done kiosk and hardware work—I’m available for remote consulting.

  • Architecture

  • Angular 20+ implementation with Signals/SignalStore

  • CI integration

  • Record/replay debugging

  • Metrics

Why Docker hardware simulation accelerates Angular delivery

Business impact

Hardware bugs are expensive: travel, equipment access, and brittle test rigs. Docker makes peripherals software-addressable, so your Angular app and e2e tests talk to predictable endpoints. That predictability is what lets us ship on time without gambling on lab availability.

  • Defect reproduction from days → minutes

  • Fewer field trips to physical devices

  • Deterministic CI—no flaky USB

Engineering impact

By normalizing device protocols into HTTP/WebSocket APIs with typed event schemas, we can model device state with Signals and SignalStore, test offline flows, and run the full matrix on GitHub Actions/Jenkins/Azure DevOps.

  • Typed contracts over WebSockets/HTTP

  • Offline-tolerant UX paths tested regularly

  • CI-friendly: ephemeral, parallelizable

Reference architecture: Dockerized device emulators for Angular 20+

docker-compose.yml
version: '3.9'
services:
  device-hub:
    build: ./emulators/device-hub
    environment:
      - WS_PORT=8081
      - HTTP_PORT=8080
    ports:
      - "8080:8080"
      - "8081:8081"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://device-hub:8080/health"]
      interval: 5s
      timeout: 2s
      retries: 20
  printer-sim:
    build: ./emulators/printer-sim
    environment:
      - PRINTER_MODEL=escpos
      - FAILURE_MODE=none # none|paper_out|jam|overheat
    volumes:
      - ./fixtures/receipts:/app/receipts
  scanner-sim:
    build: ./emulators/scanner-sim
    environment:
      - BARCODE_STREAM=./fixtures/barcodes.json
    volumes:
      - ./fixtures:/app/fixtures
  cardreader-sim:
    build: ./emulators/cardreader-sim
    environment:
      - CARD_PROFILE=visa
  record-replay:
    build: ./tools/record-replay
    volumes:
      - ./scenarios:/app/scenarios
    ports:
      - "9090:9090"
networks:
  default:
    name: devices

Core components

  • Device Hub: wraps protocols (ESC/POS, OPOS, USB HID, serial) into clean REST + WebSocket APIs; publishes typed events.

  • Emulators: small containers that mimic peripherals: success + failure states (paper-out, jam, offline, busy).

  • Record/Replay: captures event streams for defect scenarios; replays deterministically in CI.

  • Angular app: consumes APIs via a SignalStore; uses PrimeNG/Angular Material for operator UI.

  • Device Hub (Node.js or .NET)

  • Emulators (printer, scanner, card reader)

  • Record/Replay service

  • Angular app (Nx workspace)

docker-compose for dev

Spin everything locally with one command.

Model device state with Angular Signals + SignalStore

// device-events.ts
export type DeviceEvent =
  | { type: 'printer.status'; online: boolean; paperPct: number }
  | { type: 'printer.error'; code: 'paper_out' | 'jam' | 'overheat'; message: string }
  | { type: 'scanner.scan'; text: string; symbology: 'qr' | 'code128' | 'pdf417' }
  | { type: 'cardreader.result'; status: 'approved' | 'declined'; authCode?: string }
  | { type: 'hub.heartbeat'; ts: number };

export interface DeviceState {
  printerOnline: boolean;
  paperPct: number;
  lastPrinterError?: string;
  scannerConnected: boolean;
  lastScan?: { text: string; symbology: string };
  payment?: { status: 'approved' | 'declined' };
  kioskMode: 'online' | 'offline' | 'degraded';
}

export const initialDeviceState: DeviceState = {
  printerOnline: false,
  paperPct: 100,
  scannerConnected: true,
  kioskMode: 'offline', // deterministic initial value
};
// device.store.ts (NgRx SignalStore)
import { signalStore, withMethods, withState } from '@ngrx/signals';
import { effect } from '@angular/core';
import { webSocket, WebSocketSubject } from 'rxjs/webSocket';
import { retry, tap } from 'rxjs/operators';
import { DeviceEvent, initialDeviceState } from './device-events';

export const DeviceStore = signalStore(
  withState(initialDeviceState),
  withMethods((store) => {
    let socket: WebSocketSubject<DeviceEvent> | null = null;
    function connect(url = 'ws://localhost:8081/ws') {
      socket = webSocket<DeviceEvent>({ url });
      socket
        .pipe(
          retry({
            delay: (err, count) => Math.min(1000 * 2 ** count, 15000), // exponential backoff
          }),
          tap((msg) => dispatch(msg))
        )
        .subscribe();
    }
    function dispatch(msg: DeviceEvent) {
      switch (msg.type) {
        case 'printer.status':
          store.patchState({ printerOnline: msg.online, paperPct: msg.paperPct, kioskMode: 'online' });
          break;
        case 'printer.error':
          store.patchState({ lastPrinterError: msg.code, kioskMode: 'degraded' });
          break;
        case 'scanner.scan':
          store.patchState({ lastScan: { text: msg.text, symbology: msg.symbology } });
          break;
        case 'cardreader.result':
          store.patchState({ payment: { status: msg.status } });
          break;
        case 'hub.heartbeat':
          if (!store.kioskMode() || store.kioskMode() === 'offline') {
            store.patchState({ kioskMode: 'online' });
          }
          break;
      }
    }
    effect(() => {
      // reconnect on mode change if we drop offline
      if (store.kioskMode() === 'offline') connect();
    });
    return { connect };
  })
);
<!-- device-banner.component.html -->
<p-toast *ngIf="deviceStore.lastPrinterError()" severity="warn" summary="Printer" [detail]="deviceStore.lastPrinterError()"></p-toast>
<p-tag [severity]="deviceStore.kioskMode() === 'online' ? 'success' : 'warning'">
  {{ deviceStore.kioskMode() | titlecase }}
</p-tag>

Typed event schema

Create a typed event schema and a SignalStore that translates WebSocket events into deterministic UI state. This keeps SSR and tests stable while preserving real-time updates.

  • Stable initial values for SSR/tests

  • Strict null checks

  • Exponential reconnect

DeviceStore example

CI integration: GitHub Actions, Jenkins, Azure DevOps

# .github/workflows/e2e.yml
name: e2e-devices
on: [push, pull_request]
jobs:
  e2e:
    runs-on: ubuntu-latest
    services:
      device-hub:
        image: ghcr.io/yourorg/device-hub:latest
        ports: ["8080:8080", "8081:8081"]
        options: >-
          --health-cmd="curl -f http://localhost:8080/health || exit 1" 
          --health-interval=5s --health-timeout=2s --health-retries=20
      printer:
        image: ghcr.io/yourorg/printer-sim:latest
      scanner:
        image: ghcr.io/yourorg/scanner-sim:latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20 }
      - run: npm ci
      - run: npx nx run webapp:e2e --configuration=ci
      - name: Upload emulator logs
        uses: actions/upload-artifact@v4
        with:
          name: emulator-logs
          path: emulators/**/logs/*.log

Spin up emulators in CI

Use services or docker-compose in CI. Health checks gate tests until emulators are ready. Parallelize scenarios (happy path, paper-out, offline) to keep pipelines under 10 minutes.

  • Health checks

  • Wait-for scripts

  • Parallel matrices

GitHub Actions example

Record and replay failure scenarios

# record a scenario for printer paper-out
curl -X POST http://localhost:9090/record/start -d '{"name":"paper_out_checkout"}'
# trigger actions in the app, then stop
curl -X POST http://localhost:9090/record/stop
# replay in CI
curl -X POST http://localhost:9090/replay -d '{"name":"paper_out_checkout"}'
// cypress/e2e/paper-out.cy.ts
describe('Paper out flow', () => {
  it('shows recovery guidance and prevents duplicate charges', () => {
    cy.request('POST', 'http://localhost:9090/replay', { name: 'paper_out_checkout' });
    cy.visit('/checkout');
    cy.contains('Printer out of paper').should('be.visible');
    cy.contains('Retry Print').should('be.disabled'); // until paper restored
  });
});

Why record/replay

When a defect appears in staging, record the event stream (with timestamps) and add the scenario to your fixtures. Every engineer can reproduce with one command; CI runs it on every PR.

  • Deterministic failures

  • Shareable artifacts

  • One-command repro

CLI example

Security and compliance in simulated environments

Boundary hygiene

Keep emulators clearly segmented from real devices and data. Use synthetic tokens and barcodes. Restrict ports to the CI network; scrub logs before upload.

  • No real PAN data

  • Mask tokens in logs

  • Least-privileged networks

Secrets and images

Store images in private registries, produce SBOMs, and verify signatures. In regulated environments (PCI, SOC 2), keep emulator fixtures separate from production traffic and rotate CI credentials frequently.

  • Private registries

  • SBOMs

  • Signature verification

When to Hire an Angular Developer for Legacy Rescue

Signals that you need help

If your AngularJS-to-Angular migration stalled, or your kiosk app has field-only defects, a senior Angular engineer can stand up Dockerized emulators, wire a SignalStore, and stabilize CI in 2–4 weeks. This is the playbook I used at a major airline, a global entertainment company, and retail fleets.

  • “Works on my lab rig” bugs

  • Field-only failures

  • Flaky e2e tied to USB

What I do in week one

I map device protocols, define typed events, implement a DeviceStore with Signals, and ship a green CI with 3–5 critical scenarios. If you need a remote Angular developer or Angular consultant, I’m available for short, focused engagements.

  • Assess protocols

  • Design event schema

  • Automate CI services

Metrics that keep you honest

Delivery metrics

Track defect reproduction time (target: minutes), e2e duration (target: <10 min), and flake rate (target: <2%). Use Angular DevTools + Lighthouse for UI budgets; feed telemetry into GA4/Firebase Logs or OpenTelemetry.

  • Defect repro time

  • E2E duration

  • Flake rate

Runtime metrics

Instrument emulator uptime (>99.5% in CI), categorize errors (paper_out, jam, offline), and visualize backoff/reconnect histograms in dashboards (D3/Highcharts).

  • Emulator uptime

  • Error taxonomy

  • Backoff behavior

Related Resources

Key takeaways

  • Simulate kiosk peripherals with Docker to cut defect reproduction from days to minutes.
  • Use typed event schemas over WebSockets and a SignalStore to model device state deterministically.
  • Run emulators in CI (GitHub Actions/Jenkins/Azure DevOps) for stable, reproducible e2e tests.
  • Record/replay device scenarios to debug offline, paper-out, jammed, or driver-error states.
  • Instrument metrics: repro time, e2e duration, flake rate, emulator uptime, and error taxonomies.

Implementation checklist

  • Define device interfaces and typed event schemas for each peripheral.
  • Create a Device Hub container that normalizes device protocols to HTTP/WebSocket.
  • Model device state in Angular with Signals + SignalStore and deterministic initial values.
  • Add a docker-compose stack for local dev; mount fixtures and logs as volumes.
  • Use Cypress/Playwright e2e tests pointing at emulators; add record/replay fixtures.
  • Integrate into CI with health checks, wait-for scripts, and artifact uploads.
  • Gate merges with stability metrics: flake rate <2%, e2e <10 min, emulator uptime >99.5%.
  • Document recovery: exponential reconnect, backpressure, and offline-first UX paths.

Questions we hear from teams

What does an Angular consultant do for kiosk or device-heavy apps?
Model device state with Signals/SignalStore, stand up Dockerized emulators, wire WebSocket/HTTP adapters, and stabilize CI with deterministic e2e. I also add telemetry, metrics, and rollback plans so you can ship confidently.
How long to implement Docker hardware simulation and CI?
A focused engagement is 2–4 weeks: week 1 for architecture, event schemas, and a working compose stack; weeks 2–3 for Angular integration and 3–5 critical scenarios; week 4 for CI hardening and metrics.
How much does it cost to hire an Angular developer for this?
Scope-driven. Most teams complete a baseline simulation + CI in 2–4 weeks. I work as a remote Angular contractor or consultant; we align on fixed-scope or weekly rates after a short assessment.
Will this work with Jenkins or Azure DevOps instead of GitHub Actions?
Yes. The pattern is portable: docker-compose or services, health checks, wait-for scripts, and artifact uploads. I’ve shipped it on GitHub Actions, Jenkins, and Azure DevOps across AWS/Azure/GCP.
Do we need real devices at all?
Yes—for final acceptance and driver compatibility. Emulators cover 80–90% of flows and all failure paths quickly. Keep a small lab for smoke tests and firmware updates; run everything else in Docker.

Ready to level up your Angular experience?

Let AngularUX review your Signals roadmap, design system, or SSR deployment plan.

Hire Matthew – Remote Angular Expert (Available Now) See how we rescue chaotic code at gitPlumbers

NG Wave

Angular Component Library

A comprehensive collection of 110+ animated, interactive, and customizable Angular components. Converted from React Bits with full feature parity, built with Angular Signals, GSAP animations, and Three.js for stunning visual effects.

Explore Components
NG Wave Component Library

Related resources