Overview
The SIMATIC IOT2000 family (IOT2020 and IOT2040) is a Siemens edge gateway intended to sit between the OT network and a higher-level MES or engineering host. The IOT2020 uses an Intel Quark SE x86 CPU with 1 GB of RAM; the IOT2040 uses the Quark x1020 with the same RAM and adds a second Ethernet port, RS232/RS485, and battery-backed RTC. Both ship with a Yocto-based Linux image (Siemens Example Image V2.6.x or V3.x) that includes Node-RED 0.20 or newer, but not the CIFS client tools. Out of the box the IOT2000 cannot attach a Windows SMB share, and the built-in Node-RED file in and file out nodes operate exclusively on local filesystem paths.
This article documents the production-grade procedure for letting a Node-RED flow read and write files that physically live on a Windows host: prepare the SMB share, install or build cifs-utils on the IOT2000, perform a one-shot mount, drive the share from a Node-RED flow, and finally make the mount persistent across reboots. Each step gives the exact command, the expected output, the error codes that appear when the step fails, and the field-tested fix. The article ends with a troubleshooting matrix, security analysis, and an alternative-architectures table that covers FTP, SFTP, HTTP, MQTT, and Git-based distribution.
Prerequisites and Components
| Component | Version / Specification | Notes |
|---|---|---|
| SIMATIC IOT2020 | Intel Quark SE x86, 1 GB DDR3, 8 MB flash, SD card boot | Single Ethernet, USB 2.0, RS232/485 |
| SIMATIC IOT2040 | Intel Quark x1020, 1 GB DDR3, 8 MB flash, SD card boot | Dual Ethernet, USB 2.0, RS232/485, RTC |
| Siemens Example Image | V2.6.0 or V3.x (Yocto Morty / Kirkstone) | Node-RED 0.20–3.x pre-installed |
| Node-RED version | 0.20.x – 3.x | Built-in file nodes; no extra palette required |
| cifs-utils | 6.10 – 6.15 | Provides mount.cifs, smbclient, cifs.upcall
|
| Windows host | Windows 10 1709+, Windows 11, Server 2016/2019/2022 | SMB2 or SMB3 enabled, SMB1 disabled |
| Network | TCP/IP, layer-2 reachable | Port 445/TCP open between IOT2000 and the share host |
| Build toolchain (only for source build) | gcc, make, autoconf, automake, libtool, pkg-config, gettext, libcap-ng-devel, libkrb5-devel, keyutils-devel, talloc-devel, popt-devel, attr-devel, samba-libs | Provided by the IOT2000 development SDK or by opkg install on a development image |
| Access method | SSH (port 22) or serial console | Default user root on the Yocto image; password set on first boot |
Before touching the Linux side, validate the network path. From the IOT2000 shell:
ping -c 3 192.168.10.99
nc -zv 192.168.10.99 445
getent hosts recipes-host
If nc -zv reports Connection refused or Connection timed out, the issue is the Windows firewall or the SMB service, not Linux. Resolve the network layer first.
SMB / CIFS Protocol Background
Server Message Block (SMB) is the file-sharing protocol implemented by Windows. Common Internet File System (CIFS) is the historical dialect and the name Linux adopted for its client implementation. Modern mount.cifs negotiates SMB2 or SMB3 when the server advertises them. The relevant version is selected with the vers= mount option. The valid values are 1.0, 2.0, 2.1, 3.0, and 3.1.1. SMB1 was deprecated by Microsoft in 2014 and removed from cifs-utils in version 6.11. A 2024-era IOT2000 image should never negotiate SMB1.
| Mount option | Effect | Recommended value |
|---|---|---|
vers= |
Forces SMB protocol version |
3.0 or 3.1.1
|
sec= |
Authentication and signing mode |
ntlmssp for Windows passwords; krb5 for Active Directory |
username=, password=
|
Inline credentials (insecure) | Avoid in production; use credentials= file |
credentials= |
Path to a file with username, password, optionally domain
|
/etc/samba/iotcred with mode 0600 |
uid=, gid=
|
Owner UID/GID of mounted files |
uid=1000,gid=1000 for the Node-RED user |
iocharset= |
Filename character set | utf8 |
file_mode=, dir_mode=
|
Permission bits on files / directories |
0644 / 0755
|
noserverino |
Disable server-side inode generation | Recommended for Windows shares; improves compatibility |
_netdev |
Defer mount until the network is up | Required in fstab entries |
cache= |
Cache mode: none, strict, loose, singleclient
|
loose for typical office-to-edge traffic |
soft / hard
|
Behavior on I/O timeout |
soft for industrial networks; hard can hang processes when the share disappears |
timeo= |
I/O timeout in centiseconds (default 1000 = 10 s) |
timeo=3000 (30 s) for slow links |
retrans= |
Number of retries before giving up |
retrans=2 with soft
|
x-systemd.automount |
Mount on first access instead of at boot | Recommended on systemd-equipped images |
Step 1: Configure the Windows SMB Share
- Create a folder on the Windows host that will hold the recipes, for example
D:\Recipes. - Right-click the folder, choose Properties → Sharing → Advanced Sharing, tick Share this folder, and set a share name such as
Recipes. The resulting UNC path is\\HOSTNAME\Recipesor\\192.168.10.99\Recipes. - Click Permissions, add the Windows account that the IOT2000 will use, and grant at minimum Read. For write access from Node-RED, grant Change or Full Control.
- Open Control Panel → Programs and Features → Turn Windows features on or off and confirm that SMB 1.0/CIFS File Sharing Support is disabled. Enable SMB Direct if your network adapter supports RDMA.
- Open the Windows Defender Firewall with Advanced Security console and confirm that the inbound rule File and Printer Sharing (SMB-In) is enabled on the active profile (Domain, Private, or Public).
- Test from a second Windows machine:
\\192.168.10.99\Recipesshould open, prompt for credentials, and list the folder contents. - From a Windows PowerShell prompt, validate the share with
Get-SmbShare -Name Recipes. The output should report the share path, the description, and the scope.
Microsoft's official procedure for sharing a folder over a network is documented at File sharing over a network in Windows.
mount.cifs client on cifs-utils 6.11 or later also removed SMB1 support. If you observe mount error(13): Permission denied with no other apparent cause, the share is most likely advertised only as SMB1. Enable SMB2/SMB3 on the Windows host rather than re-enabling SMB1.Step 2: Install cifs-utils on the IOT2000
The stock Siemens IOT2000 image does not ship cifs-utils. Two options exist.
Option A: Use a custom image that bundles cifs-utils
Some Siemens Application Examples deliver example images that bundle cifs-utils along with smbclient and several other utilities. If your project allows, use such an image; it is the lowest-risk path. Verify the package is present with:
which mount.cifs smbclient cifs.upcall
mount.cifs --version 2>&1 | head -1
Option B: Build cifs-utils from source on the device
When the production image is frozen, build the package in place. The build needs the development headers. On a Yocto-based IOT2000 image with the package feed enabled:
opkg update
opkg install gcc make autoconf automake libtool pkg-config gettext \
libcap-ng-dev libkrb5-dev keyutils-dev talloc-dev popt-dev attr-dev
Download the cifs-utils release archive and transfer it to the IOT2000 (for example with WinSCP, scp, or the SD card). Connect to the device over SSH or via the serial console:
cd /tmp
tar -xzf cifs-utils-<version>.tar.gz
cd cifs-utils-<version>
autoreconf -i
./configure --prefix=/usr --sysconfdir=/etc
make -j$(nproc)
make install
Expected output ends with lines such as libtool: install: /usr/bin/install -c mount.cifs /usr/sbin/mount.cifs. Verify the install with:
which mount.cifs
ls -l /usr/sbin/mount.cifs /usr/sbin/umount.cifs
mount.cifs -V
mount.cifs and umount.cifs binaries must carry the setuid bit for non-root users (including the Node-RED service user) to mount and unmount shares. If ls -l shows -rwxr-xr-x, run:chmod u+s /usr/sbin/mount.cifs /usr/sbin/umount.cifs
chown root:root /usr/sbin/mount.cifs /usr/sbin/umount.cifs
Without the setuid bit, the Node-RED exec node will fail with Operation not permitted even when the credentials file is in place.
Step 3: Mount the Share from the Linux Shell
Create the mount point and a credentials file so passwords do not appear in shell history, process listings, or the systemd journal.
mkdir -p /mnt/recipes
cat > /etc/samba/iotcred <<'EOF'
username=iotuser
password=YourSecurePassword
domain=WORKGROUP
EOF
chmod 0600 /etc/samba/iotcred
chown root:root /etc/samba/iotcred
Perform a manual mount to validate the configuration end to end:
mount -t cifs //192.168.10.99/Recipes /mnt/recipes \
-o credentials=/etc/samba/iotcred,vers=3.0,sec=ntlmssp,\
uid=1000,gid=1000,iocharset=utf8,noserverino,soft,timeo=3000
A successful mount returns to the prompt with no output. Verify the mount is in place:
mount | grep cifs
ls -l /mnt/recipes
stat /mnt/recipes
The mount command should print a line similar to:
//192.168.10.99/Recipes on /mnt/recipes type cifs (rw,relatime,vers=3.0,sec=ntlmssp,uid=1000,gid=1000,...)
If the line is missing, the mount failed. Run dmesg | tail -20 to read the kernel log, which contains the underlying CIFS error message. The table in Step 5 covers the most common failure modes.
To unmount during testing:
umount /mnt/recipes
If the share is a guest share (no password), replace the credentials file with the inline options guest,uid=1000,gid=1000 and remove sec=ntlmssp. If the share lives on a Windows host in an Active Directory domain, use sec=ntlmssp with domain=YOURDOMAIN in the credentials file, or migrate to sec=krb5 with a valid /etc/krb5.keytab for full Kerberos authentication.
Step 4: Build the Node-RED Flow
Once the share is mounted locally, Node-RED's built-in file in and file out nodes read and write the recipes without any custom node or extra palette.
4.1 Read a recipe on a trigger
- Drag an
injectnode onto the canvas, set Payload to a timestamp string, and set Repeat to a 5-minute interval (interval with 300 seconds, or a cron expression). - Drag a
file innode, set Filename to/mnt/recipes/current.csv, and leave Format as utf-8 string. - Drag a
debugnode. - Wire
inject→file in→debug. - Click Deploy. Each inject tick produces a debug message containing the current recipe CSV.
4.2 Write a recipe from an HTTP upload
- Add an
http innode configured asPOST /uploadwith File upload enabled. - Add a
functionnode that sanitises the filename and moves the buffer to the payload: - Add a
file outnode, leave its Filename property empty so it reads frommsg.filename, and set the action to overwrite file. - Add an
http responsenode with status 200. - Wire
http in→function→file out→http response.
4.3 Sample function node
// Build a safe filename
var name = (msg.payload && msg.payload.name) || "recipe.csv";
// Strip path components to avoid directory traversal
name = name.replace(/^.*[\\\/]/, "");
// Replace unsafe characters
name = name.replace(/[^A-Za-z0-9._-]/g, "_");
// Refuse empty or hidden names
if (!name || name.charAt(0) === ".") {
node.warn("Rejected filename: " + name);
msg.payload = "invalid filename";
msg.statusCode = 400;
return [null, msg];
}
msg.filename = "/mnt/recipes/" + name;
if (msg.payload && msg.payload.buffer) {
msg.payload = msg.payload.buffer;
}
return msg;
.. path traversal and strips shell metacharacters. For production, write to a staging directory (/var/staging/) and have a separate Node-RED flow move the file into /mnt/recipes/ only after validation. Keep the staging directory outside the cifs mount to avoid leaving partial files visible to the Windows host.4.4 Listing the share contents
The built-in file in node does not list directories. Use the exec node to call ls -1 /mnt/recipes and parse the result in a function node:
var lines = (msg.payload || "").split("\n");
var files = [];
for (var i = 0; i < lines.length; i++) {
var n = lines[i].trim();
if (n.length > 0) files.push(n);
}
msg.payload = files;
return msg;
Feed the resulting array to a dropdown UI node so the operator can pick a recipe from a list rendered by the Dashboard. For real-time updates, place the exec node inside a flow that fires when a file-system change occurs; on Linux this is best done with inotifywait in a polling loop, since cifs shares do not deliver native inotify events.
4.5 Detecting changes written by the Windows host
The cifs client uses the kernel's directory entry cache, which by default holds entries for 30 seconds. Add the cache=loose option to the mount if the operator changes recipes interactively on the Windows side and the IOT2000 must see the change within a few seconds. For sub-second freshness, run an exec node every second that does ls -1 /mnt/recipes and compares the output to the previous tick.
Step 5: Persist the Mount Across Reboots
A mount performed with mount -t cifs does not survive a reboot. The two field-proven approaches are fstab and a systemd unit with retry logic.
5.1 /etc/fstab entry
Add a single line to /etc/fstab:
//192.168.10.99/Recipes /mnt/recipes cifs credentials=/etc/samba/iotcred,vers=3.0,sec=ntlmssp,uid=1000,gid=1000,iocharset=utf8,noserverino,soft,timeo=3000,_netdev,x-systemd.automount 0 0
The _netdev option tells systemd to wait for the network before attempting the mount. On systemd-equipped IOT2000 images (V3.x and newer), the x-systemd.automount option mounts the share on first access, which eliminates boot-time race conditions when the Windows host takes longer to come up than the IOT2000.
Test the fstab entry without rebooting:
systemctl daemon-reload
mount -fav 2>&1 | grep recipes
mount -a
The dry run reports what would be mounted; the actual mount happens on mount -a or on the next reboot.
5.2 Autostart script (rc.local / systemd unit)
For older images without full fstab support, or when the share is not always reachable, a small autostart script with retry logic is more robust. Create /opt/scripts/mount-recipes.sh:
#!/bin/sh
# mount-recipes.sh - retry mount of Windows share on boot
LOG=/var/log/mount-recipes.log
SHARE="//192.168.10.99/Recipes"
MNT="/mnt/recipes"
OPT="credentials=/etc/samba/iotcred,vers=3.0,sec=ntlmssp,uid=1000,gid=1000,soft,timeo=3000"
echo "=== mount-recipes start $(date) ===" >> "$LOG"
if mount | grep -q "${MNT} type cifs"; then
echo "already mounted" >> "$LOG"
exit 0
fi
for attempt in 1 2 3 4 5 6 7 8 9 10; do
if mount -t cifs "$SHARE" "$MNT" -o "$OPT" >> "$LOG" 2>&1; then
echo "mounted on attempt $attempt" >> "$LOG"
exit 0
fi
echo "attempt $attempt failed, sleeping 10s" >> "$LOG"
sleep 10
done
echo "FAILED after 10 attempts at $(date)" >> "$LOG"
exit 1
Make it executable and wire it into systemd:
chmod +x /opt/scripts/mount-recipes.sh
mkdir -p /opt/scripts
cat > /etc/systemd/system/mount-recipes.service <<'EOF'
[Unit]
Description=Mount Windows Recipes share
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=300
StartLimitBurst=5
[Service]
Type=oneshot
ExecStart=/opt/scripts/mount-recipes.sh
RemainAfterExit=yes
Restart=on-failure
RestartSec=30
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable mount-recipes.service
systemctl start mount-recipes.service
Verify the service is active and the share is mounted:
systemctl status mount-recipes.service
journalctl -u mount-recipes.service --no-pager | tail -20
mount | grep recipes
5.3 Re-issuing the mount from Node-RED
For a more dynamic pattern, drive the mount from a Node-RED exec node. The mount command requires root, so Node-RED must run as root, or the binary must be setuid. Running all of Node-RED as root is a poor security practice. The recommended approach is a small setuid wrapper that only accepts a known share and mount point.
cat > /usr/local/bin/mount-recipes <<'EOF'
#!/bin/sh
# mount-recipes - fixed share and mount point, setuid root
exec /usr/sbin/mount.cifs //192.168.10.99/Recipes /mnt/recipes \
-o credentials=/etc/samba/iotcred,vers=3.0,uid=1000,gid=1000,soft,timeo=3000
EOF
chmod 4755 /usr/local/bin/mount-recipes
chown root:root /usr/local/bin/mount-recipes
The Node-RED exec node then invokes /usr/local/bin/mount-recipes with no arguments. If you need the password supplied dynamically, pass it on stdin rather than on the command line. The exec node can be configured to send the password string to the spawned process's stdin, where mount.cifs will read it during the password prompt phase.
Verification and Acceptance Test
-
Share reachability. From the IOT2000, run
nc -zv 192.168.10.99 445and confirm the port is open. -
Mount present. Run
mount | grep recipes. The output must include a cifs line with the expected options. -
Read path. Deploy a
file innode reading/mnt/recipes/current.csvand observe the debug payload. The payload must match the file on the Windows host. -
Write path. Trigger a
file outnode writing a known string. From a second Windows machine, runGet-ChildItem \\192.168.10.99\Recipesin PowerShell. The file size and timestamp must update. -
Persistence. Run
rebooton the IOT2000. Wait 60 seconds, log in over SSH, and repeat steps 2–4 without manual mounting. The systemd unit or fstab must have remounted the share. -
Error handling. Power off the Windows host (or block port 445 with a firewall rule). Within 30 seconds, the
mount-recipesservice should report retries in the journal. The Node-REDfile innode should report an error in its status badge and the debug tab. -
Recovery. Restore the Windows host. Within 30 seconds, the next retry or the next access to
/mnt/recipesshould re-establish the mount.
Performance Tuning
The cifs client exposes several tunables that affect throughput and freshness on a plant network.
| Parameter | Default | Tuning recommendation | Effect |
|---|---|---|---|
cache= |
loose |
none for live data; loose for recipes that change rarely |
Tightens or relaxes the attribute and directory-entry cache |
rsize=, wsize=
|
131072 (128 KiB) on SMB2/3 | Default; reduce to 32768 on lossy wireless links | Maximum read/write block size per request |
actimeo= |
10000 (10 s) on SMB2/3 | Reduce to 1–2 seconds for live data | Attribute cache timeout in seconds |
echo_interval= |
60 s | Lower to 30 s on flaky links | How often the client sends an SMB echo to keep the session alive |
max_credits= |
320 on SMB3 | Lower to 128 on memory-constrained IOT2020 | Maximum number of in-flight requests; lower value reduces memory pressure |
Number of Node-RED file in triggers |
n/a | Avoid sub-second polling; batch reads | Reduces SMB traffic and CPU load on the IOT2000 |
For multi-megabyte recipe files, the bottleneck is almost always the kernel's copy-from-user path, not the wire. A 1 MB read on a 100 Mbit link completes in well under a second; a 50 MB file can take 5–10 seconds and block the Node-RED event loop. Use streaming writes via the exec node and dd for files larger than a few megabytes.
Troubleshooting Matrix
| Symptom | Likely cause | Resolution |
|---|---|---|
| mount error(2): No such file or directory | Wrong UNC path or share not published | Validate the share from another Windows host; check for typos in the path |
| mount error(13): Permission denied | Wrong credentials, no share permission, or SMB1 fallback | Re-test credentials, grant share permission, force vers=3.0
|
| mount error(22): Invalid argument | Unsupported sec= or vers= value |
Try sec=ntlmssp, vers=3.0; check cifs-utils version |
| mount error(112): Host is down | Port 445 blocked, host offline, or wrong IP |
nc -zv HOST 445 from the IOT2000; check firewall on Windows |
Password for user@//host/share: prompt in exec
|
cifs-utils falling back to interactive auth | Provide credentials= file or pipe password via stdin to mount.cifs |
Mount present, but Node-RED file in reports ENOENT
|
Wrong path, permission bit, or SELinux/AppArmor |
ls -l /mnt/recipes/<file> as the Node-RED user; check file_mode
|
| Mount present, but files are empty or stale | Kernel attribute cache or cache= option |
Add cache=loose or actimeo=1 for better freshness |
| Mount disappears after reboot | No persistence configured | Add fstab entry or systemd unit per Step 5 |
| Windows 10 cannot see the share from itself | SMB1 disabled and client requesting SMB1 | Enable SMB2 on the client via Windows features; use the host's IP rather than hostname |
cifs-utils not found when running mount -t cifs
|
Package not installed or not in image | Build cifs-utils per Step 2 or switch to a custom image |
| High CPU on the IOT2000 while idle | cifs kernel module retry loop after host went down | Add soft and timeo=3000 options; do not use hard on industrial networks |
| Mount succeeds but every read returns Permission denied |
uid=/gid= mismatch with Node-RED user |
Check Node-RED's running uid with ps -o user,pid,cmd -C node-red; set matching uid=/gid=
|
| Reboot leaves share in a half-mounted state | fstab entry without _netdev
|
Add _netdev and x-systemd.automount; or use the systemd unit in Step 5.2 |
| Filename appears as garbled characters on Linux | Windows codepage mismatch | Add iocharset=utf8 to the mount options |
| Mount succeeds but file modifications are not visible to the Windows host | Local kernel cache holding the dirty page | Add cache=none or call sync from the application |
| PowerShell shows the share but Linux shows it as read-only | Windows shared NTFS permissions differ from share permissions | Align NTFS permissions with share permissions; the more restrictive of the two applies |
Security Considerations
Sharing files between a production control system and a Windows workstation is a deliberate act and must be evaluated against the IEC 62443 zone and conduit model. Treat the Windows host as a higher-trust zone, the IOT2000 as a cell-site controller, and the SMB session as a conduit that crosses the boundary.
-
Credentials storage. The credentials file at
/etc/samba/iotcredmust be mode 0600, owned by root. Never embed credentials in Node-RED flow JSON files; export flows to an offline editor and check the JSON for accidental leakage before commit. -
Transport encryption. SMB3 with
sec=ntlmsspsigns but does not encrypt traffic. For sensitive data, requiresec=ntlmsspover an isolated VLAN, or migrate the recipes to SFTP/HTTPS with mutual TLS. -
Protocol version. Pin
vers=3.1.1when the Windows host supports SMB3.1.1 encryption. Addsealorsignin thesec=option on cifs-utils ≥ 6.13 to enforce signing. -
Account scope. Create a dedicated Windows user
iotuserwith the narrowest possible share rights and an unguessable password. Disable interactive logon for the account in Active Directory Users and Computers. - Network segmentation. Place the IOT2000 and the recipe host in the same engineering VLAN. Do not route SMB across the process control network.
-
Audit. Enable Windows security auditing for logon and file access on the share. Forward events to the central SIEM. On the IOT2000, log all
mountandumountinvocations to/var/log/messageswithauditdrules. - File integrity. If the recipes drive safety-relevant control logic, compute a SHA-256 hash of the file on the Windows side and verify it on the IOT2000 in the Node-RED flow before consuming the data.
For a high-level primer on configuring SMB sharing on Windows, review Microsoft's File sharing over a network in Windows guide before exposing the share to the IOT2000.
Alternative Architectures
CIFS is convenient but not always the right tool. Consider the alternatives below when the recipe set grows, when the Windows host must be replaced by a version control system, or when the IOT2000 needs to be portable across plants.
| Option | Pros | Cons | Recommended when |
|---|---|---|---|
Node-RED file in/out + cifs mount (this article) |
Zero custom code, built-in nodes, no protocol work in the flow | Inherits SMB weaknesses, single-host coupling, no version control | Small to mid-size recipe set, single Windows host, no compliance constraint |
| FTP / SFTP client in Node-RED | Encrypted transport, mature tooling, audit logs on the server | Adds an FTP server to the Windows host or replaces it with a NAS | Recipes traverse plant boundaries, audit required |
| HTTP / REST with bearer token | Firewall-friendly, scales to many devices, easy to monitor | Requires a small HTTP service on the Windows host or a middleware box | Many IOT2000 units consume the same recipe set |
| MQTT publish / subscribe | Real-time push, no polling, fits the Node-RED model | Requires a broker and a publisher on the Windows side | Recipes change often and the HMI should react in <1 s |
Git pull via Node-RED exec
|
Versioned recipes, branch per product, signed commits | Adds a developer workflow; Windows-side tooling | Recipes are released as part of the product |
| OPC UA method call from a Windows service | Native to the SIMATIC ecosystem, signed and encrypted | Requires a Windows-side OPC UA server and Node-RED OPC UA client | The same data is also consumed by TIA Portal or WinCC |
The IOT2000 has enough CPU and RAM to run a small HTTP or MQTT client natively, so any of the alternatives can be implemented without the cifs-utils dependency. For pure local file access on the IOT2000 itself, leave the recipes on the SD card and let a service on the Windows host push changes through HTTP or SFTP.
Operational Notes
A few additional points observed in the field.
- The cifs module is built into the standard IOT2000 kernel, so
modprobe cifsis rarely required. Iflsmod | grep cifsreturns nothing, the kernel on your image does not include cifs support and you must rebuild the kernel or switch to a custom image. - The IOT2020 has 1 GB of RAM. A long-running cifs session that reads large files can fragment the page cache and starve Node-RED. Set
vm.swappiness=10in/etc/sysctl.d/to favour dropping cache over swapping. - DNS resolution on the IOT2000 is sometimes slow when the engineering VLAN does not advertise a DNS server. Use the IP address in the UNC path or add the Windows host to
/etc/hoststo avoidmounthangs during boot. - When the IOT2000 is in a DMZ and the Windows host is inside the OT network, an SMB session is typically allowed only inbound. The mount direction (IOT2000 → Windows) is supported by the IOT2000's default firewall, so no extra rule is needed.
- The mount survives a Windows reboot on the share host if
softandtimeo=3000are set. The next access after the Windows host returns re-establishes the session automatically.
FAQ
Do I need a custom Node-RED node to read a Windows share?
No. Mount the share with cifs-utils and reference the local mount point in the built-in file in and file out nodes. The mount is a Linux concept; Node-RED only sees a normal directory path such as /mnt/recipes.
Where do I put the username and password?
Store them in a credentials file at /etc/samba/iotcred with mode 0600 and pass credentials=/etc/samba/iotcred as a mount option. Do not embed credentials in the Node-RED flow JSON, the systemd unit, or the command line, where they would appear in process listings and logs.
Why does the mount fail with Permission denied on a fresh Windows 10 install?
Windows 10 1709 and newer disable SMB1 by default, and modern cifs-utils no longer fall back to SMB1. Add vers=3.0 (or 3.1.1) to the mount options, and verify that SMB2/SMB3 is enabled on the Windows host under Windows features.
How do I make the mount survive an IOT2000 reboot?
Either add a line to /etc/fstab with the _netdev and x-systemd.automount options, or create a systemd oneshot service that calls mount -t cifs after network-online.target and enables retry on failure. Both approaches are detailed in Step 5.
Can I trigger the mount directly from a Node-RED flow?
Yes, by using the exec node to call a small setuid wrapper script that mounts only a fixed share and mount point. Avoid passing the password on the command line; pipe it on stdin or use a credentials file with mode 0600.
What is the recommended SMB protocol version in 2024?
SMB3.1.1 (vers=3.1.1) is the current default on Windows 11 and Server 2022. It supports AES-128 encryption when paired with sec=ntlmssp on a cifs-utils build that supports signing. SMB2.1 (vers=2.1) remains a safe fallback for older Windows hosts.
How do I list the files in the share from a Node-RED flow?
Use the exec node to run ls -1 /mnt/recipes and parse the stdout string in a function node with payload.split("\n"). Feed the resulting array to a dropdown UI node for operator selection.