> Configure your Penombre instance with environment variables.
> Source: https://orochibraru.com/penombre/docs/env · Site index: https://orochibraru.com/llms.txt

# Environment Variables

Configure your Penombre instance with environment variables.

See
[.example.env](https://github.com/orochibraru/penombre/blob/main/.example.env)
for a complete reference.

## Core

General application settings that control the runtime identity, environment
mode, and logging behavior.

`ORIGIN` must be set to the public URL where your instance is accessible. It is
used to generate absolute URLs in OAuth callbacks and email links. In
development this is typically `http://localhost:3000`.

Behind a reverse proxy, also set `ADDRESS_HEADER` (and `XFF_DEPTH` for
`X-Forwarded-For`) so Penombre reads the real client address instead of the
proxy's own; see [Reverse proxy](https://orochibraru.com/penombre/docs/reverse-proxy#client-address).

| Variable         | Description                                                | Default                 |
| ---------------- | ---------------------------------------------------------- | ----------------------- |
| `APP_NAME`       | Application name (used in UI and emails)                   | `Penombre`              |
| `APP_ENV`        | Environment (`dev`/`production`)                           | `production`            |
| `ORIGIN`         | Public origin URL (used for OAuth callbacks)               | `http://localhost:3000` |
| `ADDRESS_HEADER` | Header carrying the real client IP, e.g. `x-forwarded-for` | Unset                   |
| `XFF_DEPTH`      | Trusted proxy hops to count back in `X-Forwarded-For`      | `1`                     |
| `LOG_LEVEL`      | `debug`, `info`, `warn`, `error`, `trace`                  | `info`                  |
| `LOG_FORMAT`     | `console` or `json`                                        | `console`               |

## Database

Penombre runs on **SQLite** by default — no database server to install, back up
or keep patched. The Docker image ships pointing at a file inside its data
volume, so a fresh install needs no database configuration at all.

The dialect is picked from the `DATABASE_URL` scheme: a `postgres:`/
`postgresql:` URL runs PostgreSQL, anything else runs SQLite — including an
empty value, which falls back to the default below. With a `file:`/`sqlite:` URL
the rest of the value is the path to the database file, created on first boot.

```ini
# SQLite (default) — no database server needed
DATABASE_URL=file:/data/db/penombre.sqlite

# PostgreSQL (optional)
DATABASE_URL=postgresql://penombre:penombre@localhost:5432/penombre
```

**PostgreSQL is optional.** It is still fully supported and worth the extra
container if you run **multiple app instances** against one database. For a
single container — which is what most self-hosted installs are — SQLite is the
simpler and faster choice.

| Variable       | Description                                          | Default                             |
| -------------- | ---------------------------------------------------- | ----------------------------------- |
| `DATABASE_URL` | SQLite `file:` path, or a Postgres connection string | `file:$DATA_DIR/db/penombre.sqlite` |

## Initial Setup

These variables are only used during the **first-time database seed** to create
the initial admin account. They have no effect after the database has been
initialized and can be removed from your environment afterwards.

There are **no admin credential variables**. The first administrator is created
through the setup screen on first boot — see
[Authentication](https://orochibraru.com/penombre/docs/authentication#initial-admin-account).

## Authentication

Controls how users authenticate. `AUTH_SECRET` must be a long, random string —
you can generate one with `openssl rand -hex 32`. Keep it secret and do not
rotate it without invalidating all existing sessions.

| Variable                | Description                        | Default  |
| ----------------------- | ---------------------------------- | -------- |
| `AUTH_SECRET`           | Secret key for signing auth tokens | Required |
| `ENABLE_EMAIL_SIGNIN`   | Enable email/password sign-in      | `true`   |
| `ENABLE_OAUTH_SIGNIN`   | Enable OAuth sign-in               | Auto     |
| `ENABLE_PASSKEY_SIGNIN` | Enable passkey sign-in             | `true`   |
| `MIN_PASSWORD_LENGTH`   | Minimum password length            | `8`      |

`AUTH_AUTO_REDIRECT_PROVIDER` skips the sign-in screen and sends users straight
to one OIDC provider. See
[Skipping the sign-in screen](https://orochibraru.com/penombre/docs/authentication#skipping-the-sign-in-screen).

| Variable                      | Description                          | Default |
| ----------------------------- | ------------------------------------ | ------- |
| `AUTH_AUTO_REDIRECT_PROVIDER` | Provider name to redirect to on load | /       |

## OAuth Providers (Optional)

Penombre supports any OIDC-compliant provider. Configure one with the naming
pattern `OAUTH_<PROVIDER>_<SETTING>`, where `<PROVIDER>` is an uppercase
identifier of your choice (e.g. `GOOGLE`, `GITHUB`, `POCKET_ID`) — declaring a
provider is enough to turn OAuth sign-in on. `ENABLE_OAUTH_SIGNIN` forces it
either way, and taking it out of the environment hands the decision to the admin
UI, which can manage providers of its own.

| Variable                         | Description            | Default                |
| -------------------------------- | ---------------------- | ---------------------- |
| `OAUTH_<PROVIDER>_ENABLED`       | Enable this provider   | `true`                 |
| `OAUTH_<PROVIDER>_CLIENT_ID`     | OAuth client ID        | Required               |
| `OAUTH_<PROVIDER>_CLIENT_SECRET` | OAuth client secret    | Required               |
| `OAUTH_<PROVIDER>_DISCOVERY_URL` | OIDC discovery URL     | Required               |
| `OAUTH_<PROVIDER>_PRETTY_NAME`   | Display name in the UI | Provider name          |
| `OAUTH_<PROVIDER>_PKCE`          | Use PKCE               | `true`                 |
| `OAUTH_<PROVIDER>_SCOPES`        | Comma-separated scopes | `openid,profile,email` |

## Redis (Optional)

Penombre includes an in-memory cache for file listings and metadata. By default,
each app instance maintains its own cache in-process. To share the cache across
multiple instances or preserve it across restarts, you can connect an external
Redis server.

When `REDIS_URL` is set, all caching is offloaded to Redis. When it is not set,
caching falls back to an in-process memory cache (production) or is disabled
entirely (development).

| Variable    | Description             | Default |
| ----------- | ----------------------- | ------- |
| `REDIS_URL` | Redis connection string | /       |

## Worker

Thumbnails, waveforms, scans, zip downloads and trash/copy byte work run in a
separate Go worker process. By default it runs embedded inside this container;
set `WORKER_MODE=external` to run it as its own container instead. See
[Worker](https://orochibraru.com/penombre/docs/worker) for the full guide.

| Variable             | Description                                 | Default    |
| -------------------- | ------------------------------------------- | ---------- |
| `WORKER_MODE`        | `embedded` (this container) or `external`   | `embedded` |
| `WORKER_CONCURRENCY` | Number of jobs the worker runs concurrently | `4`        |

## SMTP (Optional)

Required for email features such as password reset and email verification. Set
`SMTP_ENABLED=true` and provide the connection details for your mail server. If
SMTP is disabled, email-dependent features will be unavailable.

| Variable        | Description              | Default             |
| --------------- | ------------------------ | ------------------- |
| `SMTP_ENABLED`  | Enable SMTP              | `false`             |
| `SMTP_HOST`     | SMTP server hostname     | Required if enabled |
| `SMTP_PORT`     | SMTP server port         | `587`               |
| `SMTP_USER`     | SMTP username            | Required if enabled |
| `SMTP_PASSWORD` | SMTP password            | Required if enabled |
| `SMTP_FROM`     | Sender email address     | Required if enabled |
| `SMTP_SECURE`   | Use TLS (`true`/`false`) | `false`             |

## Storage

Everything Penombre writes — uploaded files and the SQLite database — hangs off
one data directory, so a single mounted volume covers a whole install. Set
`DATA_DIR` to move all of it at once, or override a single path on its own. See
[Storage](https://orochibraru.com/penombre/docs/storage) for the full guide.

| Variable       | Description                       | Default                   |
| -------------- | --------------------------------- | ------------------------- |
| `DATA_DIR`     | Base directory for all app data   | `/data` (`./data` in dev) |
| `STORAGE_PATH` | Absolute path to the storage root | `$DATA_DIR/storage`       |

## Mounted Volumes (Optional)

Extra directories mounted alongside the main drive, one block per volume. See
[Mounted volumes](https://orochibraru.com/penombre/docs/volumes) for the full guide.

`<NAME>` is uppercase with underscores; it is lowercased and hyphenated to form
the volume's id, so `VOLUME_MEDIA_PATH` gives `/volumes/media`. A block without
a `_PATH` is ignored.

| Variable                 | Description                               | Default |
| ------------------------ | ----------------------------------------- | ------- |
| `VOLUME_<NAME>_PATH`     | Absolute path to the directory (required) | /       |
| `VOLUME_<NAME>_LABEL`    | What the sidebar shows                    | `NAME`  |
| `VOLUME_<NAME>_READONLY` | Refuse every write to the volume          | `false` |
| `VOLUME_<NAME>_ENCRYPT`  | Seal what Penombre writes (needs a key)   | `false` |

A volume is one tree shared by every account, in both modes: mount a library and
everybody browses the files already on it. Pair it with `_READONLY` when nobody
should be able to change them.

## Encryption (Optional)

Seals file bytes at rest on personal and shared drives. Losing the key loses
every sealed file: keep a copy apart from `DATA_DIR` and its backups. Refused
with `SIMPLE_MODE=true`. See [Encryption](https://orochibraru.com/penombre/docs/encryption) for the full guide.

| Variable                  | Description                                | Default |
| ------------------------- | ------------------------------------------ | ------- |
| `ENCRYPTION_KEY`          | 32 random bytes, base64                    | Unset   |
| `ENCRYPTION_KEY_FILE`     | File holding the key, e.g. a Docker secret | Unset   |
| `ENCRYPTION_KEY_PREVIOUS` | Retired keys, comma-separated, read-only   | Unset   |

## Simple Mode (Optional)

Turns Penombre into a bare shared file browser: one storage volume shared by
every account instead of a drive per user. See [Simple mode](https://orochibraru.com/penombre/docs/simple-mode) for
the full guide.

`BYPASS_AUTH` removes authentication entirely — see
[No sign-in at all](https://orochibraru.com/penombre/docs/simple-mode#no-sign-in-at-all) before enabling it. It is
ignored unless `SIMPLE_MODE=true`.

| Variable      | Description                            | Default |
| ------------- | -------------------------------------- | ------- |
| `SIMPLE_MODE` | Enable simple mode (`true`/`false`)    | `false` |
| `BYPASS_AUTH` | Disable sign-in, everyone is the owner | `false` |

## Version Check (Optional)

An hourly check against GitHub releases, shown as an "update available" banner.
Also settable in Admin → Settings; the environment wins whenever it is set. See
[Deployment](https://orochibraru.com/penombre/docs/deployment) for the tags each channel watches.

| Variable               | Description                                   | Default                                             |
| ---------------------- | --------------------------------------------- | --------------------------------------------------- |
| `ENABLE_VERSION_CHECK` | Run the check at all (`true`/`false`)         | `true`                                              |
| `RELEASE_CHANNEL`      | Compare against `stable` or `canary` releases | `canary` on a `-canary.N` build, `stable` otherwise |

## Data Retention (Optional)

How long `activity`, `notifications` and finished background job rows are kept
before a nightly sweep deletes them. Also settable in Admin → Settings; the
environment wins whenever it is set. See [Admin panel](https://orochibraru.com/penombre/docs/admin#settings).

| Variable              | Description                 | Default               |
| --------------------- | --------------------------- | --------------------- |
| `DATA_RETENTION_DAYS` | Days to keep the rows above | unset (keeps forever) |
