Quickstart
Connect a Nerves device and ship your first OTA update with shared-secret auth — from mix nerves.new to a device updating.
Written By Josh Kalderimis
Last updated About 2 months ago
Get a device receiving an OTA update, using the simplest setup: shared-secret auth.
End result: a Nerves project connected to NervesHub, a device in the web UI, signed firmware uploaded, and a deployment group that ships an update.
Shared secrets suit hobby and R&D projects. For production, use certificate auth — see Device authentication & mTLS.
Prerequisites
A working Nerves setup (Elixir, Nerves bootstrap,
fwup).A supported target (e.g. Raspberry Pi) and an SD card.
An account on NervesCloud or a self-hosted instance.
This guide uses manage.nervescloud.com as the host — swap in your own if self-hosting.
1. Create the project
mix nerves.new my_app
cd my_app
export MIX_TARGET=rpi0_2 # your target2. Create a product and shared secret
In the web UI:
Create or choose an organization.
Create a product (name it
my_appto match, for convenience).Settings → Shared Secrets → New. Copy the product key and product secret.
3. Add NervesHubLink
In mix.exs, add it to your target deps:
{:nerves_hub_link, "~> 2.2"},4. Configure the shared secret
In config/target.exs:
config :nerves_hub_link,
host: "devices.nervescloud.com",
shared_secret: [
product_key: "<product_key>",
product_secret: "<product_secret>"
]Keep real secrets out of version control — read them from the environment for anything beyond a quick test.
5. Build and burn
mix deps.get
mix firmware
mix burnInsert the SD card, power on. The device boots, connects, and opens a websocket to NervesHub.
6. Confirm the device connected
Web UI → your product → Devices. The device appears and shows as connected within a few moments.
7. Install the CLI
brew install nerves-hub/tap/nh
# or:
curl --proto '=https' --tlsv1.2 -fsSL \
https://raw.githubusercontent.com/nerves-hub/nerves_hub_cli/master/install.sh | sh
Self-hosting? Point it at your instance first:
nh config set uri "https://my.selfhosted.instance/"Then authenticate:
nh user auth8. Create firmware signing keys
Firmware must be signed. Create a key pair:
nh key create my_app_keyThis registers the public key with your org so NervesHub can verify signatures. Keep the private key safe — you need it to sign every image. (You can also manage keys in the web UI under Settings → Signing Keys.)
9. Point your project at the signing key
Provide the keys via environment variables:
export NERVES_CLOUD_FW_PRIVATE_KEY=$(cat ~/.nerves-cloud/keys/my_app_key.priv)
export NERVES_CLOUD_FW_PUBLIC_KEY=$(cat ~/.nerves-cloud/keys/my_app_key.pub)Paths/var names vary by CLI version and platform — see the CLI reference. The
NERVES_HUB_*equivalents also work.
10. Build and publish signed firmware
Make a visible change (e.g. a log line) and bump the version in mix.exs, then:
mix firmware
nh firmware publishOr upload in the web UI under Firmware → Upload.
11. Create a deployment group
Web UI → Deployment Groups → New:
Select the firmware you just published as the release.
Set targeting conditions (version + tags). Leave tags empty for now so it matches your device.
Save and mark it active.
Full options: Deployment groups & releases.
12. Watch it update
With an active group pointing at newer firmware, NervesHub offers the update. The device downloads it, verifies the signature, applies it, and reboots into the new version. Track progress on the device's page.
That's the full round trip: build → sign → publish → deploy → update. 🎉