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
ENCRYPTION_KEYenvironment variable — Validated 64-character hex string; used directly if present and valid- Development fallback file —
.encryption-keyfile adjacent to the SQLite database (non-production only) - Legacy database migration — Existing key from the
settingstable, migrated to file storage - 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-keyfile beside the SQLite database - Writes with atomic
0600permissions (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.
Key Files and Related Components
| 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_KEYenvironment 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.tsfor 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →