> For the complete documentation index, see [llms.txt](https://docs.devolutions.net/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.devolutions.net/cli/for-devops-and-administrators/automate/ci-sign-in.md).

# Sign in from a CI pipeline

Sign in to Devolutions Cloud or Devolutions Server from a CI pipeline with Devolutions CLI, using a key read from an environment variable or a file.

A pipeline can't open a browser or type a password. Devolutions CLI signs in with a key instead, and reads the secret part of that key from an environment variable or a file that your CI system protects. The secret is never written on the command line.

## Pick the right sign-in method

<table><thead><tr><th width="230">WORKSPACE</th><th>METHOD</th><th>OPTIONS</th></tr></thead><tbody><tr><td><strong>Devolutions Cloud</strong></td><td>Application key and application secret</td><td><code>--application-key</code> with <code>--application-secret-env</code> or <code>--application-secret-file</code></td></tr><tr><td><strong>Devolutions Server</strong></td><td>API key</td><td><code>--api-key-env</code> or <code>--api-key-file</code></td></tr><tr><td><strong>Devolutions Server</strong></td><td>Application user</td><td><code>--as-application</code> with <code>--username</code> and <code>--password-env</code> or <code>--password-file</code></td></tr></tbody></table>

Create the application key, API key, or application user in your workspace first, and give it access only to the vaults the pipeline needs. Then store its secret in your CI system's protected variables or secret files.

{% hint style="warning" %}
Never pass a secret as an argument. Options such as `--application-secret-env`, `--api-key-env`, and `--password-env` take the **name** of an environment variable, not its value. `devo login` has no option that accepts the secret itself.
{% endhint %}

## Sign in

{% tabs %}
{% tab title="Devolutions Cloud" %}

```bash
devo login ci-cloud --cloud https://<your-organization>.devolutions.app \
  --application-key <application-key> \
  --application-secret-env DEVO_APP_SECRET
```

`DEVO_APP_SECRET` is the name of the variable that holds the application secret. To read it from a file instead, use `--application-secret-file <path>`.
{% endtab %}

{% tab title="Devolutions Server (API key)" %}

```bash
devo login ci-server --server https://<your-server-address> \
  --api-key-env DEVO_API_KEY
```

To read the API key from a file instead, use `--api-key-file <path>`.
{% endtab %}

{% tab title="Devolutions Server (application user)" %}

```bash
devo login ci-server --server https://<your-server-address> \
  --as-application --username <application-user> \
  --password-env DEVO_APP_PASSWORD
```

To read the secret from a file instead, use `--password-file <path>`.
{% endtab %}
{% endtabs %}

The context you sign in to becomes the current one, so the next commands use it. Add `--json` to get a result you can check in a script. See [Read results as JSON](/cli/for-devops-and-administrators/automate/json-output.md).

## Keep the secret available for the whole job

Devolutions CLI doesn't copy the secret into its configuration. It saves **where** the secret is, the variable name or the file path, and reads it again every time it connects to your workspace, including from its background process. So:

* Set the variable, or create the file, **before** the first `devo` command of the job.
* Keep it set, or keep the file in place, until the last `devo` command.
* If you rotate a secret kept in a file, replace the file's content: the next connection reads the new value, without a new sign-in.

If the variable or the file is missing later, commands fail with `secretReferenceUnavailable`.

## Example: a generic CI job

This job signs in to Devolutions Cloud, runs a deployment script with a database password from the vault, and cleans up. Adapt the variable names to your CI system: most of them expose protected variables as environment variables.

```bash
#!/usr/bin/env bash
set -euo pipefail

# Keep this job's contexts separate from other jobs on the same runner.
export DEVO_CLI_CONFIG_HOME="$(mktemp -d)"
# Fail instead of waiting for an answer.
export DEVO_NON_INTERACTIVE=1

# DEVO_APP_SECRET is provided by the CI system as a protected variable.
devo login ci-cloud --cloud "https://<your-organization>.devolutions.app" \
  --application-key "<application-key>" \
  --application-secret-env DEVO_APP_SECRET \
  --json > /dev/null

devo secret run \
  --env-ref DB_PASSWORD="<reference>" \
  --timeout-seconds 900 \
  -- ./deploy.sh

devo logout ci-cloud --json > /dev/null
devo daemon stop --wait
rm -rf "$DEVO_CLI_CONFIG_HOME"
```

To run the cleanup even when a step fails, put the last three commands in your CI system's "always run" step or in a shell `trap`. More about the configuration folder and the background process is in [Install Devolutions CLI on build agents](/cli/for-devops-and-administrators/automate/install-on-runners.md).

## Sign in on a computer without a browser

On a server or a remote session where *you* are present but no browser can open, use the device code flow with `--oauth --use-device-code`. Devolutions CLI prints a web address and a code; open the address on any device and sign in. Setting the environment variable `DEVO_CLI_USE_DEVICE_CODE` to `1` has the same effect as `--use-device-code`. Either way, it applies only to a sign-in with `--oauth`.

```bash
devo login my-server --server https://<your-server-address> --oauth --use-device-code
```

This flow needs a person to complete it, so it doesn't fit an unattended pipeline.

{% hint style="warning" %}
On Devolutions Cloud, a device code sign-in can't read your data: it only allows `devo status` and `devo logout`. For data commands on Devolutions Cloud, sign in with the browser (`--oauth` without `--use-device-code`) or with an application key.
{% endhint %}

## If it doesn't work

* The error says the variable or the file can't be found: check the name you passed (the variable **name**, without `$`), and that the CI system exposes it to this step.
* Sign-in fails or the session expires: see [Sign-in fails or the session expired](/cli/help/troubleshooting/sign-in-fails.md).
* All sign-in methods and options: [Sign in to Devolutions Cloud](/cli/guides/connect/devolutions-cloud.md), [Sign in to Devolutions Server](/cli/guides/connect/devolutions-server.md).

## Next

* [Install Devolutions CLI on build agents](/cli/for-devops-and-administrators/automate/install-on-runners.md)
* [Run without prompts](/cli/for-devops-and-administrators/automate/non-interactive.md)
* [Run a command with its secrets](/cli/guides/secrets/run-with-secrets.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.devolutions.net/cli/for-devops-and-administrators/automate/ci-sign-in.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
