# How to Generate an ENCRYPTION_KEY for FreeLLMAPI

> Generate a free 32-byte ENCRYPTION_KEY for FreeLLMAPI with a simple Node.js command. Secure your API access easily and quickly. Learn how now!

- Repository: [Tashfeen/freellmapi](https://github.com/tashfeenahmed/freellmapi)
- Tags: how-to-guide
- Published: 2026-08-30

---

**FreeLLMAPI requires a 32-byte (64-hex-character) encryption key that you can generate with a single Node.js command: `node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"`.**

FreeLLMAPI uses **AES-256-GCM encryption** to protect all stored provider API keys at rest. The encryption key must be provided via the `ENCRYPTION_KEY` environment variable and follows strict formatting requirements. This guide walks through generating a valid key and configuring it properly for both development and production environments.

## Understanding the ENCRYPTION_KEY Requirements

FreeLLMAPI's encryption system, implemented in [[`server/src/lib/crypto.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/crypto.ts)](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/crypto.ts), enforces these key constraints:

- **32 bytes of random data** (256 bits for AES-256)
- **64 hexadecimal characters** when encoded as a string
- **No placeholder values** in production environments

The `initEncryptionKey()` function (lines 94-100) handles key initialization on server startup. In **production**, missing or invalid keys cause the server to abort immediately via `missingKeyError()` (lines 49-55), preventing any plaintext storage of provider credentials.

## Generating a Valid ENCRYPTION_KEY

### Method 1: Node.js Built-in Crypto (Recommended)

The FreeLLMAPI source code explicitly recommends this approach at [lines 30-34 of [`crypto.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/crypto.ts)](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/crypto.ts#L29-L36):

```bash
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

```

This command:
- Generates 32 cryptographically secure random bytes using `crypto.randomBytes(32)`
- Converts to lowercase hexadecimal with `toString('hex')`
- Outputs a 64-character string ready for use

**Example output:**

```

9f1e2a5c7b3d4e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f

```

### Method 2: OpenSSL Alternative

If Node.js isn't available, use OpenSSL:

```bash
openssl rand -hex 32

```

Both methods produce equivalent 64-character hex strings that satisfy the `parseHexKey()` validation in the source code.

## Configuring ENCRYPTION_KEY in Your Environment

### Step 1: Create or Edit `.env` File

FreeLLMAPI reads environment variables from a `.env` file in the repository root. Add your generated key:

```bash
echo "ENCRYPTION_KEY=9f1e2a5c7b3d4e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f" >> .env

```

Replace the hex string with your generated value. The `.env.example` file in the repository documents this format with a placeholder value.

### Step 2: Restart the Server

The server must reload to pick up the new environment variable:

```bash

# Native development

npm run dev

# Docker deployment

docker compose down && docker compose up -d

```

### Step 3: Verify Key Initialization

Confirm successful key loading via the `/status` endpoint:

```bash
curl http://localhost:3001/status | jq '.encryptionKeyFingerprint'

```

Expected output:

```json
"sha256:a1b2c3d4e5f6..."

```

This fingerprint is computed by `encryptionKeyFingerprint()` (lines 73-76 in [[`server/src/lib/crypto.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/crypto.ts)](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/crypto.ts)), hashing the key without exposing it. The [[`server/src/routes/status.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/routes/status.ts)](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/routes/status.ts) file exposes this via `isEncryptionKeyInitialized()`.

## Development vs. Production Behavior

| Environment | Key Requirement | Fallback Behavior |
|-------------|---------------|-------------------|
| **Development** | Optional | Auto-generates in-memory key or uses file-based key near SQLite DB |
| **Production** | **Mandatory** | Server aborts with `missingKeyError()` if `ENCRYPTION_KEY` is unset or placeholder |

The development fallback exists for quick local testing but **must not be used in production**—provider keys would be unrecoverable if the in-memory key is lost.

## Complete Configuration Example

```bash

# 1. Generate key

$ node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
a3f7e2d9c1b8a4f5e6d7c8b9a0f1e2d3c4b5a6f7e8d9c0b1a2f3e4d5c6b7a8f9

# 2. Persist to environment

$ echo "ENCRYPTION_KEY=a3f7e2d9c1b8a4f5e6d7c8b9a0f1e2d3c4b5a6f7e8d9c0b1a2f3e4d5c6b7a8f9" > .env

# 3. Start server and verify

$ npm run dev &
$ curl -s http://localhost:3001/status | grep encryptionKeyInitialized
"encryptionKeyInitialized": true

```

## Key Implementation Files in FreeLLMAPI

- **[[`server/src/lib/crypto.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/crypto.ts)](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/crypto.ts)** — Core encryption logic: `initEncryptionKey()`, `parseHexKey()`, `encryptionKeyFingerprint()`, and dev fallback handling
- **[[`server/src/routes/status.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/routes/status.ts)](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/routes/status.ts)** — HTTP endpoint exposing encryption status
- **`.env.example`** — Template documenting required environment variables
- **[`README.md`](https://github.com/tashfeenahmed/freellmapi/blob/main/README.md)** — Quick-start documentation including install script behavior

## Summary

- **FreeLLMAPI ENCRYPTION_KEY** must be 32 bytes (64 hex characters) for AES-256-GCM encryption
- **Generate** with: `node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"`
- **Configure** in `.env` file as `ENCRYPTION_KEY=<64-hex-chars>`
- **Verify** via `/status` endpoint checking `encryptionKeyFingerprint`
- **Production requires explicit keys**—development fallbacks are insecure for live deployments
- Reference [[`server/src/lib/crypto.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/crypto.ts)](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/crypto.ts) for all implementation details

## Frequently Asked Questions

### What happens if I don't set ENCRYPTION_KEY in production?

The server calls `missingKeyError()` (lines 49-55 in [[`crypto.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/crypto.ts)](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/crypto.ts)) and terminates immediately. This hard failure prevents the application from starting without encryption, ensuring no provider credentials are stored unencrypted.

### Can I reuse the same ENCRYPTION_KEY across multiple deployments?

Yes, but this reduces security isolation. FreeLLMAPI encrypts provider keys with this key, so compromise of one deployment exposes others. Best practice: generate unique keys per environment and store them in secure secret management systems.

### How do I rotate an existing ENCRYPTION_KEY?

Key rotation requires re-encrypting all stored provider keys. FreeLLMAPI does not currently expose automated key rotation—implement this by decrypting with the old key and re-encrypting with a new key via the encryption functions in [[`server/src/lib/crypto.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/crypto.ts)](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/crypto.ts).

### Why does the status endpoint show a fingerprint instead of the full key?

The `encryptionKeyFingerprint()` function (lines 73-76) computes a SHA-256 hash truncated to 16 hex characters. This allows verification that a key is loaded without exposing the sensitive key material through API responses or logs.