How to Configure the Encryption Key for FreeLLMAPI: Complete Setup Guide

Set a 64-character hex string in the ENCRYPTION_KEY environment variable to enable AES-256-GCM encryption for all stored API keys.

FreeLLMAPI uses AES-256-GCM encryption to protect API keys at rest. The encryption key must be a 32-byte (64-character hexadecimal) secret configured through environment variables. This guide walks through generating, validating, and deploying this key based on the actual implementation in tashfeenahmed/freellmapi.

Understanding the Encryption Key System

The encryption logic resides in [server/src/lib/crypto.ts](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/crypto.ts). On server startup, initEncryptionKey(db) executes a priority-based lookup to determine which key to use.

Key Resolution Order

  1. ENCRYPTION_KEY environment variable — Validated 64-character hex string; used directly if present and valid
  2. Development fallback file — .encryption-key file adjacent to the SQLite database (non-production only)
  3. Legacy database migration — Existing key from the settings table, migrated to file storage
  4. Auto-generated dev key — Fresh random key created and persisted to file (development only)

In production mode (NODE_ENV=production), the server strictly requires a valid ENCRYPTION_KEY. Missing or invalid keys trigger an immediate startup error:


ENCRYPTION_KEY is required in production for API key encryption.
Set a 64-character hex key (generate one with:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))").
Outside production a local key file is auto-generated next to the database.

Generating a Valid Encryption Key

Run this command once to produce a cryptographically secure 64-character hexadecimal string:

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

Example output:


a3f1c8e9b5d6f7a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3

This satisfies the validation check in initEncryptionKey: exactly 64 characters, hexadecimal only (/^[a-f0-9]{64}$/i).

Configuring the Encryption Key in FreeLLMAPI

Step 1: Copy the Environment Template

The repository includes .env.example as a starting configuration:

cp .env.example .env

The relevant section contains:

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

Step 2: Replace with Your Generated Key

Edit .env and substitute the placeholder:

ENCRYPTION_KEY=a3f1c8e9b5d6f7a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3

Step 3: Load and Verify

Start the server in development to confirm successful key loading:

npm run dev

With ENCRYPTION_KEY properly set, no warnings appear. The key is cached in memory via cachedKey and accessible through getEncryptionKey() for all encryption operations.

For production deployments, export the variable in your hosting environment rather than relying on .env files.

Development-Only Auto-Configuration

When running FreeLLMAPI locally without ENCRYPTION_KEY set, the system automatically handles key provisioning:

  • Creates .encryption-key file beside the SQLite database
  • Writes with atomic 0600 permissions (owner read/write only)
  • Reuses this file across subsequent restarts

This convenience is disabled in production. Never commit auto-generated key files to version control.

Programmatic Key Usage

The encryption subsystem exposes three core functions from [server/src/lib/crypto.ts](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/crypto.ts):

import { initEncryptionKey, encrypt, decrypt } from './server/src/lib/crypto.js';
import { connectDb } from './server/src/db/index.js';

const db = connectDb();
initEncryptionKey(db);  // Must run before encrypt/decrypt

const secret = 'sk-my-api-key';
const cipher = encrypt(secret);
// Returns: { encrypted: Buffer, iv: Buffer, authTag: Buffer }

const recovered = decrypt(cipher.encrypted, cipher.iv, cipher.authTag);

All stored API keys in FreeLLMAPI follow this encryption pattern. The initialization step ensures the cached key is available before any cryptographic operations execute.

File Purpose
[server/src/lib/crypto.ts](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/crypto.ts) Core implementation: initEncryptionKey, getEncryptionKey, encrypt, decrypt
.env.example Template with ENCRYPTION_KEY placeholder
[server/src/lib/db-backup.ts](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/db-backup.ts) Database backup encryption (separate FREEAPI_DB_BACKUP_KEY or falls back to main key)

Summary

  • Generate a 64-character hex key using Node.js crypto.randomBytes(32)
  • Configure via ENCRYPTION_KEY environment variable in production
  • Validate that the server starts without encryption warnings
  • Protect key files with restrictive permissions and exclude from version control
  • Reference server/src/lib/crypto.ts for implementation details

Frequently Asked Questions

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

FreeLLMAPI throws a fatal startup error with explicit instructions. The server refuses to start without valid encryption configuration when NODE_ENV=production.

Can I rotate or change the encryption key later?

The current implementation does not provide automatic key rotation. Changing ENCRYPTION_KEY invalidates all previously encrypted API keys. Plan key rotation carefully with backup restoration procedures.

How does the auto-generated dev key work?

When ENCRYPTION_KEY is unset and NODE_ENV is not production, initEncryptionKey creates .encryption-key in the database directory. This file persists across restarts but should never be used for production deployments.

Is the same encryption key used for database backups?

[server/src/lib/db-backup.ts](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/db-backup.ts) checks for FREEAPI_DB_BACKUP_KEY first, then falls back to the main ENCRYPTION_KEY. Configure a separate backup key for additional isolation if desired.

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 →