> ## 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.

# Air-gap tarball

> Install Nora on a machine with zero internet access. All container images bundled into one archive.

The air-gap tarball is a self-contained archive that installs Nora on a host with **zero internet access**. All five container images are baked in via `docker save`, so `install.sh` never touches a registry.

## What's in the tarball

The tarball is produced by `infra/onprem/build-tarball.sh` and named `nora-onprem-<version>.tar.gz`. Inside you'll find:

| File | Purpose |
| - | - |
| `docker-images.tar` | `docker save` output of all five service images |
| `docker-compose.yml` | The same compose file as online installs |
| `.env.example` | Env template with `CHANGE_ME` sentinels |
| `install.sh` | `docker load` and print next-step instructions |
| `README.md` | A one-page install guide |
| `VERSION` | The release tag matching the tarball filename |

## Prerequisites on the target host

* **Docker Engine 20.10+** (`docker --version`).
* **The Docker Compose plugin** (`docker compose version`).
* Around **15 GB of free disk** for the images.
* **4 GB of RAM available to Docker.**

No repository access, no `docker pull`, no package download is required on the target.

## Install

<Steps>
  <Step title="Extract the tarball">
    ```bash theme={null}
    tar -xzf nora-onprem-<version>.tar.gz -C /opt/nora
    cd /opt/nora
    ```
  </Step>

  <Step title="Run the installer">
    ```bash theme={null}
    ./install.sh
    ```

    The script does three things — nothing more, and it never binds a port:

    1. Loads the five service images into your local Docker daemon (`pgvector`, `redis`, `qdrant`, `nora-server`, `nora-llm-bridge`).
    2. Copies `.env.example` to `.env.onprem` only if `.env.onprem` doesn't already exist (safe to re-run for upgrades).
    3. Prints the next-step instructions.

    Pass `--dry-run` to preview without touching Docker.
  </Step>
</Steps>

## Configure

Open `/opt/nora/.env.onprem` and set the values that apply to your deployment.

* **Required — `POSTGRES_PASSWORD`.** The bundled Postgres container's superuser password. Compose refuses to boot with a `CHANGE_ME_*` placeholder left in place.
* **Recommended — `NORA_LICENSE_FILE`.** The path to your license JSON, provided by Nora sales. Drop the file next to `.env.onprem` and set the path. Without one, Nora runs in **free tier** (1 workspace, 3 flows, 100 daily invokes).
* **Recommended — `NORA_AIRGAP=1`.** Enables the boot probe. The server exits with code 78 at boot if it can still reach the public internet, so a misconfigured egress rule can't silently break isolation.
* **Optional — SSO and OAuth pairs** (`GOOGLE_OAUTH_CLIENT_ID/_SECRET`, `GITHUB_OAUTH_CLIENT_ID/_SECRET`). Only useful when the network can reach the provider's OAuth endpoints. Leave these blank in fully air-gapped installs — local email-and-password accounts still work.

## Boot

```bash theme={null}
cd /opt/nora
docker compose --env-file .env.onprem up -d
```

The stack listens on `http://localhost:${NORA_HOST_PORT:-8090}`. First boot runs SQL migrations automatically because `NORA_RUN_MIGRATIONS=1` is on by default.

## Upgrade

When a new tarball arrives, extract it next to the current install and re-run `install.sh`. Image tags roll forward, compose is re-applied, and database migrations are additive.

```bash theme={null}
tar -xzf nora-onprem-<newver>.tar.gz -C /opt/nora
cd /opt/nora
./install.sh
docker compose --env-file .env.onprem up -d
```

Desktop-app operators do the same thing via **Nora → \[Import Update…]**. See [Updates](/local-app/desktop/updates).

## Uninstall or reset

```bash theme={null}
cd /opt/nora
docker compose --env-file .env.onprem down -v
```

The `-v` drops the Postgres and Qdrant volumes — workspaces, users, flows, and vector indexes are gone. Skip `-v` to keep the data across `up` and `down` cycles.

## What version am I on?

```bash theme={null}
cat /opt/nora/VERSION
```

The tag matches the tarball filename, and it's also visible under **Settings → About** in the running SPA.

## Troubleshooting

Follow the server logs:

```bash theme={null}
docker compose -f /opt/nora/docker-compose.yml --env-file /opt/nora/.env.onprem \
  logs -f nora-server
```

The most common first-install failures:

* **`POSTGRES_PASSWORD` is still a `CHANGE_ME_*` placeholder.** Compose exits before starting.
* **`NORA_LICENSE_FILE` path doesn't exist or isn't readable by Docker.** The server logs the missing path and drops to free tier — attempts to create a second workspace are refused.
* **Host port 8090 is already bound.** Set `NORA_HOST_PORT` to something else in `.env.onprem`.
* **`NORA_AIRGAP=1` but egress isn't actually blocked.** The server exits with code 78. Fix the firewall rules and boot again.

## Building the tarball (for release engineers)

From the platform repo on a host with a working Docker daemon:

```bash theme={null}
./infra/onprem/build-tarball.sh                       # tag = git SHA
NORA_IMAGE_TAG=1.2.3 ./infra/onprem/build-tarball.sh  # semver release
```

The output lands in `dist/nora-onprem-<version>.tar.gz`. The first build takes about 30 minutes (mostly the Rust image); rebuilds with a warm cache are much faster.

## Related

<CardGroup cols={2}>
  <Card title="Docker Compose" icon="docker" href="/local-app/deploy/compose">
    The online version of the same compose file.
  </Card>

  <Card title="Kubernetes (Helm)" icon="dharmachakra" href="/local-app/deploy/kubernetes">
    Load the tarball images into a private registry, then override the chart values.
  </Card>
</CardGroup>
