# Server and VPS setup

Install the Companion on a dedicated Linux server account and keep it running, or give agents a Docker environment.

Last updated 6 November 2026

Install the Companion under a dedicated account on a Linux server and pair it as a device in Cabane. On a server, agents can keep working while your own computer sleeps. They work as that account, with its files, tools and signed-in accounts, without asking before each action.

To give them less, use a harness sandbox or a Docker environment, described in [Give agents their own environment](#give-agents-their-own-environment). For a Windows server, follow [Windows setup](https://cabane.ai/docs/windows-setup).

## Before you start

Use a **dedicated account that is not root**, with a **systemd user service** and administrator permission to enable linger. Keep that account's files and credentials separate from your personal ones. Have your Cabane workspace open and the account or provider credentials you plan to use with Claude Code, Codex or OpenCode.

Run every command on this page in that account's shell, except where a step opens a second terminal.

## 1. Pair it

If you do not have a Cabane workspace yet, [create it first](https://cabane.ai/docs/quick-start#2-create-the-workspace). In Cabane, choose the gear at the bottom of the sidebar to open the workspace's **Settings**, then choose **Devices → Set up a new device**. The same dialog is available from **Connect AI** during workspace setup, and **Your settings → Devices → Add a device** pairs a device without adding it to a workspace.

Copy the line under **With the installer** and run it in the **dedicated account's shell**:

```sh
curl -fsSL https://cabane.ai/install.sh | bash
```

In a Windows browser, that line comes second, under **In WSL · With the installer**: run it in the dedicated account's shell all the same, not on your Windows computer.

The installer supplies Node and the Companion under `~/.cabane`, then runs `cabane setup`, which prints a code and a link:

```
◇  Pair this device
│  Enter this code in Cabane, or press Return to open the page:
│
│    WXYZ-2345
│    https://app.cabane.ai/pair/enter-code
│
◒  Waiting for you to confirm in Cabane… (the code is good for 15 minutes)
```

In the dialog you opened in Cabane, type the code into **Code from the terminal**. Cabane shows which device asked: its name, platform and how long ago it asked. Choose **Confirm**. A few seconds later the terminal says the device is paired: it's paired to your account, and the workspace's dialog adds it to that workspace. A code works once and expires after 15 minutes; the terminal prints a fresh one when it does. Follow any PATH instruction the installer prints. The page the terminal's link opens also takes the code, but pairs the device to your account only; a device paired there is added to a workspace afterwards with **Add a device** in that workspace's **Settings → Devices**.

On a first interactive setup, if none of Claude Code, Codex or OpenCode is usable, setup asks **“Cabane needs one of them. Which will you set up?”** Choose the one you intend to use. Setup prints installation and sign-in lines for that choice; the complete commands are in the next step. **Needs sign-in** is expected until you sign in.

## 2. Sign in without a browser

For Claude Code and Codex, open the printed URL in your personal device's browser and return any code requested by the terminal. Sign in under the dedicated account even if you already use the same provider elsewhere.

**Claude Code:**

```sh
curl -fsSL https://claude.ai/install.sh | bash
export PATH="$HOME/.local/bin:$PATH"
claude auth login
```

**Codex:** use the Node installation supplied by the Companion.

```sh
export PATH="$HOME/.cabane/node/bin:$PATH"
npm i -g @openai/codex
codex login --device-auth
```

Use `--device-auth` even if setup prints plain `codex login`. Device authentication must be enabled for your ChatGPT account or workspace.

**OpenCode:**

```sh
curl -fsSL https://opencode.ai/install | bash
```

Open a **separate terminal logged in to that same account** so it picks up the PATH change. In that new shell, run:

```sh
opencode auth login
```

Choose your provider and follow its credential instructions. Setup's printed lines don't include this sign-in. Then start its server in this **separate terminal**:

```sh
opencode serve --port 4096
```

Keep the terminal open while using OpenCode. The Companion's service does not supervise this server.

In the **dedicated account's original shell**, check that your chosen harness shows **connected**:

```sh
cabane status
```

For other authentication options, see [signing in without a browser](https://cabane.ai/docs/faq-and-help#can-i-sign-in-on-a-machine-with-no-browser) and [when to use a token](https://cabane.ai/docs/faq-and-help#when-should-i-use-a-token-instead-of-signing-in).

## 3. Keep it running and update

Install the Companion's systemd user service. Service installation prints the linger reminder; enabling linger lets it run after logout and reboot. Run in the **dedicated account's shell**:

```sh
cabane service install
sudo loginctl enable-linger "$USER"
cabane service status
```

The service enables automatic updates, which can restart the Companion during a turn. After a reboot, check `cabane service status` in that account's shell and run `cabane start` if the Companion is stopped. For OpenCode, also open a separate terminal logged in to the same account and run `opencode serve --port 4096` again. Keep that terminal open while using OpenCode.

If you run the Companion without its service, use `cabane update` in the dedicated account's shell when no agent is working.

## Stop or disconnect

In the **dedicated account's shell**, `cabane stop` stops the Companion and any running turns, `cabane start` starts it again, and `cabane service uninstall` disables automatic startup. Stop a separate OpenCode server with **Ctrl+C** in its terminal.

`cabane logout` clears pairing credentials while keeping your configuration and the device record in Cabane. For deactivation, token rotation, removal or pairing a removed device again, see [Devices](https://cabane.ai/docs/devices#deactivate-rotate-the-token-remove).

## Give agents their own environment

Recommended for agents reading untrusted input, such as public websites or inbound mail, and for a server a team shares. [Agent access](https://cabane.ai/docs/agent-access) compares the levels.

- **A harness sandbox.** Install directly as above and follow [Harness sandboxes](https://cabane.ai/docs/harness-sandboxes) to configure the restrictions you want.
- **A Docker environment.** Agents work inside a container with its own tools and sign-ins, and see only the work folder you share. Follow the recipe below instead of steps 1 to 3.

## Docker environment

The example below keeps credentials across container replacement and shares one work folder from the host. It works on a server or with Docker Desktop. Keep the host and Docker running while agents work.

Figure: The Companion and the chosen sign-ins live in a Docker environment, with persistent storage and a work folder shared from the server

Install Docker with Docker Compose, and use an account other than root that can run Docker. For **host shell** commands, use your server's shell. With Docker Desktop, use **macOS Terminal** or a **WSL shell**. Run these commands from the `cabane-device` folder you create below, except where the recipe explicitly takes you inside a container.

### Prepare the Docker environment

In the **host shell**, create a folder for the device, a work folder and a `.env` file recording your account's user ID:

```sh
mkdir cabane-device
cd cabane-device
mkdir work
printf 'LOCAL_UID=%s\n' "$(id -u)" > .env
```

Create the following three files in `cabane-device`.

**`Dockerfile`** installs the Companion, Claude Code, Codex and OpenCode. It runs them as a non-root user whose ID matches your host account, so they can write to the shared work folder.

```dockerfile
FROM node:22-bookworm-slim

RUN apt-get update \
    && apt-get install -y --no-install-recommends ca-certificates curl git nano \
    && rm -rf /var/lib/apt/lists/*
RUN npm install -g @cabane/companion @anthropic-ai/claude-code @openai/codex opencode-ai

ARG LOCAL_UID=1000
RUN test "${LOCAL_UID}" -gt 0 && usermod -u "${LOCAL_UID}" node && chown -R node:node /home/node
USER node
WORKDIR /work
RUN cabane --version && claude --version && codex --version && opencode --version
```

**`compose.yaml`** keeps the whole container home in a named volume and mounts the host's `work` folder at `/work`. Changes in `/work` change the host files.

```yaml
x-device: &device
  build:
    context: .
    args:
      LOCAL_UID: ${LOCAL_UID:-1000}
  init: true
  restart: unless-stopped
  volumes:
    - companion-home:/home/node
    - ./work:/work

services:
  companion:
    <<: *device
    hostname: cabane-docker
    environment:
      CABANE_COMPANION_SUPERVISED: "1"
    command: ["cabane", "start", "--foreground"]

  opencode:
    <<: *device
    profiles: ["opencode"]
    network_mode: "service:companion"
    command: ["opencode", "serve", "--hostname", "127.0.0.1", "--port", "4096"]

volumes:
  companion-home:
```

**`.dockerignore`** limits the build context to the Dockerfile, keeping your work and credentials out of the image build:

```text
*
!Dockerfile
```

If you plan to use **OpenCode**, append this setting to `.env` from the **host shell**:

```sh
printf 'COMPOSE_PROFILES=opencode\n' >> .env
```

The saved setting includes OpenCode in later Compose commands. Its server shares the Companion's network and listens on `127.0.0.1:4096` inside it. This recipe publishes no ports and mounts no Docker socket.

Build the image from the **host shell**:

```sh
docker compose build
```

You build and maintain this image locally. To use specific package versions, add `@VERSION` to their names in the Dockerfile. Add any other tools agents need there too, so rebuilding restores them.

### Pair the Docker environment

Open the workspace's **Settings → Devices → Set up a new device**, as in [step 1](#1-pair-it). If the line under **With npm · @cabane/companion** carries `--server` and an address, your Cabane isn't the hosted one: note that address for the command below.

In the **host shell**, from `cabane-device`, open an interactive shell in a temporary container:

```sh
docker compose run --rm --no-deps companion sh
```

Now **inside the container**, run setup, adding `--server ADDRESS` only for a Cabane that isn't the hosted one:

```sh
cabane setup
```

Setup prints a code and waits. Enter the code in the dialog and choose **Confirm**; the container's terminal reports the pairing a few seconds later. If setup asks **“Cabane needs one of them. Which will you set up?”**, choose Claude Code, Codex or OpenCode. The image already includes all three tools: skip the installation, sign-in and server commands setup prints. Continue below to start the persistent services, then sign in from the **host shell** as described in the next section. If you chose OpenCode, its Compose service runs the server. Pairing itself does not sign you in.

After pairing, expect to find this device as **cabane-docker** in Cabane. See [Devices](https://cabane.ai/docs/devices) to rename it.

Setup starts the Companion in the temporary container. When it returns to the prompt, run these commands **inside the container** to turn off automatic updates, stop that Companion and leave the shell:

```sh
cabane update --auto off
cabane stop
exit
```

Back in the **host shell**, start the services:

```sh
docker compose up -d
```

The persistent home keeps the pairing. Later starts reuse it without another setup code. With automatic updates off, update the Companion by rebuilding the image as described in [Keep the container running and update](#keep-the-container-running-and-update).

### Sign in inside the container

For a direct installation on the server account, use [Sign in without a browser](#2-sign-in-without-a-browser). The commands below run inside Docker.

Sign in inside this environment even if you use the same account on your personal device. For Claude Code and Codex, open the printed URL yourself in your personal device's browser, complete sign-in there, and return any code requested by the terminal.

The Dockerfile has already installed the sign-in commands. Run the one you need in the **host shell**, from `cabane-device`.

**Claude Code:**

```sh
docker compose exec companion claude auth login
```

**Codex:**

```sh
docker compose exec companion codex login --device-auth
```

Use `--device-auth` even if setup prints plain `codex login`. Device authentication must be enabled for your ChatGPT account or workspace.

**OpenCode:**

```sh
docker compose exec companion opencode auth login
```

Choose your provider and follow its credential instructions. The OpenCode service uses the same home as the Companion, so it can use the credentials saved by this command. Make sure you enabled its profile in [Prepare the Docker environment](#prepare-the-docker-environment).

After signing in, check the device from the **host shell**:

```sh
docker compose exec companion cabane status
```

Check that the harness you want the agent to use shows **connected**. You will choose that harness for the agent when assigning them to this device. The persistent home keeps your sign-ins across container replacement.

### Keep the container running and update

The Compose file restarts the services unless you explicitly stop them. Keep Docker running and the host awake. After a host restart, start Docker and run these commands in the **host shell**, from `cabane-device`:

```sh
docker compose up -d
docker compose exec companion cabane status
```

To update the installed packages, wait until no agent is working. Then rebuild and replace the containers from the same **host shell and folder**:

```sh
docker compose build --pull --no-cache
docker compose up -d --force-recreate
docker compose exec companion cabane status
```

Rebuilding fetches the package versions specified by your Dockerfile, or the latest published versions where you have not pinned them. Replacing containers interrupts active work; the home volume and shared work folder survive.

### Stop, pause or remove the container

Use the **host shell** in `cabane-device`:

| Command | Result |
| --- | --- |
| `docker compose stop` | Stops the services while keeping their containers and storage. |
| `docker compose up -d` | Starts the services again. |
| `docker compose down` | Removes the containers and keeps the home volume and host work folder. |

Adding `--volumes` to `docker compose down` deletes the home volume, including pairing credentials and sign-ins. Keep the volume when updating, rotating a token or pairing a removed device again.

For deactivation or removal in Cabane, see [Devices](https://cabane.ai/docs/devices#deactivate-rotate-the-token-remove). The next two sections cover recovery inside this Docker environment.

### Rotate a Docker device’s token

When no agent is working, follow [Devices](https://cabane.ai/docs/devices#deactivate-rotate-the-token-remove) to rotate the token in Cabane. Cabane reveals the replacement once.

In the **host shell**, from `cabane-device`, stop the services and open the saved configuration in an editor:

```sh
docker compose stop
docker compose run --rm --no-deps companion nano /home/node/.cabane/config.json
```

In the editor, replace only the value of `deviceToken`, keeping its quotation marks and the rest of the file. Paste the token into this file, never into a shell command or `.env`. Save with **Ctrl+O**, press **Enter**, then exit with **Ctrl+X**.

Back in the **host shell**, restart and check the device:

```sh
docker compose up -d
docker compose exec companion cabane status
```

### Pair a removed Docker device again

If you removed the device from your account in Cabane, clear its old pairing before pairing a new device. Keep the home volume to retain your configuration and provider sign-ins.

Open **Set up a new device** in Cabane, as in [Pair the Docker environment](#pair-the-docker-environment). Then run in the **host shell**, from `cabane-device`:

```sh
docker compose stop
docker compose run --rm --no-deps companion sh
```

Now **inside the container**, run:

```sh
cabane logout
```

Confirm the logout when asked. It clears the old Cabane pairing while keeping your configuration and provider sign-ins. Then, still **inside the container**, run setup again, with `--server ADDRESS` only for a Cabane that isn't the hosted one, and enter the code it prints in the dialog:

```sh
cabane setup
```

When setup returns to the prompt, run **inside the container**:

```sh
cabane update --auto off
cabane stop
exit
```

Back in the **host shell**, restart and check the new device:

```sh
docker compose up -d
docker compose exec companion cabane status
```

### Assign agents to the container

When you assign an agent to this device in [Agent execution settings](https://cabane.ai/docs/agent-execution-settings), set **What they can reach → Starts in** to `/work`. This makes the host's shared `work` folder their starting folder inside the container.

## A fresh environment every session

To create a fresh environment for each session, use the Companion's [runner.command interface](https://www.npmjs.com/package/@cabane/companion#running-each-turn-somewhere-else) with your own wrapper. The wrapper must pass through the command, standard input and environment, and handle cleanup.

Choose what to retain between sessions. Mounted work and explicitly retained credentials can outlive the environment. The Docker recipe on this page keeps a persistent home across sessions.

## Back to Quick start

Return to [Quick start: check the device](https://cabane.ai/docs/quick-start#5-check-the-device). Follow [Agent execution settings](https://cabane.ai/docs/agent-execution-settings) to assign your agent to the device you paired here and choose a harness that shows **connected** on that device.
