Installation & Deployment
Overview
This guide walks you through installing and running the IoT Emulator — a Spring Boot application (Java 25) that simulates IoT devices across multiple protocols. Docker is the recommended deployment method: it ships a self-contained image that runs the emulator in headless web mode and exposes the browser-based control panel and REST API on a single port.
The deployment consists of:
-
IoT Emulator — Spring Boot service started with
--web-mode(no desktop/JavaFX GUI), serving the Web UI, the REST API and Swagger UI. -
Docker container — pre-built image from the Harbor registry or loaded from an archive.
-
Configuration directory —
/app/configurationinside the container, holding protocol resource files (*_resources.xml), GUI/property files, rule scripts and snapshots. It is baked into the image and can optionally be mounted as a volume for persistence and customization.
|
The emulator runs as a single container. It does not require its own database to start. A database connection is only needed for the optional Extract CPE from DB feature (see Extract CPE from DB), and network access to a target server (ACS, LWM2M server, MQTT broker, …) is needed only when you actually start a simulation against it. |
Prerequisites
System Requirements
-
Docker Engine 20.10+
-
Docker Compose 2.0+ (optional — only for the Compose deployment option)
-
Minimum 1 GB RAM (2+ GB recommended; more for large device counts)
-
500 MB free disk space for the image and logs
|
JDK 25 is only required for local Maven builds and for running the desktop GUI without Docker. Docker deployment does not need a JDK on the host machine. |
Supported Operating Systems
-
Linux (recommended)
-
macOS
-
Windows (Docker Desktop, or native Java for the desktop GUI)
Before starting, make sure:
-
Docker Engine 20.10+ is installed. If Docker is not installed, follow the Docker Installation Guide.
-
You have network access to any target server you intend to emulate against (ACS / LWM2M server / MQTT broker / STOMP broker).
-
If you plan to use Extract CPE from DB, you have network access to the ACS database (MySQL).
Quick Start
For experienced users who already have Docker installed.
The image ships with a complete default configuration directory, so no extra files are required to start.
# 1. Authenticate with the registry (first time only)
docker login hub.friendly-tech.com
# 2. Run the emulator (the image is pulled automatically if missing)
docker run -d \
--name iot-emulator \
-p 8556:8556 \
hub.friendly-tech.com/emulator/iot:latest
# 3. Verify
curl -s http://localhost:8556/api/status
After startup the Web UI is available at http://localhost:8556.
|
For repeatable deployments and upgrades that preserve your settings, snapshots and rule scripts, use the bundled |
Getting the Docker Image
Option A: Pull from Harbor Registry (recommended)
# Authenticate with the registry
docker login hub.friendly-tech.com
# Pull the latest image (optional -- "docker run" / "docker compose up"
# will pull it automatically on first use)
docker pull hub.friendly-tech.com/emulator/iot:latest
To pin to a specific release tag instead of latest:
docker pull hub.friendly-tech.com/emulator/iot:<version>
|
|
Option B: Transfer Image to an Offline Server
Use this option when the target server has no internet access and cannot pull images from Harbor directly. The image is pulled on a machine that does have Harbor access, exported to a tar archive, transferred to the offline server, and loaded there.
-
On a machine with Harbor access, log in and pull the image for the target server’s architecture:
docker login hub.friendly-tech.com docker pull --platform linux/amd64 \ hub.friendly-tech.com/emulator/iot:latestAn explicit
--platformmatching the offline target server’s architecture is required if the image is multi-arch. Without--platform,docker pullselects the host architecture, which may not match the target (for example, an Apple Silicon arm64 Mac would produce an arm64 archive that fails on amd64 servers). Replacelinux/amd64with the platform of your offline target (linux/arm64, etc.). -
Save the pulled image to a tar archive and compress it:
docker save hub.friendly-tech.com/emulator/iot:latest \ -o iot-emulator.tar gzip iot-emulator.tar -
Transfer
iot-emulator.tar.gzto the offline server (e.g., viascpor removable media). -
On the offline server, load the image:
gzip -dc iot-emulator.tar.gz | docker load
Option C: Build from Source (developers only)
# Build the application JAR (Java 25 + Maven)
mvn clean package -DskipTests
# Build the Docker image (uses the bundled Dockerfile)
docker build -t iot-emulator:latest .
|
The |
After loading, pulling or building, verify the image is available:
docker images | grep iot
Configuration Directory
The configuration directory contains every file the emulator needs at runtime.
It is baked into the image at /app/configuration/; when building from source the same files live in gui/configuration/.
configuration/ ├── gui_connection_options.properties ├── lwm2m_gui.properties ├── lwm2m_controls_names.properties ├── mqtt_gui.properties ├── mqtt_controls_names.properties ├── usp_gui.properties ├── usp_controls_names.properties ├── fsh_hub_gui.properties ├── fsh_hub_controls_names.properties ├── profiles.properties ├── sensor_info.properties ├── metering_plugin_db_connection.properties ├── rules.js ├── snapshot.json ├── lwm2m_resources.xml ├── mqtt_resources.xml ├── usp_resources.xml └── fsh_hub_resources.xml
| File | Description |
|---|---|
|
Main connection settings: server/client IP, port, security (PSK/X.509), protocol selection, GUI runtime flags ( |
|
Protocol-specific settings (LWM2M, MQTT, USP, FSH Hub) |
|
GUI control labels for each protocol |
|
Active plugin/profile configuration |
|
Device sensor definitions |
|
Database connection for the metering plugin |
|
Rule engine script — body of |
|
Saved device list + parameter tree (user state) |
|
Device data model definitions (LWM2M, MQTT, USP, FSH Hub). The active file is selected from the Config Files dropdown in the Web UI. |
|
A bind mount on |
Deployment
Option 1: run-docker.sh (recommended)
The repository ships an idempotent bootstrap script, run-docker.sh. It is safe to re-run on every release: it pulls the latest image, overlays new/renamed template files (protocol options, resource files, control names) into ./configuration, and preserves user state — persisted settings, snapshots, rule scripts and any user-uploaded files such as TLS keystores — then recreates the container.
# From the directory where you want ./configuration to live
./run-docker.sh
What it does, in order:
-
Pulls
hub.friendly-tech.com/emulator/iot:latest. -
Extracts the image’s default configuration into a staging directory.
-
Overlays template files (
*_gui.properties,*_resources.xml,*_controls_names.properties,profiles.properties,sensor_info.properties,metering_plugin_db_connection.properties) onto./configuration. -
Seeds user-state files (
gui_connection_options.properties,snapshot.json,rules.js) only on first run — never overwriting them afterwards. -
Recreates the
iot-emulatorcontainer.
|
By default |
Option 2: docker run
Run the image directly. The built-in configuration is used unless you mount your own.
docker run -d \
--name iot-emulator \
-p 8556:8556 \
--restart unless-stopped \
hub.friendly-tech.com/emulator/iot:latest
To customize configuration, extract the defaults first and mount your complete copy:
# 1. Extract the default configuration from the image
docker create --name tmp-emulator hub.friendly-tech.com/emulator/iot:latest
docker cp tmp-emulator:/app/configuration ./configuration
docker rm tmp-emulator
# 2. Edit files in ./configuration as needed, then run with the volume mounted
docker run -d \
--name iot-emulator \
-p 8556:8556 \
-v "$(pwd)/configuration:/app/configuration" \
--restart unless-stopped \
hub.friendly-tech.com/emulator/iot:latest
To override runtime behaviour, pass environment variables with -e (see Environment Variables):
docker run -d \
--name iot-emulator \
-p 8556:8556 \
-e JAVA_OPTS="-Xms1g -Xmx4g" \
-e FT_TLS_AUTH=0 \
hub.friendly-tech.com/emulator/iot:latest
Option 3: Docker Compose
Create a docker-compose.yml file:
services:
iot-emulator:
image: hub.friendly-tech.com/emulator/iot:latest
container_name: iot-emulator
ports:
- "8556:8556" # <host-port>:<container-port> -- change the host
# port if 8556 is in use (e.g. "9556:8556")
environment:
- SPRING_PROFILES_ACTIVE=prod
- JAVA_OPTS=-Xms512m -Xmx1g
# USP MQTTv5 MTP TLS mode: 1 = mutual auth (default), 0 = server auth only
- FT_TLS_AUTH=1
# Uncomment to customize configuration. The mounted directory REPLACES the
# built-in one, so it must contain ALL required files (see warning above).
# volumes:
# - ./configuration:/app/configuration
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8556/api/status"]
interval: 30s
timeout: 3s
start_period: 30s
retries: 3
Start with:
docker compose up -d
docker compose logs -f iot-emulator
Verify Installation
After starting the container, run the following checks:
# 1. Check container status
docker ps -f name=iot-emulator
# Expected: container "Up" with port 0.0.0.0:8556->8556/tcp
# 2. Status endpoint
curl -s http://localhost:8556/api/status
# Expected JSON, e.g.:
# {"protocol":"LWM2M","initialized":false,"started":false,"deviceCount":0}
# 3. Application logs
docker logs iot-emulator --tail 50
# Look for the Spring Boot "Started ..." line
# 4. Web UI (open in a browser)
# http://localhost:8556
# 5. Swagger UI (open in a browser)
# http://localhost:8556/swagger-ui.html
Environment Variables
These variables are baked into the image with sensible defaults and can be overridden at runtime with -e / environment:.
| Variable | Description | Default | Required |
|---|---|---|---|
|
HTTP port the emulator listens on inside the container |
|
No |
|
Active Spring profile |
|
No |
|
JVM options (heap sizing, GC, heap-dump on OOM). Increase the heap for large device counts. |
|
No |
|
USP MQTTv5 MTP TLS mode: |
|
No |
|
Container timezone |
|
No |
|
JVM options auto-prepended by the launcher (opens |
|
No |
|
Headless mode only. Overrides the target server IP at startup, without editing |
(unset) |
No |
|
Headless mode only. Overrides the target server port at startup. Applied together with |
(unset) |
No |
|
|
|
|
Application Properties
Beyond environment variables, runtime behaviour is driven by the files in the configuration directory. Most of these are managed for you through the Web UI and persisted automatically (see User Guide); you rarely need to edit them by hand.
TLS Authentication Mode
The emulator can switch between TLS mutual authentication and server-only authentication for the USP MQTTv5 MTP.
It is controlled by the FT_TLS_AUTH environment variable (mapped to the JVM system property ft.tls.auth):
| Value | Behaviour |
|---|---|
|
Mutual TLS authentication. The emulator presents its client certificate to the MQTT broker in addition to validating the server certificate. |
|
Server-only authentication. The emulator validates the broker certificate but does not send a client certificate. |
| This setting currently affects only the USP MQTTv5 MTP. Other protocols ignore it and may use their own TLS configuration. |
Docker:
docker run -d \
--name iot-emulator \
-p 8556:8556 \
-e FT_TLS_AUTH=0 \
hub.friendly-tech.com/emulator/iot:latest
Manual / run.sh / run.bat: export FT_TLS_AUTH before launching, or pass -Dft.tls.auth=0 directly to the JVM:
FT_TLS_AUTH=0 ./run.sh
# or
java -Dft.tls.auth=0 -jar gui/target/iot-emulator.jar --web-mode
XML Resource Files
Resource files define the device data model. The active file is chosen from the Config Files dropdown in the Web UI; new ones can be uploaded there. Example structure:
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<resource>
<path>Device.DeviceInfo.Manufacturer</path>
<value>MyCompany</value>
<type>string</type>
<writable>false</writable>
</resource>
<!-- More resources... -->
</resources>
Manual Installation (without Docker)
Use this when you want to run the emulator directly on a host with Java 25 installed — either in headless web mode or as the desktop GUI.
Build from Source
-
Clone the repository:
git clone https://github.com/friendly-tech/iot-emulator.git cd iot-emulator -
Build with Maven:
mvn clean package -DskipTests
Run in Web Mode (headless)
Web mode serves the same browser UI and REST API as the Docker container — ideal for servers without a display.
java -jar gui/target/iot-emulator.jar --web-mode
Then open http://localhost:8556.
Run the Desktop GUI (JavaFX)
The assembled distribution includes launch scripts that start the native JavaFX desktop application:
# Linux / macOS
./run.sh
# Windows
run.bat
|
The desktop GUI requires JavaFX modules, which are bundled in the assembly under |
Container Management
| Command | Description |
|---|---|
|
Start the emulator in the background |
|
Stop / start the container |
|
Restart the application |
|
Follow application logs |
|
View the last 100 log lines |
|
Open a shell inside the container |
|
List the active configuration files |
|
View CPU / memory usage |
|
Show the container’s environment variables |
Updating
Via run-docker.sh (recommended)
Re-running the bootstrap script is the simplest upgrade path. It pulls the new image, propagates new template files, keeps your settings/snapshots/rules, and recreates the container.
# (Optional) back up your configuration first
tar -czf emulator-config-$(date +%Y%m%d).tar.gz configuration/
./run-docker.sh
Via plain Docker
# 1. Pull the new image
docker pull hub.friendly-tech.com/emulator/iot:latest
# 2. Recreate the container
docker rm -f iot-emulator
docker run -d \
--name iot-emulator \
-p 8556:8556 \
-v "$(pwd)/configuration:/app/configuration" \
--restart unless-stopped \
hub.friendly-tech.com/emulator/iot:latest
# 3. Verify
curl -s http://localhost:8556/api/status
|
If you are not mounting a |
Production / QA Checklist
| # | Item | Notes |
|---|---|---|
1 |
Restart policy |
Use |
2 |
Configuration persistence |
Mount a |
3 |
Heap sizing |
Raise |
4 |
Network exposure |
Bind to loopback ( |
5 |
Health check |
Configure a healthcheck against |
6 |
Timezone |
Set |
7 |
Log management |
Application logs go to |
Troubleshooting
Start with quick diagnostics:
docker logs iot-emulator --tail 100
docker inspect iot-emulator --format '{{json .Config.Env}}'
docker exec iot-emulator ls -la /app/configuration
Common Issues
Container exits immediately / fails to start
Symptom: The container is not in the Up state, and logs mention a missing file under /app/configuration.
Check:
docker exec iot-emulator ls /app/configuration
Solution:
-
If you mounted a
configurationvolume, ensure it contains all required files (both.propertiesand.xml) — a bind mount replaces the entire built-in directory. -
Re-extract the defaults (see Deployment, Option 2) or deploy via
run-docker.sh.
Web UI not reachable on port 8556
Symptom: The browser cannot connect to http://localhost:8556.
Check:
docker ps -f name=iot-emulator # is the port mapped?
curl -s http://localhost:8556/api/status
Solution:
-
Confirm the port mapping (
-p 8556:8556). -
If you deployed with
run-docker.sh, the UI is bound to127.0.0.1only — access it from the host itself or change the binding to-p 8556:8556.
Port conflict (address already in use)
Symptom: The container fails to start because port 8556 is taken.
Check:
docker ps
lsof -i :8556 # Linux/macOS
Solution:
-
Stop the conflicting service, or publish a different host port:
-p 9556:8556.
OutOfMemoryError with many devices
Symptom: A heap dump appears at /app/logs/heapdump.hprof; logs show OutOfMemoryError.
Solution:
-
Increase the JVM heap via
JAVA_OPTS, e.g.-e JAVA_OPTS="-Xms1g -Xmx4g".
USP/MQTT TLS handshake fails
Symptom: The USP agent cannot connect to the MQTT broker over TLS.
Solution:
-
If the broker does not require client certificates, switch to server-only auth with
-e FT_TLS_AUTH=0(see TLS Authentication Mode). -
Verify the keystore/truststore paths and the broker certificate chain.
Port Reference
| Port | Protocol | Description |
|---|---|---|
8556 |
HTTP |
Web UI, REST API, and Swagger UI |
| Protocol traffic (CoAP/LWM2M 5683/5684, MQTT 1883/8883, USP MTP ports, …) is outbound from the emulator to the target server, so it does not need to be published from the container. See Supported Protocols for protocol-specific defaults. |
Related Documentation
-
User Guide — how to drive the emulator from the Web UI
-
REST API Reference — automation and scripting
-
Supported Protocols — per-protocol options and ports