Devices Simulator Setup
The devices simulator is an optional platform component, disabled by default. This page walks through the steps required to enable it in a Kamea deployment.
For an overview of how the simulator is wired into the platform (CI gating, Terraform module, Caddy routing, Socket.IO co-existence), see Devices Simulator.
Prerequisites
- A working Kamea deployment with a shared App Service Plan (the simulator API piggy-backs on
module.core-platform.azurerm_service_plan_webapp_plan). - Caddy already deployed as the reverse proxy in front of the platform.
- The identity provider used by the management API (
JWT_ISSUER,JWT_AUDIENCE,JWT_ISSUER_WELL_KNOWN_URL) — the simulator API reuses the same JWT settings. - A user account on the identity provider with permission to call the simulator API.
Step 1 — Enable the USE_SIMULATOR CI variable
In the target environment's GitLab CI/CD variables (Settings → CI/CD → Variables), set:
USE_SIMULATOR = "true"
Scope it to the environment (or environments) that should run the simulator. Leave it false (or unset) elsewhere.
This single flag enables:
- the simulator-back test, build, and deploy jobs;
- the simulator-front build and deploy jobs;
- the Terraform
module.simulatorinstantiation, which creates the${PROJECT_NAME}-simulator-apiApp Service.
Step 2 — Provide the simulator front-end environment file
The simulator front-end's build pipeline copies an environment file from $SPA_SIMULATOR_ENVIRONMENT_FILE_PATH (a path on the CI runner, typically a file delivered through a GitLab file CI variable) into core/apps/devices-simulator/front-end/environments/environment-simulator.values.json at build time.
The template at core/apps/devices-simulator/front-end/environments/environment-simulator.template.json documents the required fields. For a Caddy-fronted deployment the values should be:
json
{
"simulatorHttpBaseUrl": "/simulator-api",
"simulatorWsPath": "/simulator-api/socket.io",
"defaultMqttEndpoint": "mqtts://<your-mqtt-broker>:8883"
}
simulatorHttpBaseUrlbecomes the base URL prefixed to every REST call from the SPA. A relative path keeps the front-end same-origin with Caddy.simulatorWsPathis passed verbatim to socket.io-client as itspathoption, so the WebSocket handshake hits the same Caddy reverse-proxy block.defaultMqttEndpointis the value prefilled in device-credentials forms when creating simulated devices.
Save this JSON somewhere accessible to the GitLab runner and set SPA_SIMULATOR_ENVIRONMENT_FILE_PATH to its path on the runner.
The companion file SPA_ENVIRONMENT_FILE_PATH (referenced by other front-ends as well) provides the Kamea-wide values (apiUrl, wssUrl, B2C client ID, etc.) and is reused unchanged.
Step 3 — Update the Caddy extension
The default core/apps/caddy/caddyfile.extension is intentionally minimal and does not route the simulator. Use the example overlay at core/apps/caddy/caddyfile.extension.simulator.example as the starting point:
- Copy the example to a project-specific location alongside the other Caddy extensions you maintain.
- Replace
<project-name>with the actualPROJECT_NAMEof your deployment, so the hostname matches the App Service created by Terraform (${PROJECT_NAME}-simulator-api.azurewebsites.net). - Set the CI variable
CADDY_EXTENSION_PATHin the target environment to the path of this file. Thecaddy:deployjob will copy it into the Caddy image at build time.
After the next Caddy deploy, the platform exposes:
https://<reverse-proxy>/simulator/— the simulator SPA.https://<reverse-proxy>/simulator-api/...— REST endpoints and Socket.IO handshakes routed to the simulator API.
Step 4 — Configure the identity provider for the simulator SPA
The simulator SPA reuses the platform's identity provider configuration from $SPA_ENVIRONMENT_FILE_PATH (the same one the management front-end uses), so no separate B2C/Auth0 app registration is strictly required. If your access-control policy requires the simulator SPA to be a distinct OAuth client, provision a new app registration in your IdP and override clientId / scope in the env file for this front-end.
The simulator API itself does not require its own app registration — it validates incoming JWTs using JWT_ISSUER, JWT_AUDIENCE, and JWT_ISSUER_WELL_KNOWN_URL, which are inherited from the platform-wide settings via Terraform.
Step 5 — Trigger a deployment
Run a deploy pipeline (push to the deploy branch, or trigger from the GitLab UI). On a successful run you should see:
simulator-back:deploy-dev— built the simulator API Docker image.simulator-front:buildandsimulator-front:deploy— built the SPA and synced it to the frontend file share atfrontend/simulator/.terraform:apply— created (or updated) the${PROJECT_NAME}-simulator-apiLinux Web App and pointed it at the new image tag.caddy:deploy— rebuilt the Caddy image with the new extension.
Step 6 — Verify
- Browse to
https://<reverse-proxy>/simulator/. The simulator SPA should load and prompt for authentication. - Sign in with a platform user account.
- Create a simulated device. The SPA's network panel should show REST calls hitting
/simulator-api/...and a Socket.IO WebSocket connection at/simulator-api/socket.io/?.... Both should return successfully. - Start the simulated device and verify telemetries appear on the management front-end.
Disabling the simulator
To remove the simulator from a deployment:
- Set
USE_SIMULATOR = "false"in the environment's CI/CD variables. - Re-run the deploy pipeline. The simulator CI jobs will be skipped, and
terraform:applywill destroymodule.simulator's resources (the App Service and any IP restrictions). - Optionally, revert
CADDY_EXTENSION_PATHto a Caddy extension that does not include the simulator routes, so the public surface no longer advertises/simulator/and/simulator-api/.
Caveats
simulator-back:test-e2e:builduses a RabbitMQ service container. IfUSE_MQTT=false, the correspondingrabbitmq-server-e2eimage is not built and the simulator's e2e test will fail to start. This coupling is not specific to the simulator's gating — it is the same dependency the platform-wide tests have on RabbitMQ.