# Install

Install surrealctl on macOS, Linux, or Windows, verify the download, sign in for the first time, and keep the binary up to date.

This page installs the `surrealctl` binary, signs you in to SurrealDB Cloud, and confirms that both worked. Pick one installation method. Everything after it applies whichever you chose.

`surrealctl` is a single executable with no runtime dependencies. It is published for macOS, Linux, and Windows, on both `arm64` and `amd64`.

> [!NOTE]
> The current release is a beta. Until the first stable release, the install script and `surrealctl upgrade` both follow the beta channel, and the version you get carries a `-beta` suffix.

## Install with the install script

On macOS and Linux, the install script detects your platform and CPU, downloads the matching asset, checks it against its published SHA-256, and only then puts it in place.

```bash
curl -fsSL https://download.surrealdb.com/surrealctl/install.sh | sh
```

The script is served as plain text, so you can open the same URL in a browser and read it before you run it.

It installs into the first writable directory of `~/.local/bin` and `/usr/local/bin`, creating `~/.local/bin` if neither exists. **It never uses `sudo`.** If the directory it picks is not on your `PATH`, the script tells you and does not edit your shell profile.

### Options

Pass options after `-s --`, which is how `sh` forwards arguments to a script it reads from a pipe.

```bash
curl -fsSL https://download.surrealdb.com/surrealctl/install.sh | sh -s -- --to ~/bin
```

| Option | Effect |
| --- | --- |
| `--version <VERSION>` | Install this exact version, written `1.0.0` or `v1.0.0` |
| `--beta` | Install the newest beta release |
| `--alpha` | Install the newest alpha release |
| `--to <DIR>` | Install into this directory |
| `--dry-run` | Print what would be downloaded and installed, then stop |
| `-h`, `--help` | Print the options and exit |

Two environment variables do the same job as options, which is easier to set in a Dockerfile or a CI step.

| Variable | Effect |
| --- | --- |
| `SURREALCTL_INSTALL_DIR` | Same as `--to` |
| `SURREALCTL_DOWNLOAD_ROOT` | Point the script at a different release host, for testing |

Use `--dry-run` to see the exact URL and destination before anything is written.

```bash title="Check what the script would do"
curl -fsSL https://download.surrealdb.com/surrealctl/install.sh | sh -s -- --dry-run
```

> [!NOTE]
> No musl build is published yet. On Alpine and other musl-based distributions the script stops and says so, rather than installing a glibc binary that cannot start. Build from source on those systems.

## Install on Windows

Download the executable, then put it somewhere on your `PATH`.

```powershell
$version = (Invoke-WebRequest -Uri https://download.surrealdb.com/surrealctl/beta.txt -UseBasicParsing).Content.Trim()
Invoke-WebRequest -Uri "https://download.surrealdb.com/surrealctl/$version/surrealctl-$version.windows-amd64.exe" -OutFile surrealctl.exe
```

Read `latest.txt` instead of `beta.txt` once a stable release is published.

## Download a binary

Every release is published under `https://download.surrealdb.com/surrealctl/`. Use this route for container images, air-gapped hosts, and anywhere you want the version pinned in source control.

Three text files name the current version of each channel. Each contains one line, such as `v1.0.0-beta.1`.

| File | Channel |
| --- | --- |
| `latest.txt` | Stable |
| `beta.txt` | Beta |
| `alpha.txt` | Alpha |

```bash title="Download and install the current beta"
version=$(curl -fsSL https://download.surrealdb.com/surrealctl/beta.txt)
curl -fsSL "https://download.surrealdb.com/surrealctl/${version}/surrealctl-${version}.darwin-arm64.tgz" \
  | tar -xz -C ~/.local/bin
```

Replace the asset name to match your platform.

| Platform | Asset |
| --- | --- |
| macOS, Apple silicon | `surrealctl-<version>.darwin-arm64.tgz` |
| macOS, Intel | `surrealctl-<version>.darwin-amd64.tgz` |
| Linux, ARM64 | `surrealctl-<version>.linux-arm64.tgz` |
| Linux, x86-64 | `surrealctl-<version>.linux-amd64.tgz` |
| Windows, x86-64 | `surrealctl-<version>.windows-amd64.exe` |

The archives hold a single file, `surrealctl`, at the root. Windows ships the executable directly, with no archive.

### Verify a download

Every asset has a `.txt` file beside it holding its SHA-256, and every version directory has a `SHA256SUMS` covering all of them. The install script and `surrealctl upgrade` both check this automatically; verify by hand when you download the asset yourself.

```bash title="Check one asset"
version=$(curl -fsSL https://download.surrealdb.com/surrealctl/beta.txt)
asset="surrealctl-${version}.linux-amd64.tgz"
base="https://download.surrealdb.com/surrealctl/${version}"

curl -fsSLO "${base}/${asset}"
curl -fsSL "${base}/${asset}.txt"
sha256sum "${asset}"
```

The two digests must match. On macOS, use `shasum -a 256` instead of `sha256sum`.

```bash title="Check every asset you downloaded"
curl -fsSLO "${base}/SHA256SUMS"
shasum -c --ignore-missing SHA256SUMS
```

## Build from source

Building needs a Rust toolchain. The repository pins the version it wants, so `rustup` fetches the right one on the first build.

```bash
git clone https://github.com/surrealdb/surrealctl
cd surrealctl
cargo install --path .
```

`cargo install` places the binary in `~/.cargo/bin`, so make sure that directory is on your `PATH`.

## Confirm the installation

```bash
surrealctl version
```

The command prints the version, the commit it was built from, the build date, and the target triple. If the shell reports that the command was not found, the install directory is not on your `PATH`.

`surrealctl --help` lists the command groups. Every group prints its own help, so `surrealctl instance --help` and `surrealctl instance create --help` both work.

## Sign in for the first time

```bash
surrealctl auth login
```

The default flow opens a browser, waits for you to approve the sign-in, and stores the result. Three flows are available and `surrealctl` picks the first one that can work here:

1. **Browser loopback** — opens a browser and listens on `127.0.0.1` for the redirect. The default when a browser is available.
2. **Device code** — prints a short code to type on another device. Used automatically when there is no browser, or on request with `--flow device`.
3. **Paste** — prints a URL and takes back the address the browser was redirected to. Used with `--flow paste`, or when neither of the others can work.

Over SSH, `surrealctl` skips the browser flow without being asked, because a browser on the far end of an SSH connection opens on the wrong machine.

To force a particular flow, name it. A flow you ask for is used or it fails; `surrealctl` does not substitute another one silently.

```bash
surrealctl auth login --flow device
surrealctl auth login --no-browser
```

If the profile already has a credential, `auth login` reports who is signed in and does nothing. Pass `--force` to sign in again.

### Verify with whoami

```bash
surrealctl whoami
```

`whoami` asks the API who you are, so it confirms that the stored credential is accepted and that the network path works. It is the counterpart to `surrealctl auth status`, which reads the local store and touches no network at all.

```bash
surrealctl auth status
```

For what the two credential kinds are and how to use a token in CI, see [Authentication](/docs/manage/surrealctl/authentication.md).

## Check the whole setup

`surrealctl status` — also spelled `surrealctl doctor` — runs eight checks in dependency order and prints one row for each. No check can abort the command, so you get the whole diagnosis in one run.

| Check | What it tells you |
| --- | --- |
| `credential` | Which credential is in use, and whether it is usable. A failure here is fatal |
| `api` | Whether the API base URL answers |
| `version` | The minimum client version the API advertises. Reported, never enforced |
| `cloud session` | Whether a Cloud session is held. Skipped for a personal access token |
| `organization` | Which organisation resolves, and which layer decided it |
| `surreal binary` | Which copy the handoff commands will use |
| `system message` | Any platform notice currently published |
| `release` | Whether a newer `surrealctl` release is available |

Because a failure low in the table is often the one above it restated, read from the top: the first failing row is usually the cause and the rest are symptoms.

This is the one command that still prints its table when it exits non-zero, because the table is the diagnosis.

## Keep surrealctl up to date

`surrealctl` replaces itself.

```bash
surrealctl upgrade
```

The command follows the channel this build is on, verifies the download against its published checksum, runs the new binary once to prove it works, and only then replaces the running one. Nothing is overwritten until every one of those steps has passed.

| Option | Effect |
| --- | --- |
| `--check` | Report whether a newer release exists, and install nothing |
| `--version <VERSION>` | Install this exact version instead of the newest |
| `--channel <CHANNEL>` | Follow `latest`, `beta`, or `alpha` instead of this build's channel |
| `--force` | Replace the binary without confirming |
| `-o`, `--output <PATH>` | Write the new binary here instead of replacing the running one |

```bash title="Ask without installing"
surrealctl upgrade --check
```

> [!IMPORTANT]
> If the binary was installed by a package manager, `upgrade` refuses and names that tool's own command instead. Replacing a file a package manager owns leaves its records wrong, and the next upgrade through that tool would silently put the old version back. Use `brew upgrade`, `cargo install --force`, or your Nix workflow instead.

### Be told when a release lands

`surrealctl status` reports the release check as one of its rows. To be told automatically, at most once a day:

```bash
surrealctl config set release_check true
```

That prints a single line on stderr after a command finishes, and only when a terminal is attached — never in a pipe, a CI job, or `--json` output. Set `SURREALCTL_RELEASE_CHECK` to override the setting for one invocation.

## Shell completion

`surrealctl completion` writes a completion script to stdout for `bash`, `elvish`, `fish`, `powershell`, or `zsh`.

```bash title="zsh"
surrealctl completion zsh > "${fpath[1]}/_surrealctl"
```

```bash title="bash"
surrealctl completion bash > /etc/bash_completion.d/surrealctl
```

## The `surreal` handoff

`instance sql`, `instance import`, and `instance export` hand off to the `surreal` binary. Install it the [usual way](/docs/running/installation.md) and `surrealctl` finds it on your `PATH`.

If it is missing when you run one of those three commands, `surrealctl` offers to fetch a copy.

```text
`surrealctl instance sql` hands off to the `surreal` binary, and it is not installed.
? Download it from https://download.surrealdb.com now? (y/N)
```

The download lands in `~/.config/surrealctl/bin`, never in `/usr/local/bin`, so it needs no `sudo`. The offer appears only when somebody is there to answer it: an unattended run gets an error naming the install command instead, and `--yes` does not turn the offer into a silent download.

`surrealctl` looks for the binary in this order, and the managed copy is last.

1. The `surreal_binary` key in the active profile
2. `SURREALCTL_SURREAL_BINARY`
3. `PATH`
4. `~/.config/surrealctl/bin/surreal`

Installing `surreal` properly later therefore takes over with nothing to undo. To point at a specific copy, name it.

```bash
surrealctl config set surreal_binary /opt/surrealdb/bin/surreal
```

A path you name explicitly and that does not resolve is an error, not a fallback — silently using a different binary than the one you asked for is how the wrong version gets blamed.

## Where surrealctl keeps its files

Four paths, all in one directory under `$XDG_CONFIG_HOME/surrealctl` or `~/.config/surrealctl`. XDG applies on macOS too, because this is a terminal tool that people symlink into a dotfiles repository.

| File | Contents |
| --- | --- |
| `config.toml` | Profiles, the remembered organisation, and other non-secrets. Safe to commit |
| `credentials.json` | Tokens, and the values that must be replaced in the same write as them. Mode `0600` |
| `credentials.lock` | An empty advisory lock file, so two concurrent runs cannot corrupt a refresh |
| `bin/surreal` | The managed `surreal` copy, when one was downloaded |

`surrealctl config path` prints the configuration file path. Set `SURREALCTL_CONFIG_DIR` to move the whole directory, or `--config` to point at one configuration file.

> [!WARNING]
> `surrealctl` refuses to read `credentials.json` if it is readable by group or other, and tells you to run `chmod 600` on it. The file holds a refresh token.

## Next steps

- [Authentication](/docs/manage/surrealctl/authentication.md) — login sessions, personal access tokens, and CI credentials.
- [Instances](/docs/manage/surrealctl/instances.md) — your first real workflow.
- [`surrealctl` reference](/docs/reference/cli/surrealctl/overview.md) — every command and flag.
