# How to Self-Host FreeLLMAPI Using Docker and Docker Compose

> Easily self-host FreeLLMAPI with Docker and Docker Compose. Deploy securely and efficiently in one command for a streamlined experience.

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

---

**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.

```dockerfile

# 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`](https://github.com/tashfeenahmed/freellmapi/blob/main/docker-entrypoint.sh) to fix volume permissions before dropping privileges and running `node server/dist/index.js`

## Docker Compose Configuration

The [`docker-compose.yml`](https://github.com/tashfeenahmed/freellmapi/blob/main/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**

```bash
git clone https://github.com/tashfeenahmed/freellmapi.git
cd freellmapi

```

2. **Configure environment variables**

```bash
cp .env.example .env

```

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

```bash
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.

3. **Build and start the container**

```bash
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.

4. **Verify deployment health**

```bash
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.

5. **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`](https://github.com/tashfeenahmed/freellmapi/blob/main/docker-compose.yml) maps this to the named volume `freellmapi-data`. To inspect or back up the SQLite database:

```bash

# Create a backup

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

```

The [`docker-entrypoint.sh`](https://github.com/tashfeenahmed/freellmapi/blob/main/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`](https://github.com/tashfeenahmed/freellmapi/blob/main/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".