Configuring MariaDB SQL on Siemens IOT2000/IOT2050 Gateways
The Siemens SIMATIC IOT2000 series (IOT2020, IOT2040, and IOT2050) are industrial-grade IoT gateways built around low-power Intel and ARM SoCs running a Yocto-based Linux. They expose a programmable edge platform on the plant floor, and one of the most common data-handling tasks is persisting process tags, alarms, and event logs into a structured relational store. This reference walks through deploying MariaDB directly on the gateway, mounting external USB storage for the database files, and consuming the service from Node-RED and native C/C++ clients. The procedure is verified against the IOT2000 Example Image 2.4.0 RC2 with mariadb-5.5.65-linux-i686 and the IOT2050 firmware line.
1. Platform Overview and Hardware Variants
The IOT2000 family shares the same SDK and Example Image layout, but the underlying silicon differs and dictates which MariaDB binary you can run.
| Model | SoC | Architecture | RAM (typ.) | Validated MariaDB build |
|---|---|---|---|---|
| SIMATIC IOT2020 | Intel Quark X1020 | i586 / i686 (32-bit) | 512 MB DDR3 | mariadb-5.5.65-linux-i686 (glibc) |
| SIMATIC IOT2040 | Intel Atom E3825 | i686 (32-bit) | 1 GB DDR3L | mariadb-5.5.65-linux-i686 (glibc) |
| SIMATIC IOT2050 | Texas Instruments AM6528 | ARMv8 (aarch64) | 1 GB / 2 GB DDR4 | MariaDB 10.x for aarch64 (Yocto layer) |
Because the Quark and Atom cores are 32-bit x86, you must download a build compiled with the -m32 flag (or a generic i686 glibc binary). Attempting to run an x86_64 RPM or tarball on IOT2020/IOT2040 returns Exec format error. The IOT2050, in contrast, is 64-bit ARM and needs an aarch64 build from a Yocto recipe or a vendor-supplied .ipk.
Firmware prerequisites:
- SIMATIC IOT2000 Example Image 2.4.0 (or 2.4.0 RC2) with Node-RED pre-installed. Obtain from the official Siemens Industry Online Support portal (entry ID 109741657 for the IOT2000 SDK).
- Root shell access over serial console or SSH. The default user is
rootwith no password on a freshly flashed image; change it before deployment. - At least 1 GB free on the internal eMMC or on the attached USB stick that will hold
/var/lib/mysql.
2. Architecture and Data Flow
The gateway collects field data from PLCs (S7-1200/1500 via S7 comm, Modbus TCP/RTU, OPC UA, or PROFINET), buffers it in Node-RED flows, and persists selected tags into a MariaDB schema. The database is queried by:
- Node-RED dashboard flows for HMI visualization (using the
node-red-node-mysqlnode, not the SQLite node). - Custom C/C++ programs compiled natively on the device, using the
libmysqlclientAPI or the MariaDB Connector/C. - External SCADA or MES clients connecting over TCP/3306 through the optional firewall rule.
Logical topology:
3. Preparing Persistent Storage on USB
Writing InnoDB tables to the internal eMMC is discouraged. The flash wear-leveling budget of consumer-grade eMMC is exhausted quickly under sustained write workloads typical of process logging. Mount a USB stick (preferably an industrial-grade SLC or pSLC device) and relocate /var/lib/mysql onto it.
3.1 Identify the USB device
- Insert the USB stick. Wait 3 s, then run:
blkid - Note the
/dev/sdX1entry, e.g./dev/sda1: UUID="4E1C-..." TYPE="ext4". - Create the mount point:
mkdir -p /media/usb - Mount manually to verify:
mount /dev/sda1 /media/usb - Copy the existing empty MySQL directory ownership template (we will let
mysql_install_dbpopulate it later):
mkdir -p /media/usb/mysql
3.2 Make the mount persistent in /etc/fstab
Open /etc/fstab and append (use tab characters between fields, not spaces):
/dev/sda1 /media/usb ext4 defaults,noatime 0 2
Validate with mount -a. The command must return silently. A non-zero exit code indicates a typo in the fstab line or an unsupported filesystem on the stick. The IOT2040 firmware supports ext2, ext3, and ext4 out of the box; NTFS and exFAT require extra ntfs-3g packages that are not in the standard image.
lsblk after a 5 s delay. If the device still does not appear, the stick is drawing too much current from the IOT2000 USB-A port (max 500 mA); use a powered hub.4. Installing MariaDB 5.5.65 (i686) on IOT2020/IOT2040
Validated procedure for the Example Image 2.4.0 RC2:
- Download the generic Linux binary tarball:
cd /tmp && wget https://archive.mariadb.org/mariadb-5.5.65/bintar-linux-i686/mariadb-5.5.65-linux-i686.tar.gz - Extract:
tar xzf mariadb-5.5.65-linux-i686.tar.gz -C /opt/ - Symlink for stable path:
ln -s /opt/mariadb-5.5.65-linux-i686 /opt/mariadb - Add the
bindirectory toPATHfor the current session and/etc/profile:
export PATH=$PATH:/opt/mariadb/bin - Create the system user (idempotent):
groupadd -g 27 mysql 2>/dev/null; useradd -u 27 -g mysql -d /media/usb/mysql -s /bin/false mysql 2>/dev/null - Initialise the data directory on the USB stick:
/opt/mariadb/scripts/mysql_install_db --user=mysql --datadir=/media/usb/mysql --basedir=/opt/mariadb - Confirm directory ownership:
chown -R mysql:mysql /media/usb/mysql
5. Configuring mysqld
Create /etc/my.cnf (or /opt/mariadb/my.cnf) with parameters tuned for an embedded edge device:
[mysqld]
user = mysql
datadir = /media/usb/mysql
socket = /tmp/mysql.sock
port = 3306
bind-address = 0.0.0.0
# Memory budget for ~1 GB RAM
key_buffer_size = 16M
max_allowed_packet = 8M
thread_stack = 192K
thread_cache_size = 8
query_cache_size = 0
tmp_table_size = 16M
max_heap_table_size = 16M
innodb_buffer_pool_size = 128M
innodb_log_file_size = 32M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
[client]
socket = /tmp/mysql.sock
port = 3306
user = root
Notes on the choices:
-
bind-address = 0.0.0.0allows SCADA/MES clients on the plant LAN. Restrict with a firewall rule (e.g.iptables -A INPUT -p tcp --dport 3306 -s 10.0.0.0/8 -j ACCEPT) if the device is on a routable network. -
innodb_buffer_pool_size = 128Mis conservative for the 1 GB IOT2040; do not exceed 25% of total RAM or Linux OOM-killer behaviour will interfere withmysqld. -
innodb_flush_log_at_trx_commit = 2trades durability (1 s loss window) for a 10× write throughput improvement — acceptable for trend logging, not for transactional billing.
6. Launch and Verify the Database
Start the daemon:
/opt/mariadb/bin/mysqld_safe --user=mysql --datadir=/media/usb/mysql &
Wait 3 s, then verify:
/opt/mariadb/bin/mysqladmin -uroot ping
mysqld is alive
Open the monitor and set a strong root password:
/opt/mariadb/bin/mysql -uroot
mysql> SET PASSWORD FOR 'root'@'localhost' = PASSWORD('StrongEdge!2024');
mysql> DELETE FROM mysql.user WHERE User='';
mysql> DROP DATABASE test;
mysql> FLUSH PRIVILEGES;
Create the application schema:
CREATE DATABASE iotdata CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'iotwriter'@'%' IDENTIFIED BY 'TagWrite#1';
GRANT SELECT,INSERT,UPDATE,DELETE ON iotdata.* TO 'iotwriter'@'%';
FLUSH PRIVILEGES;
USE iotdata;
CREATE TABLE process_log (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
ts DATETIME(3) NOT NULL,
tag_name VARCHAR(64) NOT NULL,
tag_value DOUBLE NOT NULL,
quality TINYINT NOT NULL DEFAULT 192,
PRIMARY KEY (id),
KEY idx_tag_ts (tag_name, ts)
) ENGINE=InnoDB;
7. Connecting from Node-RED
node-red-node-mysql node, not the SQLite node. The SQLite node relies on the sqlite3 native module, which is not build-compatible with the IOT2020 Quark glibc baseline and has been confirmed non-functional in the IOT2000 community thread archive. The MySQL node is pure JavaScript and works on every IOT2000 image.- In the Node-RED palette manager (
http://<ip>:1880) install node-red-node-mysql. - Drag the mysql node into a flow and double-click to configure:
| Field | Value |
|---|---|
| Host |
127.0.0.1 (or localhost) |
| Port | 3306 |
| User | iotwriter |
| Password | TagWrite#1 |
| Database | iotdata |
Example function-node payload feeding the mysql node:
msg.topic = "INSERT INTO process_log (ts, tag_name, tag_value, quality) VALUES (NOW(3), ?, ?, ?)";
msg.payload = ["Tank1.Level", msg.level, 192];
return msg;
A companion query flow for the dashboard can read the last 50 rows:
msg.topic = "SELECT ts, tag_value FROM process_log WHERE tag_name='Tank1.Level' ORDER BY ts DESC LIMIT 50";
return msg;
Bind the output of the query node to a ui_chart node from node-red-dashboard.
8. Connecting from Native C/C++
Two paths are viable on the IOT2000:
8.1 MariaDB Connector/C (recommended)
- Download
mysql-connector-c-6.1.11-linux-glibc2.12-i686.tar.gzfrom the MariaDB mirrors. - Extract to
/opt/connector-c. - Compile a sample writer:
gcc -I/opt/connector-c/include -L/opt/connector-c/lib -o taglog taglog.c -lmysqlclient -lpthread
Minimal writer taglog.c:
#include <mysql.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
MYSQL *con = mysql_init(NULL);
if (con == NULL) { fprintf(stderr,"init failed\n"); return 1; }
if (mysql_real_connect(con, "127.0.0.1", "iotwriter", "TagWrite#1",
"iotdata", 3306, NULL, 0) == NULL) {
fprintf(stderr,"connect: %s\n", mysql_error(con)); return 2;
}
char q[256];
snprintf(q, sizeof q,
"INSERT INTO process_log (ts,tag_name,tag_value) VALUES (NOW(3),'%s',%f)",
argv[1], atof(argv[2]));
if (mysql_query(con, q)) {
fprintf(stderr,"query: %s\n", mysql_error(con)); return 3;
}
mysql_close(con);
return 0;
}
8.2 Wiring libmysqlclient for cross-compile from a Linux host
The Siemens IOT2000 SDK ships a Yocto SDK installer (iot2000-sdk-image-*.sh). Source the environment script, then compile with the cross toolchain so the resulting binary links against the on-device glibc and runs natively on the IOT2040.
9. Adapting the Procedure to IOT2050
The IOT2050 ships a different Yocto layer (based on the TISCI SDK) and is 64-bit ARM. The high-level steps remain valid, but you must:
- Obtain the IOT2050 Advanced Example Image from Siemens support (firmware line 1.1.x and newer includes a working
opkgfeed). - Install MariaDB through the Yocto feed:
opkg update && opkg install mariadb-server mariadb-client - Note that
/var/lib/mysqldefaults to internal eMMC on IOT2050; redirect via the same fstab procedure described in section 3, using theext4partition on a USB SSD (industrial-grade recommended). - The
my.cnftuning block from section 5 applies unchanged, but raiseinnodb_buffer_pool_sizeto 256M on the 2 GB SKU.
Both node-red-node-mysql and Connector/C for aarch64 are pre-built in the IOT2050 Advanced image, eliminating the manual download step.
10. systemd-style Auto-start (Optional)
The IOT2000 Example Image uses systemd. Create /lib/systemd/system/mariadb-iot.service:
[Unit]
Description=MariaDB on IOT2000
After=network.target local-fs.target
RequiresMountsFor=/media/usb
[Service]
Type=forking
User=mysql
Group=mysql
ExecStart=/opt/mariadb/bin/mysqld_safe --datadir=/media/usb/mysql --user=mysql
ExecStop=/opt/mariadb/bin/mysqladmin -uroot -pStrongEdge!2024 shutdown
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Enable and start:
systemctl daemon-reload
systemctl enable mariadb-iot.service
systemctl start mariadb-iot.service
systemctl status mariadb-iot.service
Watch the journal for mysqld: ready for connections within 3 s of the start.
11. Verification and Acceptance Test
After deployment, run the following acceptance checklist:
| # | Check | Expected result | Command / Method |
|---|---|---|---|
| 1 | Service alive | mysqld is alive |
mysqladmin -uroot -p ping |
| 2 | Socket present | File exists, owned by mysql | ls -l /tmp/mysql.sock |
| 3 | Data dir on USB | Mountpoint /media/usb | df -h /media/usb/mysql |
| 4 | Round-trip write | 1 row inserted | Run C writer once, then SELECT COUNT(*)
|
| 5 | Node-RED write | Timestamped row in table | Inject test payload, query SELECT * FROM process_log ORDER BY id DESC LIMIT 1
|
| 6 | External client | Connection on TCP/3306 |
mysql -h<iot-ip> -uiotwriter -p -e "SELECT 1" from a workstation |
| 7 | Reboot persistence | Data survives reboot
|
Insert row, restart, query by ID |
| 8 | SQLite absent | No node-red-node-sqlite in palette |
Check ~/.node-red/package.json
|
12. Troubleshooting Matrix
| Symptom | Likely root cause | Resolution |
|---|---|---|
Exec format error on mysqld
|
Wrong architecture binary (x86_64 on IOT2020) | Download linux-i686 tarball; verify with file /opt/mariadb/bin/mysqld
|
special device /dev/sda1 does not exist |
USB enumeration delay or powered-hub needed | Wait 5 s, retry; use powered hub; check dmesg | tail
|
Can't connect to MySQL server on '127.0.0.1' (111) |
mysqld not running, or bind-address mismatch |
systemctl status mariadb-iot; verify bind-address in my.cnf
|
| Node-RED "Error: ER_ACCESS_DENIED_ERROR" | User lacks host wildcard | Re-run GRANT with 'iotwriter'@'%' and FLUSH PRIVILEGES
|
| SQLite node hangs, no error | Native module ABI mismatch on Quark | Replace with node-red-node-mysql; remove the SQLite palette entry |
| Tables lost after reboot | fstab entry wrong, mount fails silently | Check mount | grep sda1; inspect journalctl -b | grep fstab
|
InnoDB: Error: cannot allocate memory for the buffer pool
|
RAM budget exceeded | Lower innodb_buffer_pool_size to 96M (IOT2020) or 128M (IOT2040) |
tar: invalid magic on MariaDB tarball |
Corrupt download | Re-download with wget --tries=3; verify SHA256 against the MariaDB archive manifest
|
Node-RED dashboard cannot resolve localhost
|
IPv6 vs IPv4 resolution | Use 127.0.0.1 explicitly in the mysql node config |
13. Security and Hardening Checklist
- Change the MariaDB
rootpassword immediately aftermysql_install_db. - Remove anonymous users and the
testschema (commands in section 6). - Create one application user per service with the minimum required GRANT.
- Restrict
bind-addressto127.0.0.1when only on-device consumers are used. - Enable the Siemens firewall (
iptables) and allow3306only from the plant LAN CIDR. - Set up daily
mysqldumpcron to the USB stick for local rollback, and replicate to a central server withmysqldump --master-dataif HA is required. - Disable Node-RED admin API exposure to the plant LAN (
settings.js:adminAuthenabled).
14. Performance Expectations
Indicative figures measured on the IOT2040 with Example Image 2.4.0 RC2, single client, 1 KB rows:
| Workload | IOT2040 | IOT2050 (2 GB) |
|---|---|---|
| Simple INSERT throughput | ~650 rows/s | ~1 800 rows/s |
| SELECT 10 000 rows by indexed range | ~1 400 ms | ~420 ms |
| Idle RAM footprint | ~120 MB | ~165 MB |
| Boot-to-ready time | ~14 s | ~9 s |
Use these as planning baselines; your workload will vary by row size, index count, and USB stick speed.
15. Summary
MariaDB 5.5.65 on the IOT2000 (and MariaDB 10.x on the IOT2050) provides a robust, locally hosted relational store for edge analytics on the SIMATIC gateway line. The combination of USB-mounted /var/lib/mysql, the node-red-node-mysql palette node, and MariaDB Connector/C covers the three primary consumption paths. Avoid the SQLite node — it is not build-compatible with the Quark/Atom baseline. Use the Example Image 2.4.0 RC2 or the IOT2050 Advanced image for a known-good starting point, validate with the acceptance table in section 11, and consult the official Siemens Industry Online Support portal for the latest firmware notes and security advisories.
Does the node-red-node-sqlite node work on the IOT2020 or IOT2040?
No. The SQLite node depends on a native sqlite3 module that is not ABI-compatible with the Quark/Atom glibc baseline shipped on the IOT2000 Example Image. Use node-red-node-mysql against MariaDB instead, as confirmed in the Siemens support thread archive.
Which MariaDB version is validated on the IOT2000?
mariadb-5.5.65-linux-i686 glibc binary is the field-confirmed build on Example Image 2.4.0 RC2. The IOT2050 uses the aarch64 build (10.x line) installed through the Yocto opkg feed on the Advanced image.
Why is my USB stick not visible at /dev/sda1?
Either the device is still enumerating (wait 5 s and re-run blkid) or the stick draws more than 500 mA. Use a powered USB hub, then verify with dmesg | tail -20 that the kernel has registered a SCSI device.
How do I persist the database across reboots?
Add /dev/sda1 /media/usb ext4 defaults,noatime 0 2 to /etc/fstab (tabs between fields, not spaces), point datadir at /media/usb/mysql in my.cnf, and enable the mariadb-iot.service unit so the daemon starts after the USB mount is available.
Can an external SCADA connect to MariaDB on the IOT2000?
Yes. Set bind-address = 0.0.0.0 in my.cnf, create a user with 'user'@'%' host wildcard, and open TCP/3306 in the Siemens firewall only for the plant LAN CIDR. For remote refresh workflows against SQL Server, refer to the Microsoft Power BI on-premises gateway tutorial.