# Set values per environment

> Give the pack service its environment for staging or production, with secrets by reference, and check it with kindgi env plan.

This guide gives the pack service its values in each deploy target, such as staging or production, and checks them before you deploy.

## Before you start

- **A pack that declares the names it reads,** as [Declare the environment your code reads](https://docs.kindgi.com/v0.1.9/guides/secrets/pack-env.md) shows, with Kindgi 0.1's CLI.
- **A deploy target** in the pack's config, under `environments`. This guide uses `staging`.

> **Note: On Kindgi Cloud**
>
> There the pack service gets the values your workspace sets, with `kindgi env set NAME <value> --scope=project:<projectId> --env=prod`, or as a secret with `kindgi secrets set NAME --scope=project:<projectId> --env=prod`, not the ones this page sets in the pack's config: `kindgi deploy --hosted` refuses an `env` for the environment it deploys to. Values there are kept under `prod` only: one set under another environment is refused with `env-not-served`. [Declare the environment your code reads](https://docs.kindgi.com/v0.1.9/guides/secrets/pack-env.md#in-a-deployment) says what the pack service gets there.

## Give the values

Each deploy target of a pack is an **environment** in its config, such as `staging` or `production`. Its `env` gives the values of the names the pack declares, for the pack service running there. Ask your coding agent, for example: "Set my pack's values for staging. The database URL is a secret in our Secret Manager." For reference, it writes them in the pack's config like this:

- **TypeScript**

  ```ts excerpt
  // in kindgi.config.ts
  environments: {
    staging: {
      // endpoint, build, registry, signingKey, tenantId…
      env: {
        ORDERS_APP_URL: 'https://backoffice.staging.example.com',
        ORDERS_DB_URL: { secret: 'orders-db-url', version: '3' },
      },
    },
  },
  ```

- **Python**

  ```toml excerpt
  # in pyproject.toml
  [tool.kindgi.environments.staging.env]
  ORDERS_APP_URL = "https://backoffice.staging.example.com"
  ORDERS_DB_URL = { secret = "orders-db-url", version = "3" }
  ```

- **Java**

  ```jsonc excerpt
  // in kindgi.config.json
  "environments": {
    "staging": {
      "env": {
        "ORDERS_APP_URL": "https://backoffice.staging.example.com",
        "ORDERS_DB_URL": { "secret": "orders-db-url", "version": "3" }
      }
    }
  }
  ```

- **Scala**

  ```jsonc excerpt
  // in kindgi.config.json
  "environments": {
    "staging": {
      "env": {
        "ORDERS_APP_URL": "https://backoffice.staging.example.com",
        "ORDERS_DB_URL": { "secret": "orders-db-url", "version": "3" }
      }
    }
  }
  ```

* **A plain value** is for what isn't secret. It's committed with the pack, and visible in the deployed service's settings.
* **`{ secret, version }`** names a secret in your cloud's Secret Manager. When the secret is in another project, add `project`, the project's number. The platform that runs the pack service resolves it, and Kindgi never reads the value. Use a version number: `latest` changes under you.

## Check it

Check the environment before you deploy. `--env=staging` is the deploy target:

- **TypeScript**

  ```sh
  pnpm exec kindgi env plan --env=staging
  ```

- **Python**

  ```sh
  uv run kindgi env plan --env=staging
  ```

- **Java**

  ```sh
  ./kindgiw env plan --env=staging
  ```

- **Scala**

  ```sh
  ./kindgiw env plan --env=staging
  ```

```text
  The pack service's env in staging:
    required  ORDERS_APP_URL                value "https://backoffice.staging.example.com"
    required  ORDERS_DB_URL                 secret orders-db-url:3
    optional  ORDERS_API_TIMEOUT_MS         (unset: not injected)
```

That summary goes to stderr. On stdout, `env plan` prints the same environment for your infrastructure code. By default, it's Terraform input:

```json
{
  "env": {
    "ORDERS_APP_URL": "https://backoffice.staging.example.com"
  },
  "secret_env": {
    "ORDERS_DB_URL": {
      "secret": "orders-db-url",
      "version": "3"
    }
  }
}
```

With `--format=gcloud`, it's flags for `gcloud run deploy`, which set these names and leave the service's other variables alone:

```text
--update-env-vars=ORDERS_APP_URL=https://backoffice.staging.example.com \
--update-secrets=ORDERS_DB_URL=orders-db-url:3
```

## What it refuses

`env plan` exits with status 1, and names the problem, when the environment won't work:

- **A required name has no value:** `✗ ORDERS_DB_URL is required and has no value in environments.staging.env`
- **A secret is given in the clear,** either by its value, such as a URL with a password, or by its name, such as `*_KEY`, `*_TOKEN` or `*_PASSWORD`:

```text
  ✗ ORDERS_DB_URL's value has a credential in it (a URL with a password): give it as { secret, version } in environments.staging.env
```

```text
  ✗ ORDERS_API_KEY is named like a secret: give it as { secret, version } in environments.staging.env
```

`kindgi deploy` runs the same check before it builds or sends anything:

```text
kindgi deploy: ORDERS_DB_URL is required by the pack and has no value in environments.staging.env, so the pack service wouldn't be ready. Add it, or deploy anyway with --allow-missing-env.
```

## Env files for other environments

`kindgi env` manages env files per environment. `local` is the pack's own files, `.env` and `.env.local`. Any other name is its `.env.<name>`. `--env=staging` writes `.env.staging`:

- **TypeScript**

  ```sh
  pnpm exec kindgi env set ORDERS_API_URL https://orders.staging.example.com --env=staging
  ```

- **Python**

  ```sh
  uv run kindgi env set ORDERS_API_URL https://orders.staging.example.com --env=staging
  ```

- **Java**

  ```sh
  ./kindgiw env set ORDERS_API_URL https://orders.staging.example.com --env=staging
  ```

- **Scala**

  ```sh
  ./kindgiw env set ORDERS_API_URL https://orders.staging.example.com --env=staging
  ```

```text excerpt
  ✓ Wrote ORDERS_API_URL in …/my-pack/.env.staging
```

`kindgi env list --env=staging` shows them, redacted. `kindgi env unset` removes one. `set` won't change a name a file already sets without `--force`, and refuses `KINDGI_*` names.

Under `kindgi dev`, a secret reference that names an environment other than `local` resolves from that environment's file. An HTTP tool's `secretRef: { envName: 'staging', … }`, for example, resolves from `.env.staging`.

The pack service's environment in a deployment comes from `environments.<name>.env`, not from these files: `env plan` doesn't read them.

## Next

- [Declare the environment your code reads](https://docs.kindgi.com/v0.1.9/guides/secrets/pack-env.md)
- [Store a secret](https://docs.kindgi.com/v0.1.9/guides/secrets/store-a-secret.md)
- [Keep secrets in your own secret manager](https://docs.kindgi.com/v0.1.9/deploy/self-hosted/secret-manager.md)
