Architecture Node-RED GitOps ~15 min read

Node-RED to GitOps — When to Graduate from Flows to Functions

A practical guide comparing flow-based programming (Node-RED) with the function-based GitOps approach used by fn-uns. When to use each, how to migrate, and why code-first wins when you need to scale, collaborate, and maintain a production UNS.

Start with Node-RED — Seriously

If you're exploring whether a Unified Namespace makes sense for your factory, start with Node-RED. We mean it.

Node-RED is the fastest way to wire MQTT topics together and see results. You can go from "I have a PLC publishing data" to "I have a dashboard showing machine status" in an afternoon. No code, no containers, no deployment pipeline — just drag, drop, and connect.

What Node-RED Does Brilliantly

Our recommendation: Use Node-RED to validate your UNS concept. Prove that the data is there, the topic structure works, and the business value is real. Then decide how to run it in production.

A Typical Node-RED UNS Experiment

Here's what a first UNS experiment looks like in Node-RED — and it's genuinely effective:

# A simple Node-RED flow for UNS experimentation

[MQTT In] → v1.0/enterprise/site1/area1/cnc-01/status
    ↓
[Function] → Parse JSON, extract state field
    ↓
[Switch] → Route by state: ACTIVE / IDLE / ALARM / SETUP
    ↓
[Dashboard] → Gauge showing current state
    ↓
[PostgreSQL] → INSERT INTO machine_log (topic, state, timestamp)

# Total setup time: ~30 minutes
# Nodes used: 6
# Lines of code: 0 (just node configuration)

This is a perfectly valid way to prove the concept. You've demonstrated that machine data flows through MQTT, gets processed, gets stored, and gets visualised. The business case is proven.

The question is: what happens next?

Same Logic, Different Delivery

The core logic of a UNS pipeline is the same regardless of whether you implement it in Node-RED or fn-uns. The difference is how that logic is packaged, deployed, and maintained.

Example: Detecting a Machine State Change

Node-RED Approach

An MQTT-in node subscribes to v1.0/#. A function node compares the incoming value to a context variable storing the previous value. If changed, it passes the message to a PostgreSQL node that inserts a row. The logic lives inside the function node's text editor — a small JavaScript snippet embedded in the flow JSON.

fn-uns Approach

uns-framework (Go) subscribes to MQTT and caches current + previous values in Valkey. uns-state (Go) reads the cache, compares values, and writes state transitions with durations to PostgreSQL. Each is a standalone program in its own directory with its own Dockerfile, tests, and README.

Example: Logging Data to a Database

Node-RED

Wire a function node to a PostgreSQL node. The function node builds the SQL query as a string. Table creation is manual — you run the CREATE TABLE statement separately. If the schema changes, you update the function node and hope you don't break the query.

fn-uns

Each function auto-creates its table on first run with CREATE TABLE IF NOT EXISTS. The schema is defined in the same file as the business logic. Schema changes are version-controlled — you can see exactly when a column was added and why.

Example: Computing a KPI

Node-RED

A function node queries PostgreSQL, computes utilisation as ACTIVE time divided by total time, and outputs the result to a dashboard gauge. The SQL query, the calculation logic, and the output formatting all live inside one function node's code editor.

fn-uns

uns-kpi is a standalone Go function (250 lines) that queries all PostgreSQL tables and returns a structured JSON response with utilisation, availability, throughput, MTBF, MTTR, and stoppage pareto. Filterable by machine, area, and time range. Testable. Documented.

The logic is identical. The delivery mechanism is fundamentally different — and that difference matters enormously as you scale.

When Flows Stop Scaling

Node-RED flows work beautifully at small scale. The problems emerge gradually — and by the time you notice them, you're deeply invested.

The Progression

PROTOTYPE 5–20 nodes Everything works. Fast, visual, fun. GROWING 50–100 nodes Flows get complex. Tabs multiply. STRUGGLING 200+ nodes Nobody understands the full picture. BREAKING Multi-site Replication is manual & fragile. COMPLEXITY OVER TIME → The inflection point is usually around 50–100 nodes

The Seven Problems

ProblemWhat HappensReal-World Impact
No version control Flows are stored as a JSON blob in Node-RED's runtime database. There's no native git integration, no commit history, no branches. Someone changes a flow on Friday afternoon. Monday morning it's broken. Nobody knows what changed. You can export the JSON and diff it, but a 5,000-line JSON diff is unreadable.
No code review Changes go live the moment you click Deploy. There's no pull request, no approval process, no second pair of eyes. A well-intentioned change to the stoppage classification logic silently breaks the KPI calculation. Nobody catches it for two weeks because there's no review process.
Single runtime All flows run in one Node.js process. One bad function node — an infinite loop, an unhandled exception, a memory leak — crashes everything. Your state tracking, your historian, your KPI calculation, your dashboard — all down because one experimental node had a bug. At 3am. On a Saturday.
Untestable There's no standard way to unit test a Node-RED flow. You can't run automated tests before deploying. You can't validate that a change doesn't break existing behaviour. Every deployment is a manual test. "Deploy and pray." The more complex the flow, the more likely a change has unintended side effects.
Multi-site replication To deploy the same logic to 5 factories, you export the flow JSON and import it at each site. Configuration differences (broker addresses, machine names) require manual editing. Site 3 is running a version from last month. Site 5 has a local modification that nobody documented. Keeping them in sync is a full-time job.
Team collaboration Two people editing the same flow at the same time will overwrite each other's changes. There's no merge, no conflict resolution. The automation engineer and the IT engineer both need to make changes. One deploys, the other deploys, the first person's changes are gone.
Debugging at scale A flow with 200 nodes across 8 tabs becomes a visual maze. Following data through the flow requires clicking into each node to read its configuration. When something goes wrong, you're clicking through nodes one by one trying to find where the data path diverges. The visual advantage becomes a visual nightmare.
The trap: These problems don't appear during the prototype phase. They appear after you've committed to the approach, trained your team on it, and built months of logic on top of it. By then, migration feels expensive — but staying is more expensive.
🔧

Manufacturing Engineer

You built the original flows and they work great for your line. But now management wants the same thing across all 4 sites, and the IT team is asking how to maintain it. The flows that were your strength are becoming your bottleneck — you're the only person who understands them.

💻

Developer

You've been asked to "productionise" the Node-RED flows. But there's no test suite, no CI/CD, no way to do a code review. The business logic is scattered across function nodes with names like "Function 3" and "transform data". You'd rather rewrite it than maintain it.

🖥️

IT Engineer

You need to run this across multiple sites with consistent configuration, monitoring, and update procedures. But Node-RED is a single process with no health checks, no independent scaling, and no way to update one piece of logic without redeploying everything. Your change management process doesn't work with "click Deploy."

Why GitOps Wins for Production

GitOps means git is the single source of truth for your system. Every function, every configuration, every deployment is a git commit. This isn't a philosophical preference — it's a practical requirement for production manufacturing systems.

Head-to-Head Comparison

CapabilityNode-REDfn-uns + GitOps
Version historyExport JSON manually, diff is unreadableFull git log — every change, every author, every reason
BranchingNot possible — one runtime, one versionStandard git branches — develop features in isolation
Code reviewNot possible — changes go live on DeployPull requests with line-by-line diffs and approval
RollbackManual — re-import a previous JSON export (if you have one)git revert + git push — instant, safe, audited
TestingManual — deploy and checkUnit tests, integration tests, CI/CD pipelines
Failure isolationOne bad node crashes everythingEach function is its own container — failures are isolated
Multi-site deployExport/import JSON per site, manual config changesgit push to each site — identical, reproducible
CollaborationShared runtime — last deploy winsBranches + merge — parallel work without conflicts
Audit trailPlatform-dependent, often noneGit commits with author, date, message, and diff
ReproducibilityImport JSON, hope dependencies matchgit clone + docker compose up — identical environment every time
OnboardingClick through nodes to understand the flowRead the code, read the README, run the tests

The Deployment Workflow

Node-RED Deployment

Open the editor → make changes → click Deploy → hope it works → if it doesn't, try to remember what you changed → manually revert by re-importing an old export (if you saved one). No approval. No review. No rollback safety net.

fn-uns Deployment

Edit the function → test locally with docker compose up → commit with a message explaining why → push → fnkit builds and deploys automatically. To rollback: git revert + git push.

# The fn-uns deployment workflow

# 1. Make a change
vim uns-state/function.go

# 2. Test locally
cd uns-state && docker compose up -d
curl http://localhost:8080/uns-state | jq

# 3. Commit with context
git add .
git commit -m "add MTBF calculation to state tracker

Reads ACTIVE→ALARM transitions from uns_state table
and computes mean time between failures per machine.
Requested by maintenance team for predictive scheduling."

# 4. Push to deploy
git push deploy main

# 5. If something goes wrong
git revert HEAD
git push deploy main
# → Previous version is running again in seconds
Key insight: Every change has an author, a timestamp, a reason, and a complete diff. Six months from now, you can answer "who changed the KPI calculation and why?" in seconds.

The Migration Path

You don't throw away your Node-RED work. The logic you prototyped in Node-RED becomes the specification for your production functions. Every flow maps to a function.

From Flow to Function

NODE-RED FLOW → FN-UNS FUNCTION Node-RED Flow State tracking flow — 8 nodes MQTT In Function Switch Compare Duration PostgreSQL Logic: embedded in function nodes Config: node properties dialogs Storage: flows.json (runtime DB) Deploy: click Deploy button becomes uns-state/ State tracking function — 150 lines Go ├── function.go business logic ├── Dockerfile container def ├── docker-compose.yml service config ├── .env.example env template ├── go.mod dependencies └── README.md documentation Logic: readable Go code in function.go Deploy: git push → auto-build → running

The Mapping

Every Node-RED concept has a direct equivalent in fn-uns:

Node-RED Conceptfn-uns EquivalentWhat Changes
Flow (tab)Function directoryEach flow becomes its own directory with its own container
MQTT-in nodeuns-frameworkOne Go program subscribes to all topics and caches in Valkey
Function nodefunction.go / index.jsThe JavaScript snippet becomes a proper program with error handling
Context variablesValkey cacheIn-memory context becomes a shared, persistent cache
PostgreSQL nodeDatabase calls in codeSQL is in the same file as the logic — visible, testable, version-controlled
Dashboard nodesuns-dashboard (Grafana)Grafana dashboards with SQL queries, version-controlled as JSON
Deploy buttongit pushDeployment is audited, reversible, and automated
Flow export (JSON)Git repositoryFull history, branching, merging, code review
i
You don't migrate all at once. Start by moving the most critical flow — usually state tracking — to a function. Run both in parallel. Once you're confident, move the next flow. The MQTT broker doesn't care whether a subscriber is Node-RED or a Go container.

fn-uns Equivalents

Here's how fn-uns handles every common Node-RED UNS pattern — and what you gain by making the switch.

MQTT Subscribe & Cache

Node-RED

MQTT-in node → function node that stores flow.set('prev_' + topic, value) in context. Previous values are lost on restart. Context is local to the Node-RED instance.

fn-uns: uns-framework

Go program subscribes to v1.0/#, writes current + previous to Valkey. Persistent across restarts. Shared across all functions. Includes metadata (message count, first_seen, last_updated).

State Change Detection

Node-RED

Function node compares msg.payload.state to flow.get('last_state'). Duration calculated with Date.now() - flow.get('state_since'). Logic is 15 lines of JavaScript inside a node config dialog.

fn-uns: uns-state

150-line Go program with proper error handling, in-memory state tracker per machine, precise duration calculation, and automatic table creation. Reads from Valkey, writes completed states to PostgreSQL with state, duration_s, and next_state.

Stoppage Classification

Node-RED

Switch node routes by state → function nodes map IDLE→NO_WORK, ALARM→FAULT, etc. Operator override requires a separate dashboard input flow wired back to the database. Classification logic is split across multiple nodes.

fn-uns: uns-stoppage

Single Node.js function that auto-classifies stoppages from the uns_state table and provides a POST endpoint for operator overrides. All classification logic in one file. The mapping is a simple object — easy to read, easy to change, easy to review.

KPI Computation

Node-RED

Multiple function nodes with embedded SQL queries, each computing one metric. Results wired to dashboard gauges. Adding a new KPI means adding more nodes and wiring. The SQL is hidden inside node configs — invisible unless you click into each one.

fn-uns: uns-kpi

One Go function (250 lines) that computes all KPIs in a single HTTP request: utilisation, availability, throughput, MTBF, MTTR, stoppage pareto. Filterable by machine, area, and time range. Returns structured JSON. All SQL visible in one file.

Dashboards

Node-RED Dashboard

Built-in dashboard nodes — gauges, charts, buttons. Quick to set up but limited in capability. Dashboard layout is part of the flow — changes to the dashboard risk breaking the data flow. Not version-controlled separately.

fn-uns: Grafana

5 Grafana dashboards querying PostgreSQL directly with SQL. Professional-grade visualisation with time-series, state timelines, pareto charts. Dashboard JSON is version-controlled. Dashboards are completely decoupled from the data pipeline.

The Scaling Story

The real difference between flow-based and function-based approaches becomes clear when you scale. Here's what happens at each stage:

1 Machine → 10 Machines

AspectNode-REDfn-uns
EffortDuplicate nodes or use wildcard subscriptions. Manageable.No change needed — wildcard subscription and topic parsing handle it automatically.
ComplexityFlow grows but stays readable. 30–50 nodes.Same 12 functions. Zero additional complexity.
VerdictBoth approaches work fine at this scale.Both approaches work fine at this scale.

10 Machines → 100 Machines

AspectNode-REDfn-uns
EffortFlows need restructuring. Multiple tabs. Performance tuning. The single Node.js process starts to strain under message volume.No change needed. Valkey handles the cache volume. PostgreSQL handles the write volume. Functions are stateless.
Complexity200+ nodes. Multiple tabs. Hard to follow data paths. Context variables proliferate.Same 12 functions. Topic parsing extracts machine identity automatically.
VerdictManageable but painful. You're spending time on flow management, not on business logic.No additional work. The architecture was designed for this.

1 Site → 5 Sites

AspectNode-REDfn-uns
DeploymentExport flow JSON → import at each site → manually edit broker addresses, machine names, database credentials. Repeat for every update.git clone at each site. Site-specific config in .env files. Updates via git pull.
ConsistencyEach site drifts. Local modifications accumulate. No way to verify all sites run the same version.Git ensures every site runs the same code. git log shows exactly what version each site is on.
UpdatesManual process per site. High risk of human error. No rollback.git pull && docker compose up -d at each site. Rollback with git revert.
VerdictThis is where flow-based approaches break down. Multi-site management becomes a full-time job.Multi-site is just git operations. The same workflow your IT team already uses for every other system.
🔧

Manufacturing Engineer

At one site with a few machines, Node-RED is your friend. But when the plant manager says "roll this out to all our sites," you need something that scales without requiring you to manually replicate and maintain flows at each location. fn-uns gives you that — the same logic, deployed consistently, everywhere.

💻

Developer

Multi-site deployment with Node-RED means building custom tooling to export, transform, and import flows — essentially building a deployment pipeline for a tool that wasn't designed for one. With fn-uns, you use git and Docker — tools you already know, with ecosystems you already trust.

🖥️

IT Engineer

Your change management process requires audit trails, approval workflows, and rollback procedures. Git gives you all three out of the box. Node-RED's "click Deploy" doesn't fit into any enterprise change management framework. fn-uns deployments are just git operations — they slot into your existing ITIL processes.

Which Approach Should You Use?

This isn't a binary choice. The right tool depends on where you are in your UNS journey.

Use Node-RED When:

Use fn-uns + GitOps When:

The Recommended Path

1. EXPERIMENT Node-RED Prove the concept 2. VALIDATE Node-RED + fn-uns Run both in parallel 3. PRODUCTION fn-uns + GitOps Scale with confidence 4. SCALE Multi-site git push to deploy Each step builds on the previous — your Node-RED prototype becomes the spec for your production functions

The Bottom Line

Node-RED is an excellent experimentation tool. Use it to prove your UNS concept, validate the data flow, and demonstrate business value. It's the fastest path from "idea" to "working demo."

But when your UNS becomes production infrastructure — when the business depends on it, when multiple people maintain it, when it needs to run reliably across multiple sites — you need the things that flow-based programming can't give you: version control, code review, failure isolation, automated testing, and safe rollbacks.

That's what fn-uns and GitOps provide. Not a different way to do the same thing — a better foundation for the same logic you already proved works.

The best UNS implementations start with Node-RED and graduate to GitOps. The prototype proves the value. The production system delivers it reliably, at scale, with confidence.

Guide Version: 1.0 · Applies To: fn-uns pipeline, Node-RED comparison

Last updated March 2026.