How to Self-Host FreeLLMAPI Using Docker and Docker Compose

FreeLLMAPI ships as a multi-stage Docker image that compiles the full TypeScript workspace and executes the compiled runtime as a non-root node user, enabling secure, one-command deployment via Docker Compose.

This guide covers the production deployment of FreeLLMAPI from the tashfeenahmed/freellmapi repository. The containerized architecture isolates the Node.js 20 runtime, persists SQLite data through named volumes, and exposes a health-checked API gateway on port 3001.

Multi-Stage Dockerfile Architecture

The Dockerfile at the repository root implements a three-stage build pipeline to minimize image size and attack surface.

Stage 1: Dependency Installation (deps)

The deps stage starts from node:20-bookworm-slim and installs build-time system packages required for native module compilation.


# Installs Python, make, g++, then runs npm ci

This stage fetches all workspace dependencies using npm ci, ensuring reproducible builds by respecting the lockfile.

Stage 2: Compilation (build)

The build stage copies the full source tree and executes npm run build to transpile TypeScript into JavaScript. After compilation, it runs npm prune --omit=dev to strip development dependencies, reducing the final image footprint.

Stage 3: Production Runtime (runtime)

The final runtime stage creates a hardened execution environment:

  • Starts from a clean node:20-bookworm-slim image
  • Copies only production node_modules and compiled assets (server/dist, client/dist)
  • Creates a writable /app/server/data directory owned by the non-root node user
  • Sets environment variables: NODE_ENV=production, PORT=3001, FREELLMAPI_INSTALL_METHOD=docker
  • Defines a health-check that curls /api/ping every 30 seconds
  • Executes docker-entrypoint.sh to fix volume permissions before dropping privileges and running node server/dist/index.js

Docker Compose Configuration

The docker-compose.yml orchestrates the container with persistent storage and network isolation.

Volume Persistence

The compose file declares a named volume freellmapi-data mounted at /app/server/data. This preserves the SQLite database, encryption key files, and server logs across container restarts and image updates.

Network Binding and Security

By default, the service binds to 127.0.0.1:3001, restricting access to the local machine. To expose FreeLLMAPI across your LAN, modify the HOST_BIND environment variable to 0.0.0.0 in your .env file.

The configuration also injects host.docker.internal as an extra host, enabling outbound proxy URLs that resolve to the host machine when running on Linux Docker.

Step-by-Step Deployment

Deploy a production instance in five commands:

  1. Clone the repository
git clone https://github.com/tashfeenahmed/freellmapi.git
cd freellmapi
  1. Configure environment variables
cp .env.example .env

Edit .env and generate a secure 64-character hexadecimal encryption key:

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

Set this as ENCRYPTION_KEY in your .env file. Configure optional settings like PROXY_URL or HOST_BIND as needed.

  1. Build and start the container
docker compose up -d

This command builds the multi-stage image if necessary, creates the persistent volume, and starts the health-checked container in detached mode.

  1. Verify deployment health
docker compose ps
curl http://127.0.0.1:3001/api/ping

A successful deployment returns {"ok": true} from the ping endpoint and shows a "healthy" status in the container list.

  1. Access the dashboard

Navigate to http://127.0.0.1:3001 in your browser. If you changed HOST_BIND to 0.0.0.0, replace 127.0.0.1 with your host machine's IP address.

Data Persistence and Backup

All application state resides in /app/server/data inside the container. The docker-compose.yml maps this to the named volume freellmapi-data. To inspect or back up the SQLite database:


# Create a backup

docker cp freellmapi:/app/server/data ./freellmapi-backup

The docker-entrypoint.sh script ensures the node user owns this directory on startup, preventing permission errors when the container writes logs or database files.

Summary

  • FreeLLMAPI uses a hardened, multi-stage Dockerfile with separate deps, build, and runtime phases to minimize the production attack surface.
  • The container runs as the non-root node user and exposes port 3001 with a built-in health check on /api/ping.
  • Docker Compose manages a persistent named volume (freellmapi-data) that stores the SQLite database and logs at /app/server/data.
  • Default configuration binds to localhost (127.0.0.1:3001) for single-user security; change HOST_BIND to 0.0.0.0 for LAN access.
  • Deployment requires cloning the repository, configuring a 64-character ENCRYPTION_KEY in .env, and running docker compose up -d.

Frequently Asked Questions

How do I generate the required ENCRYPTION_KEY?

Run the Node.js one-liner provided in the setup instructions: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))". This outputs a 64-character hexadecimal string that you must paste into your .env file as the ENCRYPTION_KEY variable. The key encrypts sensitive data in the SQLite database and must be backed up; losing it renders stored credentials unrecoverable.

Where is the SQLite database stored inside the container?

The database resides in /app/server/data within the container filesystem. The docker-compose.yml mounts the named volume freellmapi-data to this path, ensuring data survives container restarts and image updates. You can access this directory using docker exec -it freellmapi /bin/sh or by copying files out with docker cp.

How do I expose FreeLLMAPI to other devices on my network?

Edit the .env file and set HOST_BIND=0.0.0.0, then restart the container with docker compose restart. By default, Docker Compose binds to 127.0.0.1:3001, which restricts access to the host machine. Changing this binds the port on all network interfaces, allowing other LAN devices to reach the API at http://<host-ip>:3001.

How do I upgrade FreeLLMAPI to a new version?

Pull the latest code with git pull origin main, then rebuild and restart: docker compose up -d --build. The named volume preserves your database and configuration, while the multi-stage build process compiles the updated TypeScript source. Verify the upgrade with docker compose ps and check the health status returns "healthy".

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 →