---
title: Set passwords: plain, env or bxsecret
order: 4
description: Three ways to set the console password and other secrets in BxLens - plain, an environment variable or an encrypted bxsecret value - and how to keep the seed safe.
icon: lucide:key-round
---

# Set passwords: plain, environment variable or bxsecret

The console needs a password. The SMTP and AI settings take secrets too. There are three ways to give BxLens a secret. All three go in `boxlang.json`, under `modules.bxLens.settings`.

| | How | Effort | Verdict |
|---|---|---|---|
| 1. Plain | `"password": "my-password"` | Easiest | A bad idea. The secret sits in a file you may commit or back up. Fine for a throwaway demo. |
| 2. Environment variable | `"password": "${env.BXLENS_PASSWORD}"` | Easy | Great. Nothing secret in the file, and every container platform and CI knows how to set one. |
| 3. `bxsecret:` | `"password": "bxsecret:AbCdEf123...=="` | More steps | Good. Encrypted in the file, but you must look after the seed. |

You can also combine 2 and 3: an environment variable can hold a `bxsecret:` value (see below).

## 1. Plain

```json
{ "modules": { "bxLens": { "settings": { "console": { "password": "my-password" } } } } }
```

It works, and it is the quickest way to try the console. Do not use it on a server people can reach, and never commit it.

## 2. Environment variable

BoxLang replaces `${...}` placeholders in `boxlang.json` when it loads the file, module settings included. Set the variable on the server, then name it in the file:

```json
{
	"modules": {
		"bxLens": {
			"settings": {
				"console": {
					"password": "${env.BXLENS_PASSWORD}",
					"viewerPassword": "${env.BXLENS_VIEWER_PASSWORD}"
				}
			}
		}
	}
}
```

```bash
export BXLENS_PASSWORD="a long random password"
```

* The `env.` prefix is optional since BoxLang 1.17: `${BXLENS_PASSWORD}` works too. If a Java system property has the same name, the property wins.
* The variable is read at startup. Restart the runtime after you change it.
* **Check that the variable is set.** BoxLang leaves a placeholder it cannot resolve as plain text, so with `BXLENS_PASSWORD` missing the password becomes the literal text `${env.BXLENS_PASSWORD}`. Anyone who guesses the variable name can sign in. Test the sign in after a deploy, and leave out `viewerPassword` entirely if you do not use the viewer role.
* A default goes after a colon, `${env.NAME:default}`, and needs at least one character. Do not use a default for a password.

This is a BoxLang feature, so it works for any setting, see the [BoxLang configuration docs](https://boxlang.ortusbooks.com/getting-started/configuration). Some BxLens settings also have their own variable setting: `ai.apiKeyEnv`, the SMTP password in Settings, Notifications, and `registration.keyEnv`. They take the variable **name**, not a placeholder.

## 3. bxsecret

A `bxsecret:` value is encrypted with the runtime's secret seed. BoxLang decrypts it when it loads the configuration (BoxLang 1.17 or newer). Make one with:

```bash
boxlang generatesecret "a long random password"
# bxsecret:AbCdEf123...==
```

Copy the whole output, prefix included, into the setting. Make a different value for each password.

```json
{
	"modules": {
		"bxLens": {
			"settings": {
				"console": {
					"password": "bxsecret:AbCdEf123...==",
					"viewerPassword": "bxsecret:GhIjKl456...=="
				}
			}
		}
	}
}
```

The command is the one BoxLang uses for any secret, see [boxlang generatesecret](https://boxlang.ortusbooks.com/getting-started/ide-tooling/boxlang-generatesecret) in the BoxLang docs.

An environment variable can hold the `bxsecret:` value, and the placeholder decrypts it: set `BXLENS_PASSWORD` to the `bxsecret:` text and keep `"password": "${env.BXLENS_PASSWORD}"` in the file. That keeps the file free of both the secret and the ciphertext.

## Which settings take a secret

| Setting | What it is | Notes |
|---|---|---|
| `console.password` | The admin password | Required. Without it the console shows a setup page (HTTP 503). Plain, `${env.NAME}` or `bxsecret:`. |
| `console.viewerPassword` | The viewer password | Optional. A second password for the read only role. |
| `notifications.smtp.password` | The SMTP password for alerts and reports | `boxlang.json` only. Or name an environment variable in Settings, Notifications. |
| `ai.apiKey` | The API key of a hosted model | `boxlang.json` only, the console can never change it. Or use `ai.apiKeyEnv` with the name of an environment variable. Leave it empty for a local model. |

The console never shows these values. The Settings page shows only `set` or `not set`.

## The seed (bxsecret only)

A `bxsecret:` value is encrypted with the **secret seed** of the runtime that made it. Each BoxLang installation creates its own seed in `{boxlang-home}/config/.seed`. A value only decrypts with the seed it was made with.

This matters in three cases:

* **Several servers share one config.** Give them all the same seed, or each server will fail to decrypt the password and show the setup page. Copy the same `.seed` file to each server, or set `BOXLANG_SECURITY_SECRETSEED` on each one.
* **A container or a new machine.** The seed is created again, so values made elsewhere stop working. Pin it with the environment variable.
* **A lost seed.** The values cannot be recovered. Make them again.

```bash
export BOXLANG_SECURITY_SECRETSEED=my-shared-seed-value
boxlang generatesecret "a long random password"
```

You can also set `security.secretSeed` in `boxlang.json`, but that keeps the seed in plain text next to the values it protects. Prefer the environment variable or the `.seed` file.

Do not commit the `.seed` file or a plain text password.

## When it does not work

* The console shows the setup page, and the log says `console.password is empty or cannot be decrypted`. The value was made with another seed, was copied without its prefix or was cut. Make it again on a runtime that uses the server's seed. See [Troubleshooting](../troubleshooting.md).
* A password with special characters on the command line. Quote it, and use single quotes in a shell where `$` or `!` would expand.

See also [Running Lens in production](production.md) and [Security](../security.md#console-protection).
