The ways a device can connect to NervesHub: NervesHubLink for Nerves, AtomVM on the ESP32, a Rust agent for other Linux systems, and a Yocto layer.
A device connects to NervesHub over a Phoenix Channels WebSocket and speaks the device protocol. Nothing about that protocol is Elixir-specific, so a client is whatever software on the device speaks it — reporting the firmware it is running, accepting update assignments, and handing the download to whatever writes firmware on that platform.Several clients already exist. Which one you want follows from what the device runs.
meta-nerveshub is a packaging layer rather than a separate client. It builds the Rust Linux agent into a Yocto image; the agent is what actually connects.
Running Nerves? Use NervesHubLink. It is the reference client, the one the protocol is designed against, and the one every other client is measured by.On an ESP32 with AtomVM? Use the AtomVM agent. Be aware that it needs AtomVM built from source rather than a stock build — the WebSocket transport is an ESP-IDF component, so a stock VM cannot reach NervesHub at all.On Linux, but not Nerves? Use the Linux agent. It is a single Rust binary that leaves your application alone in whatever language it is already written in, and shells out to fwup or rauc to write firmware.Building that Linux image with Yocto? Add meta-nerveshub to package the agent, alongside meta-rauc and meta-rust-bin.
None of these are privileged. The server has no notion of an official client — it speaks the documented protocol, and anything that speaks it back is a device.If you are targeting a runtime none of the above covers, the Device WebSocket Protocol and WebSocket Channel Events references describe the connection, the join, and every message in both directions. The existing clients are also worth reading as reference implementations, particularly for reconnect behaviour and update progress reporting.
{"docsConfig":{"theme":"palm","$schema":"https://mintlify.com/docs.json","name":"NervesHub","colors":{"primary":"#6366f1","light":"#818cf8","dark":"#4f46e5"},"logo":{"light":"https://mintcdn.com/nervescloud/5bz8_cB7diUKfY2L/images/logo-lockup-light-t.png?fit=max\u0026auto=format\u0026n=5bz8_cB7diUKfY2L\u0026q=85\u0026s=4fcfcec9804e3d1c56649d112bd05a07","dark":"https://mintcdn.com/nervescloud/5bz8_cB7diUKfY2L/images/logo-lockup-dark.png?fit=max\u0026auto=format\u0026n=5bz8_cB7diUKfY2L\u0026q=85\u0026s=77bf0bf18a9cd7e7d6297f9d4edadb90","href":"/"},"favicon":{"light":"https://mintcdn.com/nervescloud/LUktOqWBfC2J1-GL/images/favicon.svg?fit=max\u0026auto=format\u0026n=LUktOqWBfC2J1-GL\u0026q=85\u0026s=f5dc1749a812cc58283e4a849867d83e","dark":"https://mintcdn.com/nervescloud/LUktOqWBfC2J1-GL/images/favicon.svg?fit=max\u0026auto=format\u0026n=LUktOqWBfC2J1-GL\u0026q=85\u0026s=f5dc1749a812cc58283e4a849867d83e"},"navbar":{"links":[{"href":"https://nerves-hub.org","label":"NervesHub"},{"href":"https://nervescloud.com","label":"NervesCloud"},{"href":"https://discord.gg/Xb4pedSDy9","label":"Discord"}]},"footer":{"socials":{"github":"https://github.com/nerves-hub"}},"contextual":{"options":["copy","view","chatgpt","claude","download-pdf"],"display":"toc"},"styling":{"latex":false},"navigation":{"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"nervescloud","hasAudioNarration":false,"actualSubdomain":"nervescloud","customDomains":["docs.nerves-hub.org"],"gitSource":{"type":"github","owner":"nerves-hub","repo":"docs","deployBranch":"main","contentDirectory":"","isPrivate":false,"mountPath":"","label":""},"entitlements":{"AI_CHAT":{"status":"DISABLED"}},"buildId":"6ab472a1f7c5fd9770140c6a","clientVersion":"0.0.4087","searchFilterCounts":[{"language":"default","tag":"HTTP API","count":79},{"language":"default","tag":"Endpoints","count":77},{"language":"default","tag":"Documentation","count":20},{"language":"default","tag":"Devices","count":14},{"language":"default","tag":"Devices (short URL)","count":10},{"language":"default","tag":"Setup","count":6},{"language":"default","tag":"Organization Members","count":6},{"language":"default","tag":"Products","count":6},{"language":"default","tag":"Integrations","count":5},{"language":"default","tag":"Auth","count":5}]},"assistantConfig":{"enableAskAiButton":false,"enableStarterQuestions":false,"enableContextualStarterQuestions":false,"enableAskAiSearchBar":true},"basePath":"","hasChatPermissions":false,"hasWebMcpPermissions":false,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"rbac-expansion","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"full-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-dual-write","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"approve-review-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-metadata","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"sso-setup-deadline-banner","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-serve","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"live-agents","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"comment-mode","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":false},"customCss":[{"filePath":"style.css","content":".chat-assistant-floating-input{display:none!important}"}],"page":{"pagePath":"integrations/overview","slug":"/integrations/overview","pageMetadata":{"title":"Choosing a Device Client","sidebarTitle":"Overview","description":"The ways a device can connect to NervesHub: NervesHubLink for Nerves, AtomVM on the ESP32, a Rust agent for other Linux systems, and a Yocto layer.","href":"/integrations/overview"},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsx(_components.p, {\n children: \"The ways a device can connect to NervesHub: NervesHubLink for Nerves, AtomVM on the ESP32, a Rust agent for other Linux systems, and a Yocto layer.\"\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={a:`a`,code:`code`,p:`p`,strong:`strong`,tbody:`tbody`,td:`td`,th:`th`,thead:`thead`,tr:`tr`,..._provideComponents(),...props.components},{Heading,Note,Table}=_components;if(!Heading)_missingMdxReference(`Heading`,true);if(!Note)_missingMdxReference(`Note`,true);if(!Table)_missingMdxReference(`Table`,true);return _jsxs(_Fragment,{children:[_jsxs(_components.p,{children:[`A device connects to NervesHub over a Phoenix Channels WebSocket and speaks the `,_jsx(_components.a,{href:`/device-api/websocket-overview`,children:`device protocol`}),`. Nothing about that protocol is Elixir-specific, so a client is whatever software on the device speaks it — reporting the firmware it is running, accepting update assignments, and handing the download to whatever writes firmware on that platform.`]}),`\n`,_jsx(_components.p,{children:`Several clients already exist. Which one you want follows from what the device runs.`}),`\n`,_jsx(Heading,{level:`2`,id:`the-options`,children:`The options`}),`\n`,_jsxs(Table,{children:[_jsx(_components.thead,{children:_jsxs(_components.tr,{children:[_jsx(_components.th,{children:`Client`}),_jsx(_components.th,{children:`Language`}),_jsx(_components.th,{children:`Runs on`}),_jsx(_components.th,{children:`Writes firmware with`})]})}),_jsxs(_components.tbody,{children:[_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.a,{href:`/integrations/nerves-hub-link`,children:`NervesHubLink`})}),_jsx(_components.td,{children:`Elixir`}),_jsx(_components.td,{children:`Nerves`}),_jsx(_components.td,{children:`fwup`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.a,{href:`/integrations/atomvm-esp32`,children:`AtomVM agent`})}),_jsx(_components.td,{children:`Erlang, with an Elixir layer`}),_jsx(_components.td,{children:`AtomVM on the ESP32`}),_jsx(_components.td,{children:`packbeam partitions`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.a,{href:`/integrations/linux-agent`,children:`Linux agent`})}),_jsx(_components.td,{children:`Rust`}),_jsx(_components.td,{children:`Linux that is not Nerves`}),_jsx(_components.td,{children:`fwup or RAUC`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.a,{href:`/integrations/yocto`,children:`meta-nerveshub`})}),_jsx(_components.td,{children:`BitBake`}),_jsx(_components.td,{children:`Yocto builds`}),_jsx(_components.td,{children:`packages the Linux agent`})]})]})]}),`\n`,_jsx(Note,{children:_jsxs(_components.p,{children:[_jsx(_components.code,{children:`meta-nerveshub`}),` is a packaging layer rather than a separate client. It builds the Rust Linux agent into a Yocto image; the agent is what actually connects.`]})}),`\n`,_jsx(Heading,{level:`2`,id:`choosing`,children:`Choosing`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Running Nerves?`}),` Use `,_jsx(_components.a,{href:`/integrations/nerves-hub-link`,children:`NervesHubLink`}),`. It is the reference client, the one the protocol is designed against, and the one every other client is measured by.`]}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`On an ESP32 with AtomVM?`}),` Use the `,_jsx(_components.a,{href:`/integrations/atomvm-esp32`,children:`AtomVM agent`}),`. Be aware that it needs AtomVM built from source rather than a stock build — the WebSocket transport is an ESP-IDF component, so a stock VM cannot reach NervesHub at all.`]}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`On Linux, but not Nerves?`}),` Use the `,_jsx(_components.a,{href:`/integrations/linux-agent`,children:`Linux agent`}),`. It is a single Rust binary that leaves your application alone in whatever language it is already written in, and shells out to `,_jsx(_components.code,{children:`fwup`}),` or `,_jsx(_components.code,{children:`rauc`}),` to write firmware.`]}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Building that Linux image with Yocto?`}),` Add `,_jsx(_components.a,{href:`/integrations/yocto`,children:`meta-nerveshub`}),` to package the agent, alongside `,_jsx(_components.code,{children:`meta-rauc`}),` and `,_jsx(_components.code,{children:`meta-rust-bin`}),`.`]}),`\n`,_jsx(Heading,{level:`2`,id:`writing-your-own`,children:`Writing your own`}),`\n`,_jsx(_components.p,{children:`None of these are privileged. The server has no notion of an official client — it speaks the documented protocol, and anything that speaks it back is a device.`}),`\n`,_jsxs(_components.p,{children:[`If you are targeting a runtime none of the above covers, the `,_jsx(_components.a,{href:`/device-api/websocket-overview`,children:`Device WebSocket Protocol`}),` and `,_jsx(_components.a,{href:`/device-api/websocket-events`,children:`WebSocket Channel Events`}),` references describe the connection, the join, and every message in both directions. The existing clients are also worth reading as reference implementations, particularly for reconnect behaviour and update progress reporting.`]})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"Choosing a Device Client","sidebarTitle":"Overview","description":"The ways a device can connect to NervesHub: NervesHubLink for Nerves, AtomVM on the ESP32, a Rust agent for other Linux systems, and a Yocto layer.","href":"/integrations/overview"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"The options","slug":"the-options","depth":2,"children":[]},{"title":"Choosing","slug":"choosing","depth":2,"children":[]},{"title":"Writing your own","slug":"writing-your-own","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":[],"subdomain":"nervescloud","siteName":"NervesHub","title":"Choosing a Device Client - NervesHub","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-09-23T22:08:19.656Z","originalFileLocation":"integrations/overview.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"The ways a device can connect to NervesHub: NervesHubLink for Nerves, AtomVM on the ESP32, a Rust agent for other Linux systems, and a Yocto layer.\">\u003cmeta data-mint-head name=\"application-name\" content=\"NervesHub\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/nervescloud/N1H5AzYnduZJqgXB/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/nervescloud/N1H5AzYnduZJqgXB/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/nervescloud/N1H5AzYnduZJqgXB/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/nervescloud/N1H5AzYnduZJqgXB/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/nervescloud/N1H5AzYnduZJqgXB/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/nervescloud/N1H5AzYnduZJqgXB/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/nervescloud/N1H5AzYnduZJqgXB/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/nervescloud/N1H5AzYnduZJqgXB/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/nervescloud/N1H5AzYnduZJqgXB/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Choosing a Device Client - NervesHub\">\u003cmeta data-mint-head property=\"og:description\" content=\"The ways a device can connect to NervesHub: NervesHubLink for Nerves, AtomVM on the ESP32, a Rust agent for other Linux systems, and a Yocto layer.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"NervesHub\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.nerves-hub.org/integrations/overview\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://nervescloud.mintlify.site/_mintlify/api/og/v2?division=Documentation\u0026amp;title=Choosing+a+Device+Client\u0026amp;description=The+ways+a+device+can+connect+to+NervesHub%3A+NervesHubLink+for+Nerves%2C+AtomVM+on+the+ESP32%2C+a+Rust+agent+for+other+Linux+systems%2C+and+a+Yocto+layer.\u0026amp;theme=0d044273fdf3829dc983b90b\u0026amp;signature=f394a42d442baf9abe913c4f5b9a1ee5665092e267a25cf72673b0b61dc6046f\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Choosing a Device Client - NervesHub\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"The ways a device can connect to NervesHub: NervesHubLink for Nerves, AtomVM on the ESP32, a Rust agent for other Linux systems, and a Yocto layer.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://nervescloud.mintlify.site/_mintlify/api/og/v2?division=Documentation\u0026amp;title=Choosing+a+Device+Client\u0026amp;description=The+ways+a+device+can+connect+to+NervesHub%3A+NervesHubLink+for+Nerves%2C+AtomVM+on+the+ESP32%2C+a+Rust+agent+for+other+Linux+systems%2C+and+a+Yocto+layer.\u0026amp;theme=0d044273fdf3829dc983b90b\u0026amp;signature=f394a42d442baf9abe913c4f5b9a1ee5665092e267a25cf72673b0b61dc6046f\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/nervescloud/N1H5AzYnduZJqgXB/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"NervesHub\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#6366f1\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.nerves-hub.org/integrations/overview\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/integrations/overview.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.nerves-hub.org/#organization\",\"name\":\"NervesHub\",\"url\":\"https://docs.nerves-hub.org\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/nervescloud/5bz8_cB7diUKfY2L/images/logo-lockup-light-t.png?fit=max\u0026auto=format\u0026n=5bz8_cB7diUKfY2L\u0026q=85\u0026s=4fcfcec9804e3d1c56649d112bd05a07\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.nerves-hub.org#website\",\"name\":\"NervesHub\",\"url\":\"https://docs.nerves-hub.org\",\"publisher\":{\"@id\":\"https://docs.nerves-hub.org/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.nerves-hub.org/integrations/overview#webpage\",\"url\":\"https://docs.nerves-hub.org/integrations/overview\",\"name\":\"Choosing a Device Client\",\"description\":\"The ways a device can connect to NervesHub: NervesHubLink for Nerves, AtomVM on the ESP32, a Rust agent for other Linux systems, and a Yocto layer.\",\"dateModified\":\"2026-09-23T22:08:19.656Z\",\"isPartOf\":{\"@id\":\"https://docs.nerves-hub.org#website\"}},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://docs.nerves-hub.org/integrations/overview#article\",\"headline\":\"Choosing a Device Client\",\"name\":\"Choosing a Device Client\",\"description\":\"The ways a device can connect to NervesHub: NervesHubLink for Nerves, AtomVM on the ESP32, a Rust agent for other Linux systems, and a Yocto layer.\",\"url\":\"https://docs.nerves-hub.org/integrations/overview\",\"mainEntityOfPage\":{\"@id\":\"https://docs.nerves-hub.org/integrations/overview#webpage\"},\"dateModified\":\"2026-09-23T22:08:19.656Z\",\"publisher\":{\"@id\":\"https://docs.nerves-hub.org/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.nerves-hub.org#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"NervesHub\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"NervesHub","nav":{"global":null,"tabs":[{"tab":"Documentation","pages":[{"title":"Documentation","href":"/introduction"}]},{"tab":"Integrations","pages":[{"title":"Choosing a Device Client","sidebarTitle":"Overview","href":"/integrations/overview"},{"title":"NervesHubLink for Nerves","sidebarTitle":"NervesHubLink","description":"The reference NervesHub client for Nerves devices: an Elixir library that maintains the device connection, applies firmware updates, and carries the extensions.","href":"/integrations/nerves-hub-link"},{"title":"AtomVM on ESP32","sidebarTitle":"AtomVM on ESP32","href":"/integrations/atomvm-esp32"},{"title":"Linux Agent","sidebarTitle":"Linux Agent","href":"/integrations/linux-agent"},{"title":"Yocto Layer","sidebarTitle":"Yocto","href":"/integrations/yocto"}]},{"tab":"CLI","pages":[{"title":"CLI","href":"/cli/installation"}]},{"tab":"HTTP API","pages":[{"title":"HTTP API","href":"/api/overview"}]},{"tab":"Device API","pages":[{"title":"Device API","href":"/device-api/websocket-overview"}]}]},"basePath":"","title":"Choosing a Device Client - NervesHub","buildId":"dpl_FWZhTb2vab28gFGFH4ojka6qEznQ","deploymentId":"dpl_FWZhTb2vab28gFGFH4ojka6qEznQ","dynamics":[],"chunks":[]}