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

# Setting Up Capsule with Ansible

> Provision a server with the Capsule CLI end-to-end using Ansible — no interactive prompts required.

## Overview

Every step of Capsule setup that's normally interactive in the TUI has a non-interactive CLI equivalent, so a fresh server can be fully provisioned from a playbook: install the binary, authenticate, add database connections, schedule backups, and install the systemd service.

Secrets (your license key, database passwords, MongoDB URIs) are always passed as **environment variables**, never as plain command-line flags — so they never land in shell history, `ps` output, or Ansible's own command logging.

***

## Full example playbook

```yaml theme={null}
# roles/capsule/tasks/main.yml
- name: Install Capsule CLI
  ansible.builtin.shell: curl -fsSL https://trycapsule.xyz/install.sh | sudo sh
  args:
    creates: /usr/local/bin/capsule

- name: Authenticate with license key
  ansible.builtin.command: capsule auth
  environment:
    CAPSULE_LICENSE_KEY: "{{ vault_capsule_license_key }}"

- name: Add the production Postgres connection
  ansible.builtin.command: >
    capsule connection add
    --label prod-postgres
    --type postgres
    --host {{ postgres_host }}
    --port 5432
    --database myapp
    --user {{ postgres_user }}
  environment:
    CAPSULE_CONN_PASSWORD: "{{ vault_postgres_password }}"
  register: conn_add
  changed_when: "'Connection added' in conn_add.stdout"
  failed_when: conn_add.rc != 0 and 'already exists' not in conn_add.stderr

- name: Schedule nightly backups at 2am
  ansible.builtin.command: capsule schedule add --connection prod-postgres --cron "0 2 * * *"

- name: Install and start the systemd service
  ansible.builtin.command: capsule install-service
  args:
    chdir: /opt/capsule
  become: true
```

***

## Step by step

### 1. Install the binary

```yaml theme={null}
- name: Install Capsule CLI
  ansible.builtin.shell: curl -fsSL https://trycapsule.xyz/install.sh | sudo sh
  args:
    creates: /usr/local/bin/capsule
```

`creates: /usr/local/bin/capsule` makes this idempotent — Ansible skips the install script entirely on subsequent runs once the binary exists.

### 2. Authenticate

```yaml theme={null}
- name: Authenticate with license key
  ansible.builtin.command: capsule auth
  environment:
    CAPSULE_LICENSE_KEY: "{{ vault_capsule_license_key }}"
```

Normally `capsule auth` prompts interactively for your license key. It also checks `CAPSULE_LICENSE_KEY` in the environment first — if set, it skips the prompt entirely and saves that key. Keep the key in Ansible Vault, never in plain YAML.

### 3. Add connections

```yaml theme={null}
- name: Add the production Postgres connection
  ansible.builtin.command: >
    capsule connection add
    --label prod-postgres
    --type postgres
    --host {{ postgres_host }}
    --port 5432
    --database myapp
    --user {{ postgres_user }}
  environment:
    CAPSULE_CONN_PASSWORD: "{{ vault_postgres_password }}"
  register: conn_add
  changed_when: "'Connection added' in conn_add.stdout"
  failed_when: conn_add.rc != 0 and 'already exists' not in conn_add.stderr
```

`capsule connection add` normally prompts for the password with input hidden. Setting `CAPSULE_CONN_PASSWORD` in the task's `environment` skips that prompt. For MongoDB, use `--type mongo` and set `CAPSULE_CONN_URI` instead (see [capsule connection](/cli/connection) for the full flag reference).

Re-running `add` for a label that already exists fails with `a connection named "..." already exists` — the `failed_when` above treats that as a no-op so the playbook stays idempotent instead of erroring on every re-run.

<Warning>
  If your plan's connection limit is already reached, `connection add` fails immediately with an upgrade message before prompting for anything. Check `capsule connection list` if a play fails unexpectedly.
</Warning>

### 4. Schedule backups

```yaml theme={null}
- name: Schedule nightly backups at 2am
  ansible.builtin.command: capsule schedule add --connection prod-postgres --cron "0 2 * * *"
```

`schedule add` upserts by connection label — re-running it with the same cron expression is naturally idempotent, no special handling needed. See [capsule schedule](/cli/schedule) for pausing/resuming and other subcommands.

### 5. Install the service

```yaml theme={null}
- name: Install and start the systemd service
  ansible.builtin.command: capsule install-service
  args:
    chdir: /opt/capsule
  become: true
```

`install-service` must run as root (`become: true`) and creates a systemd unit that starts on boot and restarts on failure. `chdir` sets the daemon's working directory — pick wherever you want its runtime state to live. See [capsule service](/cli/service) for details.

***

## Backing up a dockerized database

If the target database runs in a container on the same host, add `--docker <container_name>` to the connection:

```yaml theme={null}
- name: Add a connection to a dockerized Postgres
  ansible.builtin.command: >
    capsule connection add
    --label prod-postgres-docker
    --type postgres
    --host localhost
    --port 5432
    --database myapp
    --user postgres
    --docker myapp_postgres
  environment:
    CAPSULE_CONN_PASSWORD: "{{ vault_postgres_password }}"
```

This makes every backup run as `docker exec -i myapp_postgres pg_dump ...` instead of natively — you don't need the Postgres client tools installed on the host, just Docker itself. Run `capsule doctor` afterward to confirm everything required is reachable.

***

## Updating a schedule

`schedule add` upserts by connection label, so changing a cron expression later is just re-running the same task with a new value — no separate "update" step needed:

```yaml theme={null}
- name: Change the backup schedule to every 6 hours
  ansible.builtin.command: capsule schedule add --connection prod-postgres --cron "0 */6 * * *"
```

This is naturally idempotent and safe to leave in the playbook permanently — if the cron expression in your `vars` file changes, re-running the play applies it; if it's unchanged, the daemon just re-reads the same value.

<Warning>
  Don't use `capsule schedule remove` + `add` to change a cron expression — `remove` permanently deletes the schedule (you'd lose its last-run history), and it's unnecessary churn. `add` alone already updates it in place.
</Warning>

***

## Rotating database credentials

Unlike connections, `capsule connection add` refuses to touch an existing label (`a connection named "..." already exists`) — it's meant for first-time creation, not updates. Use `capsule connection update` instead, which changes fields **in place** on the same connection ID, so its attached schedule is left untouched:

```yaml theme={null}
- name: Rotate the Postgres password
  ansible.builtin.command: capsule connection update --connection prod-postgres
  environment:
    CAPSULE_CONN_PASSWORD: "{{ vault_postgres_password }}"
  no_log: true
```

Set `no_log: true` on this task — otherwise Ansible may echo the task's resolved environment into its own output/logs on failure, which would leak the new password into your CI logs.

This also works for a host change, without touching the password:

```yaml theme={null}
- name: Point the connection at the new replica host
  ansible.builtin.command: capsule connection update --connection prod-postgres --host db-replica.example.com
```

<Warning>
  Don't use `capsule connection remove` + `add` to rotate credentials — `remove` also deletes the connection's schedule (see [capsule connection remove](/cli/connection#capsule-connection-remove)), so you'd silently lose your backup cadence along with it. `update` is the safe, idempotent path for changing anything about a connection you've already created.
</Warning>

For MongoDB, rotate the whole URI the same way:

```yaml theme={null}
- name: Rotate the MongoDB connection URI
  ansible.builtin.command: capsule connection update --connection prod-mongo
  environment:
    CAPSULE_CONN_URI: "{{ vault_mongo_uri }}"
  no_log: true
```

***

## Verifying the setup

```yaml theme={null}
- name: Verify required database tools are installed
  ansible.builtin.command: capsule doctor
  register: doctor_result
  changed_when: false

- name: Confirm the schedule was created
  ansible.builtin.command: capsule schedule list
  register: schedule_list
  changed_when: false
```

<Tip>
  Run the whole role against a staging host first and check `capsule connection list` / `capsule schedule list` before rolling it out to production servers.
</Tip>

## See also

* [`capsule auth`](/cli/auth) — license key authentication details
* [`capsule connection`](/cli/connection) — full flag reference, including MongoDB URIs
* [`capsule schedule`](/cli/schedule) — cron expressions, pause/resume, live reload
* [`capsule service`](/cli/service) — systemd service management
