How to Use ENCRYPTION_KEY with FreeLLMAPI: Complete Configuration Guide

FreeLLMAPI requires a 32-byte (64-character hex) ENCRYPTION_KEY environment variable to secure provider API keys with AES-256-GCM encryption; production deployments fail to start without it, while development mode auto-generates a temporary local key.

FreeLLMAPI encrypts every provider API key stored in its SQLite database using industry-standard AES-256-GCM. This security mechanism relies on the ENCRYPTION_KEY environment variable, which must be supplied as a cryptographically secure hexadecimal string to initialize the encryption layer.

How ENCRYPTION_KEY Secures Provider Credentials

FreeLLMAPI uses AES-256-GCM encryption to protect sensitive provider tokens at rest. The ENCRYPTION_KEY serves as the master secret for this encryption layer. When you add a provider API key through the interface, the application encrypts it using the current key before writing to the database.

According to the FreeLLMAPI source code, the encryption module validates that the key is exactly 64 hexadecimal characters (representing 32 bytes) before use. This requirement is enforced in server/src/lib/crypto.ts through the parseHexKey function.

Where FreeLLMAPI Loads the Encryption Key

The server attempts to load the encryption key from environment variables during startup. In server/src/lib/crypto.ts, the initialization logic checks for the ENCRYPTION_KEY environment variable:

if (envKey && envKey !== PLACEHOLDER_KEY) {
  cachedKey = parseHexKey(envKey, 'env');   // ✅ valid key → cached
}

If the variable is present and not a placeholder, the server parses and caches it for the runtime session.

Production Requirements

In production environments, FreeLLMAPI enforces strict key presence. The boot sequence in server/src/index.ts includes a guard that aborts startup if the encryption key is missing:

// A boot failure (e.g. a missing production ENCRYPTION_KEY) must exit

If isDevFallbackAllowed() returns false and no valid key exists, the server throws missingKeyError() and exits immediately.

Development Fallback Behavior

During development, FreeLLMAPI can operate without manual key configuration. If ENCRYPTION_KEY is unset and the environment allows development fallbacks, the server checks the SQLite database for a persisted key:

const row = db.prepare("SELECT value FROM settings WHERE key = 'encryption_key'").get();
if (row) {
  cachedKey = parseHexKey(row.value, 'db');
  console.warn('[crypto] No ENCRYPTION_KEY set — using auto-generated key from the local DB (dev only).');
}

If no key exists in the database, FreeLLMAPI generates one cryptographically using crypto.randomBytes(KEY_BYTES), persists it to the settings table, and logs a warning:

cachedKey = crypto.randomBytes(KEY_BYTES);
db.prepare("INSERT INTO settings (key, value) VALUES ('encryption_key', ?)").run(cachedKey.toString('hex'));
console.warn('[crypto] No ENCRYPTION_KEY set — generated and persisted a local dev key.');

Generating a Valid 64-Character Hex Key

The ENCRYPTION_KEY must be exactly 64 hexadecimal characters (32 bytes). FreeLLMAPI provides two standard methods for generation:

Using OpenSSL (macOS/Linux):

ENCRYPTION_KEY=$(openssl rand -hex 32)
echo $ENCRYPTION_KEY

Using Node.js (cross-platform):

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

Both commands output a string like a1b2c3d4... (64 characters) suitable for the environment variable.

Configuring ENCRYPTION_KEY in Your Environment

After generating the key, persist it in a .env file in the project root:

ENCRYPTION_KEY=a1b2c3d4e5f6...64_chars_total...
PORT=3001

The .env.example file included in the repository demonstrates the required format:

ENCRYPTION_KEY=your-64-char-hex-key-here

For Docker deployments, pass the variable through your compose configuration:

services:
  freellmapi:
    image: ghcr.io/tashfeenahmed/freellmapi:latest
    env_file: .env
    volumes:
      - freellmapi-data:/app/server/data

As documented in the README and Docker instructions, generate and write the key before first launch:

ENCRYPTION_KEY="$(openssl rand -hex 32)"
printf "ENCRYPTION_KEY=%s\nPORT=3001\n" "$ENCRYPTION_KEY" > .env

The Risk of Changing Your Encryption Key

Never change the ENCRYPTION_KEY after provider keys are stored. Provider API keys are encrypted using the current master key. If you change ENCRYPTION_KEY after data exists, FreeLLMAPI cannot decrypt the stored credentials, rendering all provider configurations unusable.

When migrating or upgrading FreeLLMAPI, ensure the same ENCRYPTION_KEY value accompanies the freellmapi-data volume. If you must change keys, you must first delete all encrypted provider configurations and re-enter them after the new key is active.

Key Validation and Error Handling

The parseHexKey function in server/src/lib/crypto.ts validates key format strictly. If you provide a malformed key, the server throws a descriptive error:


Invalid ENCRYPTION_KEY (env): expected 64 hex chars (32 bytes), got X chars.
Generate one with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

This validation occurs early in the boot process, ensuring configuration issues fail fast before the server accepts connections.

Summary

  • FreeLLMAPI uses AES-256-GSM encryption requiring a 64-character hex ENCRYPTION_KEY (32 bytes).
  • Production deployments fail immediately if the variable is missing; development mode auto-generates and caches a temporary key.
  • Generate valid keys using openssl rand -hex 32 or Node.js crypto.randomBytes(32).
  • Store the key in .env or pass via Docker environment variables before starting the server.
  • Changing the key invalidates existing encrypted provider data; always maintain key continuity across upgrades.

Frequently Asked Questions

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

FreeLLMAPI aborts startup with a fatal error. The boot-time guard in server/src/index.ts checks for key presence when isDevFallbackAllowed() returns false, throwing missingKeyError() and exiting the process to prevent running with unencrypted storage.

Can I change the ENCRYPTION_KEY after initial setup?

Only if no provider API keys are stored. Since all provider credentials are encrypted with the master key, changing it renders existing database entries unreadable. To rotate keys, you must delete the encryption_key entry from the settings table (or wipe the data volume) and re-configure all providers.

How do I generate a secure ENCRYPTION_KEY?

Use the built-in cryptographic tools demonstrated in the FreeLLMAPI documentation. Run openssl rand -hex 32 on Unix systems or node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" on any platform with Node.js. Both produce a 64-character hexadecimal string that satisfies the parseHexKey validator.

Why is FreeLLMAPI rejecting my ENCRYPTION_KEY as invalid?

The key must be exactly 64 hexadecimal characters (0-9, a-f). The parseHexKey function rejects values with incorrect length or non-hex characters. Ensure you haven't included quotes, whitespace, or the 0x prefix in the environment variable value.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →