# env

Pull, compare, and push environment variables between a local dotenv file and one scope of the linked application's variables.

URL: https://coolship.itrocas.com/docs/commands/env

```text
coolship env pull [flags]
coolship env diff [flags]
coolship env push [flags]
```

Coolify keeps two scopes per application: the variables regular deployments see, and a separate copy used by preview deployments. The `env` commands act on the regular scope unless `--preview` is given. Values are masked in output unless `--show-values` is given. Shared references such as `{{team.NAME}}` are synced as references, never as the values they resolve to. [Environment variables](https://coolship.itrocas.com/docs/concepts/variables) explains the rules; the [workflow guide](https://coolship.itrocas.com/docs/guides/environment-variables) shows them in sequence.

## Shared flags

| Flag          | Purpose                                                         |
| ------------- | --------------------------------------------------------------- |
| `--file PATH` | Dotenv file, relative to the application root (default `.env`). |
| `--preview`   | Act on the preview-deployment scope instead of the regular one. |

## env pull

Write the remote variables into the local file, creating it with private permissions. Keys that exist only locally are kept, comments and ordering are preserved, and values Coolify withholds are noted as comments rather than invented.

```text
$ coolship env pull
Wrote 4 regular variable(s) to /home/you/my-app/.env
```

```json
{
  "target": { "…": "the resolved target" },
  "scope": "regular",
  "file": "/home/you/my-app/.env",
  "written": ["DATABASE_URL", "PORT"],
  "kept": ["LOCAL_ONLY"],
  "withheld": ["API_SECRET"]
}
```

`written`, `kept`, `withheld`, and `warnings` are omitted when empty.

## env diff

Compare the local file with the remote variables, from the local file's point of view: what `push` would create, update, or (with `--prune`) delete.

| Flag            | Purpose                                        |
| --------------- | ---------------------------------------------- |
| `--show-values` | Print values instead of masking them.          |
| `--exit-code`   | Exit with status 1 when there are differences. |

```text
$ coolship env diff
Comparing /home/you/my-app/.env with regular variables of fenix-bot
+ NEW_KEY=********  (local only; push creates it)
~ PORT: local ********, remote ********
- OLD_KEY=********  (remote only; push --prune deletes it)
? API_SECRET  (remote value withheld; cannot compare)
3 unchanged
```

When nothing differs, the second line is `No differences (3 unchanged)` instead.

```json
{
  "target": { "…": "the resolved target" },
  "scope": "regular",
  "file": "/home/you/my-app/.env",
  "added": [{ "key": "NEW_KEY" }],
  "changed": [{ "key": "PORT" }],
  "removed": [{ "key": "OLD_KEY" }],
  "withheld": ["API_SECRET"],
  "unchanged": 3
}
```

`added` keys exist only locally, `changed` keys differ, `removed` keys exist only remotely, and `withheld` keys have a remote value Coolify hides; `unchanged` counts the rest. Values are masked unless `--show-values` is given: the text output shows `********` (or `(empty)`) in their place, and the JSON entries carry only `key` until `--show-values` adds `local` and `remote`. `--exit-code` makes a clean comparison exit `0` and any difference exit `1`, which is useful in CI.

## env push

Create and update remote variables from the local file, in one bulk request. Keys that exist only remotely are left in place unless `--prune` is given. A key whose remote value is withheld is overwritten only with `--force`. The plan is shown and confirmed first, or requires `--yes` when noninteractive. Changes take effect on the next deployment.

| Flag          | Purpose                                                 |
| ------------- | ------------------------------------------------------- |
| `--prune`     | Delete remote variables that are not in the local file. |
| `--force`     | Overwrite remote values Coolify withholds.              |
| `-y`, `--yes` | Push without confirmation.                              |

```json
{
  "plan": {
    "target": { "…": "the resolved target" },
    "scope": "regular",
    "file": "/home/you/my-app/.env",
    "create": [{ "key": "NEW_KEY", "local": "value" }],
    "update": [{ "key": "PORT", "local": "3000", "remote": "8080" }],
    "delete": [{ "key": "OLD_KEY", "remote": "value" }],
    "skipped": ["API_SECRET"]
  }
}
```

`skipped` lists withheld keys that were not forced. `push` has no `--show-values`; its JSON carries the values it pushed and deleted as they are, so treat that output as you would the file itself. `push` preserves each variable's literal, multiline, and shown-once flags, which the server would otherwise reset. Coolify creates a preview-scope twin of every regular variable `push` creates.

## Examples

```bash
coolship env pull                 # remote → .env, keeping local-only keys and comments
coolship env diff                 # what push would change, values masked
coolship env diff --show-values
coolship env diff --exit-code     # CI check: 1 when .env drifted from Coolify
coolship env push                 # create and update; asks first
coolship env push --prune --yes   # also delete remote-only keys, without asking
coolship env pull --preview --file .env.preview
```

## Exit codes

`0` on success (for `diff --exit-code`, when there are no differences); `1` when the request fails, or for `diff --exit-code` when there are differences; `2` when the directory is not linked, the file cannot be parsed, or `push` needs `--yes` noninteractively; `130` when the confirmation is cancelled.
