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

# Deployment Guide

> Deploy nao with Docker on your own cloud infrastructure

This is a 5 steps guide to create a nao context project, and deploy nao chat UI in your own infrastructure.

## Step 1: Create your nao context repository

**1. Create a new folder / repository and initialize a nao project**

```bash theme={null}
nao init
```

Run a first nao sync if you want to start populating content in your repo:

```bash theme={null}
nao init
```

**2. In `nao_config.yaml`, replace your secrets by environment variables**

Instead of hard‑coding credentials (service account keys, API tokens, database passwords) in your config file, reference them via environment variables.\
This keeps sensitive values out of Git, makes it easier to rotate keys, and lets you reuse the same config across local, staging, and production.

Example:

```yaml theme={null}
databases:
  - name: bigquery-prod
    accessors:
      - columns
      - preview
      - description
    include: []
    exclude: []
    type: bigquery
    project_id: nao-production
    dataset_id: prod_silver
    credentials_json: {{ env('GCP_SERVICE_ACCOUNT_KEY_JSON') }}
    location: EU
```

You’ll later provide `GCP_SERVICE_ACCOUNT_KEY_JSON` via your deployment platform (for example, as a Secret in Google Secret Manager wired to an env var in Cloud Run).

**3. Init a git repository from your context folder**

Versioning your nao project in Git lets you review changes to context, roll back safely, and collaborate with your team using pull requests.
It also allows your deployed nao chat to directly sync with the contet in your GitHub repository.

```bash theme={null}
git init
git add .
git commit -m "Initial nao project"
```

<Card title="Git Repository Setup" icon="git" href="/nao-agent/context-builder/git-repository">
  Learn how to turn your nao project into a GitHub repo and follow best practices
</Card>

## Step 2: Create Dockerfile

Create a `Dockerfile` in your repository, using the official [`getnao/nao` image](https://hub.docker.com/r/getnao/nao) as the base:

```dockerfile theme={null}
FROM getnao/nao:latest

# Copy your project files
COPY . /app/project/

# Set working directory
WORKDIR /app/project
```

Create a `.dockerignore`:

```text theme={null}
.env
venv/
.gitignore
.DS_Store
```

Commit and push the created files.

### Alternative: use `NAO_CONTEXT_GIT_URL` instead of baking the project

If your nao context already lives in a Git repository, you don't strictly need to copy the project into the container image.\
Instead, you can point the runtime to your Git repo via the `NAO_CONTEXT_GIT_URL` environment variable and let the container clone it on startup.

At a high level:

* **Image**: use the stock `getnao/nao` image (or the Dockerfile above without the `COPY` step)
* **Git repository**: the same repo you configured in `nao_config.yaml` / the [Repositories](/nao-agent/context-builder/repos) docs
* **Environment variable**: set `NAO_CONTEXT_GIT_URL` to the HTTPS URL of your context repository

Example environment variable value:

```bash theme={null}
NAO_CONTEXT_GIT_URL=https://github.com/your-org/your-nao-context.git
```

When this variable is set, the container will clone that repository into the default project path at startup, so you don't have to build a custom image just to include your context code.

You can still combine this with Git‑based deployments (for example Cloud Run building from your repo): in that case the Git repository is both the build source and the runtime context, and `NAO_CONTEXT_GIT_URL` makes the linkage explicit.

#### Authenticate with a GitHub deploy key (SSH)

For private repositories, use an SSH deploy key instead of a personal access token. nao clones context from both **GitHub** and **Bitbucket**. Generate an `ed25519` key pair, add the public key as a deploy key on the GitHub or Bitbucket repo (read-only is enough), and pass the private key through `NAO_CONTEXT_GIT_SSH_KEY`:

```bash theme={null}
NAO_CONTEXT_GIT_URL=git@github.com:your-org/your-nao-context.git
NAO_CONTEXT_GIT_SSH_KEY="-----BEGIN OPENSSH PRIVATE KEY-----
...
-----END OPENSSH PRIVATE KEY-----"
```

For HTTPS auth with a personal access token, use `NAO_CONTEXT_GIT_TOKEN` instead:

```bash theme={null}
NAO_CONTEXT_GIT_URL=https://github.com/your-org/your-nao-context.git
NAO_CONTEXT_GIT_TOKEN={{ secret_pat }}
```

The container picks the auth scheme from the URL: `git@…` / `ssh://…` requires `NAO_CONTEXT_GIT_SSH_KEY`, `https://…` accepts an optional `NAO_CONTEXT_GIT_TOKEN`. GitHub and Bitbucket host keys are pre-pinned in the entrypoint, so SSH connects to either without a known-hosts prompt.

#### Pick a branch or subpath

Two more environment variables let you scope what the container clones:

* `NAO_CONTEXT_GIT_BRANCH` - branch to check out (defaults to `main`).
* `NAO_CONTEXT_GIT_SUBPATH` - clone only a subdirectory of the repo via sparse checkout. Useful when your nao project lives next to other code in a monorepo.

```bash theme={null}
NAO_CONTEXT_GIT_URL=git@github.com:your-org/data-platform.git
NAO_CONTEXT_GIT_BRANCH=production
NAO_CONTEXT_GIT_SUBPATH=tools/nao
NAO_CONTEXT_GIT_SSH_KEY={{ secret_ssh_key }}
```

## Step 3: Create a PostgreSQL database

1. Create a PostgreSQL instance - here I'm using Cloud SQL.
2. Allow unencrypted network traffic (or configure SSL).
3. Enable **Private API** connections.
4. Note your connection string / instance connection name.

## Step 4: Deploy nao on your cloud infrastructure

In this guide we’ll use **Google Cloud Run** as a concrete example, but the same pattern applies to other container platforms (ECS, Kubernetes, etc.).\
You’ll build a Docker image from your nao project and deploy it to Cloud Run, connect it to a managed PostgreSQL instance (Cloud SQL), and load secrets via Google Secret Manager.

### 4.1 Configure Environment Variables

Create secrets in Google Secret Manager for all sensitive values:

* `OPENAI_API_KEY` / `ANTHROPIC_API_KEY`
* `GCP_SERVICE_ACCOUNT_KEY_JSON` (full JSON content of the BigQuery service account, or other warehouse provider secrets)
* `DB_URI` with your PostgreSQL URI – used for storing your app DB. For example:

```text theme={null}
postgres://[user_name]:[password]@[host]:[port]/[database]
```

* `BETTER_AUTH_SECRET` – a long random string used to sign authentication sessions. Generate one with:

```bash theme={null}
openssl rand -base64 32
```

<Warning>
  **This is critical.** If you don't set `BETTER_AUTH_SECRET` to a stable value, a new secret will be generated on every container restart, which invalidates all existing sessions and forces every user to sign in again after each redeploy or restart. Generate the value **once**, store it in Secret Manager, and reuse it across deployments.
</Warning>

* Any other secrets used in `nao_config.yaml` (e.g. Notion key)

### 4.2 Configure Cloud Run service

1. Create a new Cloud Run service.

<Frame>
  <img src="https://mintcdn.com/naolabs/H8nuWO6cOsYjJyxc/images/nao-agent/self-host/deploy_container.png?fit=max&auto=format&n=H8nuWO6cOsYjJyxc&q=85&s=c4e19355978c8d042c3f97e585ad1154" alt="Cloud Run - Deploy container button" width="800" data-path="images/nao-agent/self-host/deploy_container.png" />
</Frame>

2. Use your GitHub repository as the source and set up Cloud Build to build the Dockerfile.

Create service from a GitHub repository.

<Frame>
  <img src="https://mintcdn.com/naolabs/H8nuWO6cOsYjJyxc/images/nao-agent/self-host/deploy_from_git.png?fit=max&auto=format&n=H8nuWO6cOsYjJyxc&q=85&s=3c9d17ecbf310a8cc953a8390233eb3e" alt="Cloud Run - Deploy from git" width="800" data-path="images/nao-agent/self-host/deploy_from_git.png" />
</Frame>

Choose your context Git Repository.

<Frame>
  <img src="https://mintcdn.com/naolabs/H8nuWO6cOsYjJyxc/images/nao-agent/self-host/choose_repo.png?fit=max&auto=format&n=H8nuWO6cOsYjJyxc&q=85&s=686e43565eeb7b84aa0042157e3dfe97" alt="Cloud Run - Choose repository" width="800" data-path="images/nao-agent/self-host/choose_repo.png" />
</Frame>

Setup synchronization with your main branch.

<Frame>
  <img src="https://mintcdn.com/naolabs/H8nuWO6cOsYjJyxc/images/nao-agent/self-host/build_config.png?fit=max&auto=format&n=H8nuWO6cOsYjJyxc&q=85&s=6811251d002a9ec343957b379b7f3bc6" alt="Cloud Run - Build configuration" width="800" data-path="images/nao-agent/self-host/build_config.png" />
</Frame>

<Info>
  For Cloud Run deployments sourced from GitHub, Cloud Build will automatically rebuild and redeploy on each push to the configured branch.
  So your context will always be up to date with the context in the main branch.
</Info>

3. In the service configuration, add these configurations:

**Container:**
Container port: `5005`

**Environment variables:**

```bash theme={null}
NAO_DEFAULT_PROJECT_PATH=/app/project
BETTER_AUTH_URL=https://placeholder.run.app   # will be updated after first deploy
```

<Frame>
  <img src="https://mintcdn.com/naolabs/H8nuWO6cOsYjJyxc/images/nao-agent/self-host/env_vars.png?fit=max&auto=format&n=H8nuWO6cOsYjJyxc&q=85&s=2123074b38ef91ef193804e229055124" alt="Cloud Run - Environment variables" width="800" data-path="images/nao-agent/self-host/env_vars.png" />
</Frame>

**Secrets (from Secret Manager):**

* `OPENAI_API_KEY` / `ANTHROPIC_API_KEY`
* `GCP_SERVICE_ACCOUNT_KEY_JSON`
* `DB_URI`
* `BETTER_AUTH_SECRET` – wire it from Secret Manager so the same value is reused across every revision. Without this, users get signed out on every restart.

<Frame>
  <img src="https://mintcdn.com/naolabs/H8nuWO6cOsYjJyxc/images/nao-agent/self-host/secrets.png?fit=max&auto=format&n=H8nuWO6cOsYjJyxc&q=85&s=678a978d9db650232c01df6ea14f6c8d" alt="Cloud Run - Secrets" width="800" data-path="images/nao-agent/self-host/secrets.png" />
</Frame>

**Connections:**

* Add Cloud SQL connection to your PostgreSQL instance.

<Frame>
  <img src="https://mintcdn.com/naolabs/H8nuWO6cOsYjJyxc/images/nao-agent/self-host/cloud_sql.png?fit=max&auto=format&n=H8nuWO6cOsYjJyxc&q=85&s=2940164c41cee406d85b8b61f7e24e10" alt="Cloud Run - Cloud SQL" width="800" data-path="images/nao-agent/self-host/cloud_sql.png" />
</Frame>

**Networking:**

* Activate "Connect to VPC for outbound traffic" / "Send traffic directly to a VPC"

<Frame>
  <img src="https://mintcdn.com/naolabs/H8nuWO6cOsYjJyxc/images/nao-agent/self-host/networking.png?fit=max&auto=format&n=H8nuWO6cOsYjJyxc&q=85&s=c49cd24c01b62c68e84d95f0cfb57bac" alt="Cloud Run - Networking" width="800" data-path="images/nao-agent/self-host/networking.png" />
</Frame>

4. Deploy the service.
5. Once the service is live, copy the Cloud Run URL and update:

```bash theme={null}
BETTER_AUTH_URL=https://your-cloud-run-url.run.app
```

6. Open this URL in your browser, confirm that the nao chat UI loads, and complete the first sign‑up flow.\
   At this stage, only the very first user can sign up directly; additional users are added and authorized later via the admin setup and user management flows.

<Frame>
  <img src="https://mintcdn.com/naolabs/H8nuWO6cOsYjJyxc/images/nao-agent/self-host/signup.png?fit=max&auto=format&n=H8nuWO6cOsYjJyxc&q=85&s=a6a15350c40a043cc7618e38f56bb055" alt="Cloud Run - Sign up" width="800" data-path="images/nao-agent/self-host/signup.png" />
</Frame>

7. Map a custom domain (optional)

If you prefer a friendly URL instead of the default Cloud Run URL:

1. In Cloud Run, go to **Domain mappings**.
2. Verify your domain.
3. Add the subdomain you want to use (e.g. `sky.naolabs.io`).
4. Add the required DNS records at your DNS provider.
5. Update `BETTER_AUTH_URL` to the custom domain, for example:

```bash theme={null}
BETTER_AUTH_URL=https://sky.naolabs.io
```

## Version update notifications

nao checks the latest release on GitHub and shows an amber notification at the bottom of the sidebar when your deployment is running an older version. The notification links directly to the release page so you can review what changed.

* Visible to **admin users only** - non-admins do not see the notification.
* The GitHub check is cached for 1 hour to avoid rate limiting.
* Works for both Docker and CLI deployments.
* If the GitHub API is unreachable (e.g. offline environments), the notification is silently skipped.

## Step 5: Customize your setup

### 5.1 Add users to your app

Once your chat UI is deployed, invite teammates and manage access from the admin interface.

<Card title="Admin Setup" icon="user-shield" href="/nao-agent/chat/admin/setup">
  Invite users, configure authentication, and manage access
</Card>

### 5.2 Automate nao sync with GitHub Actions

Automate `nao sync` so your context stays up to date in Git.

<Card title="Synchronization" icon="sync" href="/nao-agent/context-builder/synchronization">
  Set up GitHub Actions workflows to automatically sync your context
</Card>

### 5.3 Connect a Slack bot

Expose your analytics agent directly in Slack so teams can ask questions where they work.

<Card title="Slack Bot" icon="slack" href="/nao-agent/connectors/slack">
  Connect your deployed agent to Slack channels
</Card>

### 5.4 Enable Google OAuth for user sign‑in

To allow users to sign in with Google and control which domains can self‑register:

<Card title="Google Auth Setup" icon="key" href="/nao-agent/chat/admin/setup#google-auth-sign-up">
  Configure Google OAuth for domain-based sign-up
</Card>

### 5.5 Host several projects on this instance

Serve more than one project from the same deployment and domain, with a project switcher.

<Card title="Host Multiple Projects" icon="folders" href="/nao-agent/self-hosting/multiple-projects">
  Run several nao projects on one instance with `NAO_MODE=cloud`
</Card>
