# Env

Manage environment variables, locally or on a server. Everything that differs between environments is an environment variable, for the app and for PocketBase alike: locally they live in `.env` at the project root; on a server they live under `/etc/vela`, readable only by root, and never travel inside a release.

### Syntax

```
$ vela env <command>
```

- [`list`](/docs/env/list) - Show which variables exist where
- [`get`](/docs/env/get) - Print a public variable
- [`set`](/docs/env/set) - Set a variable, prompting for the value
- [`unset`](/docs/env/unset) - Remove a variable
- [`import`](/docs/env/import) - Merge a dotenv file into the environment

### Scopes

On a server, a target's environment is three layers, lowest to highest. A variable in a higher layer wins:

```
$ vela env set VELASTACK_API_KEY -t all        # every target, on every server the app is deployed to
$ vela env set WHATSAPP_MODE -t preview        # every preview
$ vela env set STRIPE_SECRET_KEY -t production # one target
```

`-t all` and `-t preview` write to every server the project's targets are bound to, and restart every instance the layer reaches on each. A preview branch inherits both shared layers, so it is never empty on its first deploy; `-t preview:<branch>` addresses one preview on its own. `-t local` is the `.env` on this machine.

For `env`, a bare `-t preview` means the layer every preview shares; for [`vela deploy`](/docs/deploy) it means the branch checked out here. A preview API key is set once, a branch is deployed many times.

There is no default. Without `-t`, `set`, `unset` and `import` ask which environment at a terminal and refuse in CI; `list` shows every scope at once.

### Secret unless said otherwise

A value on a server is secret by default: written, never read back, shown as `••••`. Pass `--public` for one that should read back — `vela env get` prints it and `vela env list` shows it. `PUBLIC_*` keys are public without asking, since they are meant for the browser. Setting a key again with or without `--public` moves it; `unset` removes it from whichever side it was on.

Values are always prompted for, never taken as an argument, so none lands in shell history. A secret is typed without echo, a public value with, so the prompt itself says which kind you are setting.

### The app reads what `src/env.ts` declares

SvelteKit exposes only the variables declared in `src/env.ts`. A server-side one is imported by name from `$app/env/private`, one declared `public: true` from `$app/env/public`; a variable that isn't declared isn't exposed at all, however it was set. The templates declare the variables they read, and an [`enable`](/docs/enable) command that adds code reading one declares it too. A variable of your own needs an entry in the file's `defineEnvVars` call before the app can see it:

```ts
MY_API_KEY: {
	schema: (value) => value ?? '',
	description: 'Key for the example API.'
},
```

Vela's declarations turn a missing value into `''`, so the app builds and starts with a blank `.env`.

### PocketBase reads the same environment

With a backend, PocketBase takes its own settings from the same variables and shows them locked in the [admin panel](/docs/admin): `APP_NAME` (handed over from [`src/lib/site.ts`](/docs/cli/project-structure)), `APP_URL` (the deploy's origin unless you say otherwise), `PB_SMTP_HOST`, `PB_SMTP_PORT`, `PB_SMTP_USERNAME`, `PB_SMTP_PASSWORD`, `PB_SMTP_TLS`, `PB_SENDER_NAME`, `PB_SENDER_ADDRESS`, `PB_S3_*`, `PB_BACKUPS_S3_*`, and the WhatsApp plugin's `WHATSAPP_*` / `VELASTACK_*`. Setting `PB_SMTP_HOST` turns SMTP on; setting `PB_S3_ENDPOINT` moves uploads to that bucket. The mail and storage names are prefixed on purpose: an app's own `SMTP_HOST` or `S3_BUCKET` must not switch PocketBase mail or storage on.

### Options

Every subcommand takes the same target and connection options:

- `-t, --target <target>` - Which environment: `local`, `all`, `preview`, `preview:<branch>`, `production`, or any bound target such as `staging`
- `--public` - On `set` and `import`: readable back
- `--server <ssh>` - Server a target runs on, recorded on first use; not taken by `all` or `preview`, which go to the bound servers
- `-i, --identity <file>` - SSH private key to authenticate with
- `--ssh-port <port>` - SSH port
- `--accept-host-keys` - Trust an unknown host key on first connect (CI)

### Deploys never write them

[`vela deploy`](/docs/deploy) does not upload or overwrite the environment on a server. It does read it: the build runs with the target's resolved environment rather than your `.env`, so a prerendered page sees what the running app will. A key set only in `.env` is blanked for the build, and a `$env/static` import of one fails the deploy before anything is uploaded. A key the app imports from `$env/static` is baked at build time, so `set` says "redeploy to apply" for it rather than restarting.

On an instance's first deploy, the keys `.env.example` documents that the target lacks are listed and, at a terminal, offered for setting. Nothing is required; the file documents, it does not gate.