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 target

2. Create a product and shared secret

In the web UI:

  1. Create or choose an organization.

  2. Create a product (name it my_app to match, for convenience).

  3. 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 burn

Insert 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 auth

8. Create firmware signing keys

Firmware must be signed. Create a key pair:

nh key create my_app_key

This 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 publish

Or upload in the web UI under Firmware → Upload.

11. Create a deployment group

Web UI → Deployment Groups → New:

  1. Select the firmware you just published as the release.

  2. Set targeting conditions (version + tags). Leave tags empty for now so it matches your device.

  3. 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. 🎉

Next steps