Skip to main content
Firmware signing ensures that only firmware produced by a party you trust can run on your devices. Before a device applies any OTA update, it verifies the signature embedded in the .fw file against a public key that was compiled into the firmware at build time. This verification happens entirely on the device — independently of NervesHub — so the chain of trust holds even if the server or the network is compromised.

How It Works

Signing happens inside the fwup tool that packages Nerves firmware, using a public/private key pair you create. The signature is then checked in two places:
  1. At build time, you embed the public key into your firmware image via the fwup_public_keys configuration. Every device that runs that firmware will only accept OTA updates signed by the corresponding private key.
  2. When you upload firmware to NervesHub, the server checks the signature against the public keys registered for your product. Unsigned firmware or firmware signed by an unregistered key is rejected at upload time.
The private key is used only during the signing step and should never leave your secure build environment.

Creating a Signing Key

Use the nh CLI to generate a keypair and register the public key with your NervesHub product in one step:
This command creates the keypair locally and uploads the public key to NervesHub. The private key is stored in your local nh configuration directory — treat it with the same care as any other secret credential.
Never commit signing private keys to source control. In CI/CD pipelines, pass the private key via the NERVES_HUB_FW_PRIVATE_KEY environment variable rather than writing it to disk. Rotate any key that has been exposed and remove the corresponding public key from NervesHub immediately.

Embedding the Public Key in Firmware

For a device to verify firmware signatures independently, the public key must be compiled into the firmware image at build time. Add the base64-encoded public key to your config/target.exs:
You can retrieve the base64-encoded public key for any registered key from the NervesHub console or with:
Replace "base64_encoded_public_key_here" with the value shown for your key. Rebuild and re-flash any devices that need to accept firmware signed by the new key — the embedded public keys are part of the firmware itself and cannot be updated over the air without a matching signed update.

Signing and Uploading Firmware

Once you have a signing key registered, sign your firmware and upload it to NervesHub in a single command:
The CLI signs the .fw file with the private key associated with my-key and then uploads the signed binary to NervesHub. The server verifies the signature against the registered public key before accepting the upload.
If your build system already signs the firmware (for example, in a CI pipeline using a hardware signing service), pass --skip-signing to upload the pre-signed file without re-signing it:
The server will still verify the signature against registered keys — --skip-signing only skips the client-side signing step.

Using Multiple Signing Keys

Maintaining more than one signing key is a best practice for production fleets. A common setup uses two keys:
  • Production key — tightly controlled; used only in your release pipeline; private key stored in a secrets manager or HSM.
  • Development key — used for internal builds and QA; acceptable to store in developer workstations.
To support multiple keys:
  1. Register each key with NervesHub:
  2. Embed all accepted public keys in your firmware. A device will accept firmware signed by any of the keys listed in fwup_public_keys:
  3. Sign each firmware with the appropriate key for its intended audience:
Production devices can be built with only the production public key embedded (removing the dev key from fwup_public_keys in your production Mix target), ensuring that development-signed firmware cannot be installed on production hardware even if an attacker gains access to the dev key.
Removing a public key from fwup_public_keys and rebuilding only affects devices that receive the new firmware. Devices still running older firmware will continue to accept the keys that were embedded when that firmware was built.