Delta-V is a microservice decomposition of OpenNMS Horizon, transforming the monolithic Java application into independently deployable Kafka-connected containers.
For the original OpenNMS Horizon project description, see OPENNMS.md.
Delta-V has removed Apache Karaf/OSGi from the runtime architecture. All 12 service daemons and Minion run as standalone Spring Boot 4 applications on a jlink custom JRE built from alpine:3.21, deployed as individually-sized Docker images via layered JAR deduplication.
Delta-V code lives in the org.deltav package namespace with org.deltav.core Maven groupIds. Horizon-derived entity classes (opennms-model-jakarta) remain at org.opennms since horizon JARs reference them by FQN. Horizon dependencies are pre-built in the delta-v-horizon repository and consumed as Maven artifacts from GitHub Packages.
Where we are: All 12 daemons + Minion migrated to Spring Boot 4. The legacy opennms-config and opennms-model modules are fully decoupled from the daemon stack — all XML config loading uses Jackson XmlMapper with defaultUseWrapper(false). Layered JAR deduplication extracts shared dependencies (~80% overlap) into a common daemon-base Docker image (~415MB), with per-daemon overlay images adding only unique libraries. Each daemon starts in 2-4 seconds. 98 E2E tests pass across 8 test suites.
| Daemon | Reason |
|---|---|
| Scriptd | Event-driven BSF scripting — unused, eliminated |
| Notifd | Notification system eliminated — alerts handled externally |
| Ackd | Acknowledgment daemon — unused |
| Actiond | Legacy shell-command execution on events |
| Vacuumd | Database maintenance automations — replaced by PostgreSQL native |
| Statsd | Unused statistics reporting |
| Tl1d | Legacy TL1 telecom protocol support |
| Queued | RRD write optimization — no longer needed |
| RTCd | Real-Time Console — dead without webapp |
| Ticketer | Trouble ticketing integration — not in microservice architecture |
| DHCPd | DHCP monitor/detector service |
| Daemon | Spring Boot Module | Startup | Key Feature |
|---|---|---|---|
| Alarmd | daemon-boot-alarmd |
2.7s | Full JPA (Hibernate 7) |
| EventTranslator | daemon-boot-eventtranslator |
2.4s | JDBC-only, event-conf enrichment |
| Trapd | daemon-boot-trapd |
2.0s | Kafka Sink bridge pattern (daemon-sink-kafka) |
| Syslogd | daemon-boot-syslogd |
2.2s | Reuses Sink bridge, local DNS resolver |
| Discovery | daemon-boot-discovery |
~2s | Kafka RPC client pattern (KafkaRpcClientConfiguration) |
| Provisiond | daemon-boot-provisiond |
4.2s | JPA + 3x Kafka RPC + Quartz + SNMP adapters |
| BSMd | daemon-boot-bsmd |
3.3s | JPA + AlarmLifecycleListener + REST API |
| Pollerd | daemon-boot-pollerd |
4.1s | JPA + Kafka RPC + Twin API + PassiveStatusKeeper |
| PerspectivePollerd | daemon-boot-perspectivepollerd |
3.6s | JPA + Kafka RPC + perspective outages + event self-consumption |
| Telemetryd | daemon-boot-telemetryd |
3s | Pure ingestion bridge, multi-bridge KafkaSink, Twin API for OpenConfig |
| Enlinkd | daemon-boot-enlinkd |
3.5s | JPA + Kafka RPC + LLDP/CDP/OSPF/ISIS/Bridge topology |
| Collectd | daemon-boot-collectd |
~3s | JPA + Kafka RPC + SNMP collection + thresholding |
| Minion | daemon-boot-minion |
~4s | Kafka RPC server + Kafka Sink + Twin API subscriber |
| Image | Base | Contents |
|---|---|---|
opennms/jre-deltav:21 |
alpine:3.21 |
jlink custom JRE (22 modules) + diagnostic tools |
opennms/daemon-base |
jre-deltav:21 |
Shared libraries (~321 JARs deduped across 12 daemons) |
opennms/<daemon> |
daemon-base |
Per-daemon unique libs + thin app JAR (12 images) |
opennms/minion-boot |
jre-deltav:21 |
Spring Boot 4 Minion — Kafka RPC server + Sink listeners + Twin API |
opennms/db-init |
jre-deltav:21 |
One-shot Liquibase schema migration |
| Module | Package | Purpose |
|---|---|---|
daemon-common |
org.deltav.core.daemon.common |
DataSource, Kafka event transport, Kafka RPC client, AbstractDaoJpa, EventIpcManagerEnrichingWrapper |
daemon-boot-minion-common |
org.deltav.minion.common |
Minion shared infra: KafkaTwinSubscriberConfiguration, PassiveStatusTwinSubscriber, SnmpV3 config sync |
daemon-sink-kafka |
org.deltav.core.daemon.sink.kafka |
KafkaSinkBridge — consumes from Minion Sink topics (OpenNMS.Sink.*) |
dao-jpa-support |
org.deltav.core.daemon.common |
AbstractDaoJpa base class + UpsertTemplate for JPA DAOs |
event-forwarder-kafka |
org.deltav.core.event.forwarder.kafka |
KafkaEventForwarder + EventConfEnrichmentService |
opennms-model-jakarta |
org.opennms.netmgt.model |
Jakarta Persistence entities + JPA DAOs + Enlinkd DAOs + utility classes (stays org.opennms) |
| Component | Reason |
|---|---|
| opennms-webapp | Legacy JSP webapp — removed from Maven reactor |
| opennms-webapp-rest | REST API for dead webapp |
| opennms-full-assembly | Monolithic assembly that packaged the webapp |
| Notification system | Tables, entities, DAOs, REST, Vaadin UI, config managers |
| OnmsEvent entity | Denormalized into OnmsAlarm/OnmsOutage; EventDao deleted |
| Database Reports | Jasper Reports, Availability Reports |
| Device Config Backup | Entire feature |
| Charts | Legacy JFreeChart bar charts |
| Self-monitoring | Deprecated monitors for monitoring OpenNMS itself |
| Eventd TCP/UDP listeners | Events arrive via Kafka only |
| MessageBus JMS | JMS implementation removed; Kafka-only |
| Zenith Connect | Cloud registration feature |
| RPM/Debian packaging | Native OS packages — Docker-only deployment |
OpenNMS Horizon is an enterprise-grade open-source network monitoring platform. Delta-V restructures it from a single 35.6 GB monolith into lean, focused microservices:
- Each daemon runs in its own container — independent scaling, isolation, and restartability
- Kafka-only event transport — no ActiveMQ, no shared event bus
- Events never touch PostgreSQL — only alarms are persisted to the database
- Layered Docker images — shared
daemon-base(~415MB) + 12 per-daemon overlay images on a 143MB jlink Alpine JRE - Spring Boot 4 migration complete — all 12 daemons + Minion run as JARs (2-4s startup); Karaf fully retired
- One-shot database initialization —
opennms/db-initreplaces the Core container for schema setup org.deltavpackage namespace — original delta-v code usesorg.deltav.*packages; horizon-derived model-jakarta staysorg.opennms
Minion → Kafka Sink → Trapd/Syslogd
↓
KafkaEventForwarder → opennms-fault-events (Kafka)
↓
┌───────────────┼───────────────┐
↓ ↓ ↓
Alarmd EventTranslator All Daemons
(→ PostgreSQL) (→ translate (subscribe to
→ re-publish) relevant events)
↓
opennms-ipc-events (Kafka)
↓
Provisiond, Discovery, etc.
| Service | Runtime | Purpose |
|---|---|---|
| alarmd | Spring Boot 4 | Kafka → alarm creation/reduction → PostgreSQL |
| pollerd | Spring Boot 4 | Service availability polling |
| collectd | Spring Boot 4 | Performance data collection |
| perspectivepollerd | Spring Boot 4 | Perspective (remote location) polling |
| discovery | Spring Boot 4 | Network discovery (via Minion Kafka RPC) |
| trapd | Spring Boot 4 | SNMP trap reception (via Minion Kafka Sink) |
| syslogd | Spring Boot 4 | Syslog reception (via Minion Kafka Sink) |
| eventtranslator | Spring Boot 4 | Event translation rules + enrichment |
| enlinkd | Spring Boot 4 | Enhanced link discovery (LLDP/CDP/OSPF/ISIS/Bridge) |
| provisiond | Spring Boot 4 | Node provisioning and scanning (via Minion Kafka RPC) |
| bsmd | Spring Boot 4 | Business service monitoring |
| telemetryd | Spring Boot 4 | Telemetry/flow ingestion bridge (via Minion Kafka Sink) |
| minion | Spring Boot 4 | Distributed data collection agent (Kafka RPC + Sink + Twin API) |
| db-init | Spring Boot 4 | One-shot Liquibase schema migration |
| postgres | postgres:15 | PostgreSQL database (alarms only) |
| kafka | Apache Kafka | Event transport backbone |
| Topic | Purpose |
|---|---|
opennms-fault-events |
Alarm-bearing events (traps, syslog, translated events with alarm-data) |
opennms-ipc-events |
Daemon-to-daemon internal events (newSuspect, nodeScanCompleted, reloadDaemonConfig) |
OpenNMS.Sink.Trap |
Minion → Trapd raw trap forwarding |
OpenNMS.Sink.Syslog |
Minion → Syslogd raw syslog forwarding |
OpenNMS.Sink.Telemetry-* |
Minion → Telemetryd per-protocol flow forwarding |
OpenNMS.twin.response |
Pollerd → Minion Twin API state sync (passive status, SNMPv3 users) |
OpenNMS.twin.request |
Minion → Pollerd Twin API subscription requests |
cd opennms-container/delta-v
# Start all 16 services
./deploy.sh up full
# Check service health
./deploy.sh status
# Run E2E tests
./test-e2e.sh # Core: trap → provision → alarm lifecycle
./test-minion-e2e.sh # Minion: trap → Kafka Sink → alarm lifecycle
./test-minion-rpc-e2e.sh # Minion RPC: provision → detect → poll
./test-syslog-e2e.sh # Syslog: Cisco syslog → alarm lifecycle
./test-passive-e2e.sh # Passive: syslog → EventTranslator → Twin API → outage
./test-collectd-e2e.sh # Collectd: SNMP collection health
./test-perspective-e2e.sh # Perspective: remote-location polling + outage lifecycle
./test-enlinkd-e2e.sh # Enlinkd: LLDP topology via Containerlab cEOS- JDK 21 (daemon/minion build and runtime)
- Docker Desktop with 16 GB memory (16 containers in full profile)
snmptrap(net-snmp) for E2E tests
See BUILD.md for detailed build instructions.
cd opennms-container/delta-v
# Full build: compile → JRE image → layered daemon images
./build.sh
# Or individual steps:
./build.sh compile # Maven compile (22 modules, ~16s incremental)
./build.sh jre # Build jlink custom JRE base image (rarely needed)
./build.sh deltav # Build Delta-V layered images (daemon-base + 12 per-daemon + minion-boot)-
No events table — Events flow exclusively via Kafka. Only alarms are persisted to PostgreSQL by Alarmd.
-
No ActiveMQ — All cross-container communication uses Kafka topics.
-
Each daemon is self-contained — Every daemon container has its own
EventWriter,EventListener,EventExpander, andKafkaEventForwarder. -
Producer-side event enrichment — Each daemon's
KafkaEventForwarderloads event definitions from the database viaEventConfEnrichmentServiceand applies severity + alarm-data before publishing to Kafka. -
Minion communicates via Kafka only — No REST dependency. SNMPv3 user config distributed via Twin API.
-
Minion is sole network ingress — No daemon container binds external UDP/TCP monitoring ports. All protocol data enters via Minion → Kafka Sink → KafkaSinkBridge. All polling/collection executes on Minion via Kafka RPC (
force-remote=true). -
Twin API state sync — Bidirectional config/state sync between daemons and Minion via Kafka Twin topics.
-
Jackson XmlMapper for config — All daemon-boot modules use Jackson XmlMapper with
defaultUseWrapper(false)andJaxbAnnotationModule. Legacy JaxbUtils/JAXB eliminated. -
org.deltavpackage namespace — All original delta-v code usesorg.deltav.*packages with BeaconStrategists copyright. Horizon-derived code (model-jakarta, BSM entities) staysorg.opennmswith dual copyright attribution. -
Horizon as pre-built dependency — Horizon modules are built in
pbrane/delta-v-horizonand consumed as Maven artifacts, keeping delta-v's 22-module reactor fast (~16s compile).
| Document | Description |
|---|---|
| OPENNMS.md | Original OpenNMS Horizon project description |
| BUILD.md | Build instructions |
| CLAUDE.md | AI assistant project context |
| SECURITY.md | Security policy |
| NOTICE | OpenNMS derivation attribution |
Design documents are in docs/plans/ and docs/superpowers/.
This project is licensed under the GNU Affero General Public License v3.