> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nora.my/llms.txt
> Use this file to discover all available pages before exploring further.

# Updates

> Check for updates online, or import an offline update tarball. The app shell and container images update independently.

The desktop app has two things it can update independently.

* The **app shell** — the Tauri binary that renders the window and manages the stack. This is `Nora.app`, `Nora.exe`, or the `.AppImage` you downloaded.
* The **container images** — the five services (`nora-server`, `nora-llm-bridge`, `postgres`, `redis`, `qdrant`) that actually run Nora.

Both paths open the same popup window, launched from the **Nora → \[Check for Updates…]** or **Nora → \[Import Update…]** menu.

## Online — Check for Updates

The **Check for updates** tab fetches the release manifest and shows both versions side by side.

* The **Containers** row compares `NORA_IMAGE_TAG` (from `.env.onprem`) against the latest tag in the manifest. When a newer tag is available, click **\[Update containers]**.
* The **Desktop app** row compares the running Tauri binary against the manifest. When a newer build is available, click **\[Get app update →]** and Nora opens the download URL in your default browser. Replacing the binary is a manual step — the same drag-to-Applications or `.msi` flow as the first install.

### The update wizard

Clicking **\[Update containers]** switches the popup to a step checklist and streams the log inline. There are four steps:

| # | Step | What it does |
| - | - | - |
| 1 | Pin target version | Writes `NORA_IMAGE_TAG=<latest>` to `.env.onprem` so compose interpolation picks it up |
| 2 | Pull updated images | Runs `docker compose pull --ignore-buildable` |
| 3 | Restart stack | Runs `docker compose up -d --force-recreate` |
| 4 | Wait for server to answer | Polls the health endpoint until the new server answers |

<Tip>
  Postgres, Redis, and Qdrant are left alone during an update. Nothing about a Nora release touches their images.
</Tip>

### Manifest location

The manifest lives at `https://updates.nora.my/latest.json` by default. Override it with the `NORA_UPDATE_MANIFEST_URL` environment variable when you mirror releases into a staging bucket or an enterprise artefact server.

### When the manifest is unreachable

If the corporate proxy blocks HTTPS, the network is down, or the URL is misconfigured, the popup still renders. It shows a hint next to the online section, but the **Import from file** tab keeps working — so you're never stuck.

## Offline — Import from file

The **Import from file** tab is for air-gapped operators. You receive a `nora-onprem-<version>.tar.gz` bundle out-of-band (USB drive, internal artefact server), and the popup ingests it end-to-end.

<Steps>
  <Step title="Drop or paste the tarball path">
    Drag the tarball onto the drop zone, or paste its full path into the input.
  </Step>

  <Step title="Click Import + restart">
    The popup switches to a five-step checklist and streams the log inline.
  </Step>
</Steps>

### The import wizard

| # | Step | What it does |
| - | - | - |
| 1 | Extract tarball | Extracts the archive into a scratch directory under `~/.nora/updates/` |
| 2 | Load images into Docker | Runs `docker load -i docker-images.tar` for the bundled image set |
| 3 | Update compose + pin version | Overwrites `~/.nora/onprem/docker-compose.yml` with the bundled copy and writes `NORA_IMAGE_TAG` from the tarball's `VERSION` file |
| 4 | Restart stack | Runs `docker compose up -d --force-recreate` |
| 5 | Wait for server to answer | Polls the health endpoint until the new server answers |

The scratch directory is deleted before the restart runs, so the 15 GB tarball isn't holding memory-mapped pages during compose bring-up.

## Version pinning

Every successful update writes the new tag into `~/.nora/onprem/.env.onprem` as `NORA_IMAGE_TAG`. The **Containers** row on the update popup always reflects the last tag you actually installed — there's no drift between what the popup says and what's running.

## Downgrade

Downgrading isn't a first-class feature, but it works: install an older shell binary manually, or import a tarball for the older version. The compose stack tears down and comes back up on the pinned tag either way.

<Warning>
  Migrations are additive. An older server refuses to boot against a newer database schema rather than corrupting data, so if the schema has advanced, plan the downgrade against a matching database snapshot.
</Warning>

## Next

<CardGroup cols={2}>
  <Card title="Settings" icon="sliders" href="/local-app/desktop/settings">
    License key, host port, reset, and full uninstall.
  </Card>

  <Card title="Air-gap tarball" icon="box-archive" href="/local-app/deploy/airgap">
    Build and ship the same tarball for a fresh installation.
  </Card>
</CardGroup>
