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

# backend

> The backend sub-block inside ubx {} declares where Pulumi state is stored for every stack in the workspace.

The `backend {}` sub-block, declared inside `ubx {}`, controls where Pulumi stores stack state. When present, it overrides both the `PULUMI_BACKEND_URL` environment variable and the default local file backend for every `ubx ship` run in that workspace.

`backend` only affects CLI behavior — no Python code is generated from it.

## Syntax

```xcl theme={"theme":"css-variables"}
ubx {
    backend s3 {
        bucket = "my-infra-state"
        key    = "networking/prod"
    }
}
```

The backend type (`s3`, `gcs`, `azblob`, `local`, `pulumi`) is a bare identifier, not a quoted string. It follows the `backend` keyword and precedes the opening `{`.

Only one `backend {}` block is allowed inside a single `ubx {}` block. A duplicate produces a hard parse error.

## Backend types

### local

Stores state in a directory on the local filesystem.

```xcl theme={"theme":"css-variables"}
ubx {
    backend local {
        path = "/var/state/my-project"
    }
}
```

| Attribute | Required | Description                                                             |
| --------- | -------- | ----------------------------------------------------------------------- |
| `path`    | yes      | Absolute path, or a relative path resolved from the workspace directory |

Relative paths are resolved from the directory containing the `.xcl` files. The produced URL is `file://<path>`.

### s3

Stores state in an AWS S3 bucket.

```xcl theme={"theme":"css-variables"}
ubx {
    backend s3 {
        bucket = "my-infra-state"
        key    = "networking/prod"
    }
}
```

| Attribute | Required | Description                  |
| --------- | -------- | ---------------------------- |
| `bucket`  | yes      | S3 bucket name               |
| `key`     | yes      | Key prefix within the bucket |

Produced URL: `s3://<bucket>/<key>`

### gcs

Stores state in a Google Cloud Storage bucket.

```xcl theme={"theme":"css-variables"}
ubx {
    backend gcs {
        bucket = "my-state-bucket"
        prefix = "networking"   // optional
    }
}
```

| Attribute | Required | Description                  |
| --------- | -------- | ---------------------------- |
| `bucket`  | yes      | GCS bucket name              |
| `prefix`  | no       | Key prefix within the bucket |

Produced URL: `gs://<bucket>` or `gs://<bucket>/<prefix>` when prefix is set.

<Note>
  The URL scheme is `gs://` — not `gcs://`. This matches what Pulumi expects for the GCS backend.
</Note>

### azblob

Stores state in an Azure Blob Storage container.

```xcl theme={"theme":"css-variables"}
ubx {
    backend azblob {
        container = "my-state-container"
        path      = "infra/prod"   // optional
    }
}
```

| Attribute   | Required | Description                      |
| ----------- | -------- | -------------------------------- |
| `container` | yes      | Azure Blob container name        |
| `path`      | no       | Path prefix within the container |

Produced URL: `azblob://<container>` or `azblob://<container>/<path>` when path is set.

### pulumi

Uses Pulumi Cloud as the state backend.

```xcl theme={"theme":"css-variables"}
ubx {
    backend pulumi {
        org = "my-org"   // optional — omit to use the default account
    }
}
```

| Attribute | Required | Description              |
| --------- | -------- | ------------------------ |
| `org`     | no       | Pulumi organization name |

Produced URL: `https://api.pulumi.com` or `https://api.pulumi.com/api/<org>` when org is set.

## Encryption sub-block

The optional `encryption {}` sub-block configures a secrets provider for stack secrets. When present, ubx passes the corresponding `--secrets-provider` flag to Pulumi during stack operations.

```xcl theme={"theme":"css-variables"}
ubx {
    backend s3 {
        bucket = "my-infra-state"
        key    = "networking/prod"

        encryption {
            provider = "awskms"
            key      = "alias/pulumi-secrets"
            region   = "us-east-1"
        }
    }
}
```

Only one `encryption {}` block is allowed inside `backend {}`. A duplicate produces a hard parse error. Unknown attribute names produce a hard parse error.

### Encryption fields

| Attribute  | Required for                        | Optional for | Description                                      |
| ---------- | ----------------------------------- | ------------ | ------------------------------------------------ |
| `provider` | all                                 | —            | Encryption provider name (see below)             |
| `key`      | `awskms`, `azurekeyvault`, `gcpkms` | —            | KMS key identifier or URI                        |
| `region`   | —                                   | `awskms`     | AWS region override for the KMS key              |
| `env`      | —                                   | `passphrase` | Environment variable name holding the passphrase |

### awskms

```xcl theme={"theme":"css-variables"}
encryption {
    provider = "awskms"
    key      = "alias/pulumi-secrets"
    region   = "us-east-1"   // optional
}
```

`key` is required. It may be a key alias (`alias/name`), a key ARN, or any form accepted by the AWS KMS API. `region` is optional — when omitted, the ambient AWS region is used.

Produces `--secrets-provider awskms://alias/pulumi-secrets` or `awskms://alias/pulumi-secrets?region=us-east-1` when region is set.

### azurekeyvault

```xcl theme={"theme":"css-variables"}
encryption {
    provider = "azurekeyvault"
    key      = "https://myvault.vault.azure.net/keys/mykey"
}
```

`key` is required. It is the full Azure Key Vault key URI. The leading `https://` is stripped when forming the secrets provider string.

Produces `--secrets-provider azurekeyvault://myvault.vault.azure.net/keys/mykey`.

### gcpkms

```xcl theme={"theme":"css-variables"}
encryption {
    provider = "gcpkms"
    key      = "projects/my-project/locations/global/keyRings/my-ring/cryptoKeys/my-key"
}
```

`key` is required. It is the full GCP KMS resource path.

Produces `--secrets-provider gcpkms://projects/my-project/locations/global/keyRings/my-ring/cryptoKeys/my-key`.

### passphrase

```xcl theme={"theme":"css-variables"}
encryption {
    provider = "passphrase"
    env      = "MY_PULUMI_PASSPHRASE"   // optional
}
```

No `key` is required. `env` names the environment variable that holds the passphrase. When `env` is omitted, Pulumi looks for `PULUMI_CONFIG_PASSPHRASE` by default.

Produces `--secrets-provider passphrase`.

### Cross-provider warnings

The typechecker emits warnings for attributes that do not apply to the chosen provider:

| Situation                                     | Warning                                                |
| --------------------------------------------- | ------------------------------------------------------ |
| `env` set with a KMS provider                 | `'env' is only meaningful for provider = "passphrase"` |
| `region` set with `azurekeyvault` or `gcpkms` | `'region' is only meaningful for provider = "awskms"`  |
| `key` set with `passphrase`                   | `'key' is not used for provider = "passphrase"`        |

These are warnings — the stack still compiles and the extraneous attributes are silently ignored at runtime.

## Priority order

When `ubx ship` resolves the backend URL, it uses the following priority:

1. **`backend {}` in `ubx {}`** — explicit declaration in source (highest priority)
2. **`PULUMI_BACKEND_URL` environment variable** — runtime override
3. **Default** — `file://<workspace-dir>/.ubx/state/` (lowest priority)

```xcl theme={"theme":"css-variables"}
// This declaration wins over any PULUMI_BACKEND_URL in the environment.
ubx {
    backend s3 {
        bucket = "my-infra-state"
        key    = "networking/prod"
    }
}
```

## Secrets provider wiring

When an `encryption {}` block is present, `ubx ship` extracts the secrets provider string and passes it to the Pulumi plugin. The plugin uses it when initializing a new stack (`pulumi stack init --secrets-provider <value>`).

For an already-initialized stack, the secrets provider is stored in the stack's state file. Changing the provider declaration after a stack is initialized requires a migration:

```sh theme={"theme":"css-variables"}
pulumi stack change-secrets-provider "awskms://alias/new-key?region=us-east-1"
```

Run this command from the generated `.ubx/` directory, or with the Pulumi stack set to the correct stack name.

## Secrets provider drift

If the `encryption {}` block in source declares a different provider than what the existing stack was initialized with, `ubx ship` emits a warning:

```
warning: secrets provider in source (awskms://...) differs from stack's current provider
run: pulumi stack change-secrets-provider "awskms://..." to migrate
```

Full drift detection compares the declared provider against the current stack state. The migration command is `pulumi stack change-secrets-provider "<new-provider-string>"`.

<Note>
  Drift detection is a best-effort warning, not a hard error. The deploy proceeds regardless. If the providers genuinely differ at apply time, Pulumi will fail with a decryption error.
</Note>

## No codegen emitted

`backend {}` affects only `ubx ship` behavior. Nothing in `__main__.py` changes based on the backend declaration — no import, no variable, no comment is emitted. Pulumi receives the backend URL and secrets provider string through the plugin RPC, not through the generated Python program.

## All attributes must be string literals

All attributes inside `backend {}` (and `encryption {}`) must be string literals. Expressions, input references, and locals references are not allowed:

```xcl theme={"theme":"css-variables"}
// Valid
backend s3 {
    bucket = "my-bucket"
}

// Error — expressions not allowed in backend attributes
backend s3 {
    bucket = input.cfg.bucket_name  // ✗ error: must be a string literal
}
```

This restriction exists because backend configuration must be resolved at compile time, before any stack inputs are available.

## Error conditions

### Unknown backend type

```
unknown backend type "firebase" — valid types: local, s3, gcs, azblob, pulumi
```

### Non-string-literal attribute

```
backend "s3": attribute "bucket" must be a string literal — expressions and variable references are not allowed
```

### Missing encryption provider

```
encryption block requires a 'provider' attribute — valid: awskms, azurekeyvault, gcpkms, passphrase
```

### Unknown encryption provider

```
unknown encryption provider "hashivault" — valid: awskms, azurekeyvault, gcpkms, passphrase
```

### Missing key for KMS provider

```
encryption provider "awskms" requires a 'key' attribute
```

### Duplicate backend block (parse error)

```
duplicate backend block — only one backend {} is allowed inside ubx {}
```

### Duplicate encryption block (parse error)

```
duplicate encryption block — only one encryption {} is allowed inside backend {}
```
