The bootstrap subsystem handles profile-based provisioning. At startup, the agent fetches a rendered bootstrap profile from the Magistrala Bootstrap service, which provides device identity, MQTT credentials, channel IDs, and provision configuration. The profile is cached locally so subsequent starts skip the HTTP fetch.
- Agent starts with
MG_AGENT_BOOTSTRAP_URL,MG_AGENT_BOOTSTRAP_EXTERNAL_ID, andMG_AGENT_BOOTSTRAP_EXTERNAL_KEYset. - Check cache — if bootstrap-derived fields are already in the persistent config store and
bs_validis1, skip the HTTP fetch. - Fetch profile — HTTP GET to the bootstrap endpoint with the external ID and key.
- Parse content — the bootstrap response wraps a JSON string in
content:{ "content": "{ \"device_id\": \"...\", \"mqtt\": { ... } }", "client_key": "", "client_cert": "", "ca_cert": "" } - Merge with env config — bootstrap fields override env defaults for
domain_id,channels, andmqtt. - Persist — bootstrap fields are saved to the persistent config store (
agent-config.json). - Load certificates — if mTLS is configured, client and CA certs are loaded.
- Connect MQTT — the agent connects using the bootstrap-provided credentials.
The content field from the bootstrap response decodes to:
{
"device_id": "<client-id>",
"external_id": "<external-id>",
"domain_id": "<domain-id>",
"mqtt": {
"url": "ssl://host.docker.internal:8883",
"client_id": "<client-id>",
"secret": "<client-secret>"
},
"telemetry": {
"channel_id": "<telemetry-channel-id>",
"topic": "m/<domain-id>/c/<telemetry-channel-id>/msg"
},
"commands": {
"channel_id": "<commands-channel-id>"
}
}| Variable | Default | Description |
|---|---|---|
MG_AGENT_BOOTSTRAP_URL |
"" |
Bootstrap service base URL (e.g. http://bootstrap:9013/clients/bootstrap) |
MG_AGENT_BOOTSTRAP_EXTERNAL_ID |
"" |
Device external ID used to look up the profile |
MG_AGENT_BOOTSTRAP_EXTERNAL_KEY |
"" |
Device external key (sent as Authorization: Client <key>) |
MG_AGENT_BOOTSTRAP_RETRIES |
5 |
Number of retries when fetching bootstrap profile |
MG_AGENT_BOOTSTRAP_RETRY_DELAY_SECONDS |
10 |
Delay between retries in seconds |
MG_AGENT_BOOTSTRAP_SKIP_TLS |
false |
Skip TLS verification for bootstrap HTTP fetch |
MG_AGENT_BOOTSTRAP_CACHE_PATH |
/var/lib/agent/bootstrap.json |
Local file path for caching the bootstrap response |
Bootstrap mode activates when all three are set:
MG_AGENT_BOOTSTRAP_URLMG_AGENT_BOOTSTRAP_EXTERNAL_IDMG_AGENT_BOOTSTRAP_EXTERNAL_KEY
When bootstrap is active, the legacy config.toml file is ignored.
| Variable | Default | Description |
|---|---|---|
MG_AGENT_CONFIG_PATH |
agent-config.json |
Path to the JSON file used for persistent config overrides |
Bootstrap-derived fields persisted in the store:
| Key | Source |
|---|---|
domain_id |
Bootstrap profile |
channels_ctrl_id |
Bootstrap profile |
channels_data_id |
Bootstrap profile |
mqtt_url |
Bootstrap profile |
mqtt_username |
Bootstrap profile |
mqtt_password |
Bootstrap profile |
bs_valid |
Set to "1" after successful fetch |
export MG_AGENT_BOOTSTRAP_EXTERNAL_ID="<device-external-id>"
export MG_AGENT_BOOTSTRAP_EXTERNAL_KEY="<device-external-key>"
export MG_DOMAIN_ID="<domain-id>"
export MG_PAT="<personal-access-token>"
make run_provisionThe provisioning script creates:
- A Client (device) with credentials
- Telemetry and commands Channels
- A Bootstrap Profile and Enrollment with
external_idandexternal_key - Profile bindings to the provisioned client and channels
- A Rule Engine rule with
save_senmloutput for telemetry
export MG_API="https://cloud.magistrala.absmach.eu/api"
export MG_AGENT_MQTT_URL=ssl://messaging.magistrala.absmach.eu:8883
export MG_AGENT_MQTT_SKIP_TLS=false
export MG_AGENT_BOOTSTRAP_EXTERNAL_ID="<device-external-id>"
export MG_AGENT_BOOTSTRAP_EXTERNAL_KEY="<device-external-key>"
export MG_DOMAIN_ID="<domain-id>"
export MG_PAT="<pat>"
make run_provisioncurl -s 'http://localhost:9013/clients/bootstrap/<external-id>' \
-H 'accept: */*' \
-H 'Authorization: Client <external-key>'Expected response:
{
"id": "fa846d56-3100-44aa-8385-3a88cb437a5a",
"content": "{\n \"commands\": {\n \"channel_id\": \"bc9a0af7-6d0f-4806-aa5a-61d68c0a7cf7\"\n },\n \"device_id\": \"fa846d56-3100-44aa-8385-3a88cb437a5a\",\n \"domain_id\": \"e9692c28-b730-4797-8a15-2e25c08f9641\",\n \"external_id\": \"019eb690777d7452ba898a66f5cc9cb8\",\n \"mqtt\": {\n \"client_id\": \"ffec2491-0de1-4051-9e75-ad2e2d241627\",\n \"secret\": \"30c775d7-3504-42c6-976c-52c02474bf2f\",\n \"url\": \"ssl://host.docker.internal:8883\"\n },\n \"provision\": {\n \"channels_url\": \"http://channels:9005\",\n \"clients_url\": \"http://clients:9006\",\n \"rules_engine_url\": \"http://rules:9008\",\n \"token\": \"pat_TurQa8bRR72vtZguCtIIe8ZTeaSkqkinkhLxSqPo7bw=_PoOG@UuEadfD!F7TcWYzsDKSxLB%3mzlh1M\\u0026MmLIky0M8A2Ui9f9J^4DuzZ@O0rjCA-cvgjbuFjOofOwreHL-j\\u0026CcgffH7FzwoDC\"\n },\n \"telemetry\": {\n \"channel_id\": \"b465a688-c1ca-417d-a36f-71f6f1be2409\",\n \"topic\": \"\\u003cno value\\u003e\"\n }\n}"
}mosquitto_pub \
-h <mqtt-host> -p 1883 \
-u <client-id> -P <client-secret> --id "cfg-$(date +%s)" \
-t "m/<domain-id>/c/<commands-channel-id>/req" \
-m '[{"bn":"req-1:", "n":"config", "vs":"set,bs_valid,0"}]'This sets bs_valid to 0 and deletes the cached bootstrap profile. On the next restart, the agent will re-fetch the profile from the bootstrap service.
mosquitto_pub \
-h <mqtt-host> -p 1883 \
-u <client-id> -P <client-secret> --id "cfg-$(date +%s)" \
-t "m/<domain-id>/c/<commands-channel-id>/req" \
-m '[{"bn":"req-1:", "n":"config", "vs":"get,bs_valid"}]'Response: 1 (valid cache) or 0 (cache invalidated).
export MG_AGENT_BOOTSTRAP_EXTERNAL_ID="<external-id>"
export MG_AGENT_BOOTSTRAP_EXTERNAL_KEY="<external-key>"
export MG_AGENT_BOOTSTRAP_URL="http://localhost:9013/clients/bootstrap"
build/magistrala-agentAfter a successful bootstrap fetch, the agent logs:
{"level":"INFO","msg":"Client connected","client_name":"<client-id>"}
{"level":"INFO","msg":"Agent service started","port":"9999"}If bootstrap data is already cached:
{
"level": "INFO",
"msg": "Bootstrap data already present, skipping bootstrap fetch"
}