Main

How to Reconstruct a Forgotten Z-Wave Network Key from Backup Files

A Z-Wave network key is a 128-bit AES key—16 bytes, normally written as 32 hexadecimal characters—that the host side of your stack uses to encrypt commands and reports exchanged with secure devices: door locks, garage controllers, keypads, sirens. If you run Home Assistant with Z-Wave JS, a standalone Z-Wave JS UI install, or an aging OpenZWave setup, that key exists in plain text in a file on one of your disks right now. This piece is a recovery procedure for the day you need it back: which files hold it in each of those three stacks, how to pull it out of backups you already own, how to confirm you found the right key before trusting it, and when re-including your three secure devices is genuinely faster than an afternoon of searching. Along the way: S0 versus S2 keys, DSK codes, serial API mode, and why the stick itself will not give the key back.

A home-office desk with a laptop open to configuration files during a maintenance window
Recovery happens at whatever machine still holds a copy of the stack’s configuration files, not at the lock itself.

What the key is, and what losing it costs

S0, the original Z-Wave security scheme, uses one shared 16-byte network key. S2, its replacement, uses three network-level keys—Unauthenticated, Authenticated, and Access Control—plus per-device keys derived at inclusion time from the device’s DSK, the QR code or five-digit PIN printed on the device or its manual. The host software holds the network keys; every secure device holds its own copy of whatever it was included with.

Lose the key and the symptom is specific and easy to misread. Devices included without security—most plugs, most sensors—keep working. Secure devices still appear in the mesh, but encrypted commands go unanswered: a lock ignores everything, a secure switch toggles locally but never reports state, and the Z-Wave JS log fills with nonce requests and decryption failures. Nothing crashes, which is why people run half-broken networks for months before diagnosing the real problem.

The cost asymmetry is the reason to go looking. Re-including one lock is a 20–40 minute job at the door: exclude, reset, include with S2, then re-point whatever referenced the old entity. Rebuilding six secure devices across two floors is a weekend. File-diving first is cheap by comparison.

Why the stick will not give the key back

In every mainstream setup—Z-Wave JS, Z-Wave JS UI, OpenZWave, on whatever hardware runs them—the controller operates in serial API mode: an Aeotec, Zooz, or Silabs reference stick handles the radio and routing, and the host software does the security layer. The S0 key is not in the stick’s flash memory; it lives in a JSON or YAML file on the machine that ran the stack. That is why moving a stick to a fresh host gets you a mesh full of nodes and zero working secure devices.

Uncertainty flag, stated plainly: I do not know whether any S2 provisioning data persists in the controller’s NVM across host moves, and I have not found documentation that commits either way. Treat the host-side files as your only source of truth and plan around that assumption.

One pairing rule people miss: the key alone is not the network. Z-Wave JS also keeps a driver cache—node list, routing info, security class per device. To resurrect a network on new hardware you want the key and the cache from the same backup, not the key from 2021 and the cache from last month.

The five places the key hides

In the order I check them, cheapest first.

1. Home Assistant’s .storage directory

The Z-Wave JS integration stores its keys in .storage/core.config_entries, inside the config entry whose domain is zwave_js. The S0 key sits in network_key; the S2 keys in s2_unauthenticated, s2_authenticated, and s2_access_control. Field names have drifted across Home Assistant versions, so read the whole entry rather than trusting my list. The official Z-Wave JS integration documentation covers the setup flow but does not display the key afterward—which is exactly why this article needs to exist.

Handle that file like a credential store, because it is one: it also holds tokens and secrets for other integrations. Never paste it into a forum post when asking for help. Redact first.

2. The Z-Wave JS UI store directory

Z-Wave JS UI (the project formerly named zwavejs2mqtt) keeps its keys in the store volume: a driver cache file named for your network’s home ID—a hex string—alongside per-node JSON and app settings. Search the whole store instead of guessing filenames; versions differ, and recent releases also write rolling cache backups with adjacent names. Grab the newest cache file and its predecessor. The Z-Wave JS UI documentation maps the store layout for your version.

3. Legacy OpenZWave files

Pre-2021 Home Assistant installs kept the key in configuration.yaml under zwave: and network_key:. The zwcfg_*.xml files people tend to hoard contain node configuration, not the key—do not spend an evening on them. One recursive search covers the YAML and any OZW options files at once.

4. Full backups and disk images

A Home Assistant backup (.tar) contains homeassistant.tar.gz, which contains .storage. Two tar commands and you are reading your key from 2022. SD card images of a dead Pi mount fine on another machine; copied Docker volumes work the same way. Both of my recoveries came from this category, and both times the key was in the oldest backup, not the newest—because the oldest is from back when somebody (me) still thought about keys.

5. Off-box copies

Password managers—search for zwave, network key, and the stick’s model number—plus old laptops, cloud sync folders, and the occasional note taped inside a panel door. Spend 15 minutes here before the deep extraction work; it is the cheapest win on the list.

Hands at a laptop keyboard searching backup archives for a configuration string
One recursive search across every candidate directory beats an evening of guessing filenames.

The reconstruction procedure, step by step

Short version: inventory every plausible copy, sweep them all with one recursive search, normalize what you find to 32 hex characters, then test against a real secure device before writing the key anywhere permanent.

  1. Freeze changes. Do not exclude, re-include, or factory-reset anything yet. Every exclusion performed now is a device you cannot un-exclude when the key turns up an hour later.
  2. Inventory candidates. Current Home Assistant config directory, Z-Wave JS store volume, your last several full backups, old SD images, the old laptop, cloud sync folders.
  3. Sweep everything with one recursive search.
    grep -rniE 'network_key|networkkey|securitykeys|s2_access_control|s2_accesscontrol' /path/to/candidates
  4. Open tar backups if you have them.
    tar -xf backup.tar
    tar -xzf homeassistant.tar.gz .storage/core.config_entries

    If the second command says the path is not in the archive, list the contents with tar -tzf homeassistant.tar.gz and adjust—the internal layout has shifted across Home Assistant versions.

  5. Normalize the candidate. Keys appear either as 16 comma-separated hex bytes with 0x prefixes or as a single 32-character hex run. Strip prefixes, spaces, and commas, then count: 16 bytes, 32 characters. Fifteen or seventeen means you truncated a copy—start over from the file, not from memory.
  6. Take the S2 set as a unit. If your lock was included with S2, the S0 key is useless to it. Record all four values together with the home ID from the same file.
  7. Validate before recording. Next section.

Validating the key before you trust it

A key that is one transcription error off behaves exactly like a lost key: devices pair at the radio layer and fail at the crypto layer. So test it. During a maintenance window, plug the stick into a scratch Z-Wave JS UI instance on a laptop, enter the recovered keys in its security settings, and watch the log while the mesh re-interviews.

Right key: nonce exchange with your lock completes without complaint, and a harmless secure query—I use a state report, not a lock operation, and never test against a door that is your only way in—gets an encrypted reply. Wrong key: endless nonce churn, messages dropped as undecryptable, secure devices reported unreachable. Exact log wording varies by version; the pattern does not.

Then record it properly: one password-manager entry per network with all four keys, the home ID, and which backup file they came from, plus a paper copy in the panel or binder. The failure mode that eats your key file can also eat the machine holding your password manager.

A small-office workspace where recovered credentials are recorded in a password manager and on paper
Once validated, the key goes into the password manager and onto paper the same day. A recovered secret you never record is a recovery you will repeat.

If the key is truly gone: the re-inclusion math

Count your secure devices first. That number decides the answer.

One lock and nothing else: skip the hunt. Re-inclusion is 20–40 minutes including re-pointing whatever referenced the lock, and it moves the device onto S2 with a fresh DSK entry. Organizing two years of backups costs more than the fix.

Three or more secure devices, or devices in awkward places—a garage controller on an 11-foot ceiling was mine—recovery wins. My second recovery took 45 minutes of searching across four backup archives. Re-including that network’s four secure devices would have been three hours plus a ladder.

The concession that belongs in every one of these decisions: a network that is already sick—ghost nodes, failed heals, battery devices dropping weekly—is often better rebuilt from zero with fresh S2 keys than patched with a recovered 2019 S0 key. Recovery preserves continuity; a rebuild fixes rot. Diagnose which problem you actually have before choosing.

And the load-level version of the same concession: a porch light on a $12 mechanical timer has no key to lose, no mesh to heal, and no nonce churn at 2 a.m. If a load does not need encrypted state reporting, a plain switch or timer is the reliability play. The key hunt is for loads that genuinely need security—locks, garage doors, anything that opens the house.

Keeping the problem solved

The fix is unglamorous: one password-manager entry per Z-Wave network, created the day you set it up, holding all four keys and the home ID. Add a two-minute line to your quarterly review that checks the entry against what is on disk. I fold mine into the same pass as battery rotation and backup restore drills—the full checklist is in How to Audit the Small Systems That Quietly Run Your Week.

On rotation: changing the S0 key means re-including every secure device, because each one stores a copy. I do not rotate S0 keys. I migrate devices to S2 when I am already touching them for other reasons and let S2’s per-device keys do the work. That is the one situation where re-inclusion and key hygiene are the same task instead of competing ones.

FAQ

Can I read the Z-Wave network key off the USB stick?

No, not in a serial API setup. The stick stores routing and node tables; the security keys live in files on the host that runs Z-Wave JS, Z-Wave JS UI, or OpenZWave. That division of labor is why a working stick plus a fresh host equals a mesh with no working secure devices.

Where exactly does Home Assistant store the Z-Wave network key?

In .storage/core.config_entries, in the config entry for the zwave_js domain—plain text, alongside the S2 keys. Full Home Assistant backups contain that file inside homeassistant.tar.gz. Field names have shifted across versions, so search the whole file for network_key rather than assuming an exact structure.

What is the difference between the S0 key and the S2 keys?

S0 uses one shared 16-byte network key for every secure device. S2 uses three network-level keys—Unauthenticated, Authenticated, Access Control—plus a per-device key derived during inclusion from the device’s DSK. Practical consequence: an S2 device cannot be resurrected with the S0 key, and moving a lock from S0 to S2 requires a fresh inclusion with the DSK PIN.

Could someone capture my network key over the air?

Not from day-to-day traffic; the key is never transmitted after inclusion. The known weak point is S0’s initial key exchange, which is protected by a publicly documented transport key—capturing that single moment can expose the network key, and it is a major reason S2 exists. S2 requires the DSK PIN, which is not sent over the air. If you have S0 locks and a free afternoon, re-including them as S2 closes this hole permanently.

If I am re-including everything anyway, do I need the old key?

No. Fresh inclusions generate fresh keys. The old key only matters when you want existing secure devices to keep working without physically touching them—restoring onto new hardware, or splitting a network across two hosts.

Where this goes next

The companion procedure—the other half of this problem—is moving an existing network onto a new controller stick without re-including anything, including which parts of the Z-Wave JS cache are safe to edit by hand and which will corrupt on you. That is the next piece in this series. If you are sitting on a key-less network right now, start with the oldest backup you own. Both of mine were hiding there.