# How to Deploy FreeLLMAPI Using Docker Compose for Production

> Deploy FreeLLMAPI in production using Docker Compose. Secure your single-container service with persistent SQLite storage, host interface binding, and healthchecks for reliable operation.

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

---

**FreeLLMAPI can be deployed as a secure, single-container production service using Docker Compose by mounting a named volume for SQLite persistence, binding to specific host interfaces, and running healthchecks against the `/api/ping` endpoint.**

FreeLLMAPI provides a containerized deployment solution through the `tashfeenahmed/freellmapi` repository. When you deploy FreeLLMAPI using Docker Compose for production workloads, the configuration leverages a multi-stage build process, non-root execution, and environment isolation to protect provider API keys and ensure service reliability.

## Environment Configuration and Secrets

Before starting the container, you must prepare the environment variables that configure the encryption layer and server binding. The repository's `.dockerignore` explicitly excludes `.env` files from the build context to prevent accidental secret leakage into the image layers.

Generate a secure encryption key and create the environment file:

```bash
cd freellmapi
ENCRYPTION_KEY="$(openssl rand -hex 32)"
printf "ENCRYPTION_KEY=%s\nNODE_ENV=production\nPORT=3001\n" "$ENCRYPTION_KEY" > .env

```

The `ENCRYPTION_KEY` is critical for protecting stored provider credentials in the SQLite database. According to the source code in [`docker-compose.yml`](https://github.com/tashfeenahmed/freellmapi/blob/main/docker-compose.yml), the container expects these variables at runtime, not during the build phase.

## Docker Compose Production Configuration

The [`docker-compose.yml`](https://github.com/tashfeenahmed/freellmapi/blob/main/docker-compose.yml) file in the repository root defines a production service that pulls the multi-arch image `ghcr.io/tashfeenahmed/freellmapi:latest` or builds from the local `Dockerfile`. Key production settings include:

- **Image Source**: Uses the GitHub Container Registry image or builds from the multi-stage Dockerfile located at `./Dockerfile`
- **Runtime Environment**: Explicitly sets `NODE_ENV=production` to disable development features
- **Port Binding**: Exposes container port 3001 to the host
- **Non-root Execution**: The container runs as the `node` user (UID 1000), with the data directory owned by that user

The compose configuration also includes an `extra_hosts` entry mapping `host.docker.internal` to the host gateway, enabling the container to reach proxy services running on the host machine without using the Docker host network.

## Networking and Security Controls

By default, FreeLLMAPI binds to `127.0.0.1` (localhost only) through the `HOST_BIND` environment variable. This default configuration ensures the API remains single-user and unreachable from external networks, which is critical when running LLM API keys on personal infrastructure.

To expose the service to your local network (trusted environments only), override the bind address:

```bash
HOST_BIND=0.0.0.0 docker compose up -d

```

For localhost-only access (default secure configuration):

```bash
docker compose up -d

```

The `Dockerfile` implements security hardening by creating a dedicated `node` user and ensuring the application directory at `/app` has appropriate ownership, preventing privilege escalation vulnerabilities.

## Data Persistence and Volume Management

FreeLLMAPI stores encrypted provider keys and configuration in a SQLite database located at `/app/server/data`. The [`docker-compose.yml`](https://github.com/tashfeenahmed/freellmapi/blob/main/docker-compose.yml) declares a named volume `freellmapi-data` mapped to this path to ensure data survives container restarts and image upgrades.

Volume configuration in the compose file:

- **Volume Name**: `freellmapi-data`
- **Container Path**: `/app/server/data`
- **Purpose**: Preserves encrypted SQLite database across container recreation

This separation of data from the container filesystem means you can update the FreeLLMAPI image with `docker compose pull && docker compose up -d` without losing configured provider credentials.

## Healthchecks and Monitoring

Both the `Dockerfile` and [`docker-compose.yml`](https://github.com/tashfeenahmed/freellmapi/blob/main/docker-compose.yml) implement a Node.js-based healthcheck that verifies the Express server is responding correctly. The healthcheck executes:

```bash
node -e "fetch('http://127.0.0.1:3001/api/ping').then(r=>r.ok||process.exit(1)).catch(()=>process.exit(1))"

```

Docker marks the container as unhealthy if this endpoint fails, enabling orchestration systems to restart the service automatically.

To monitor logs and verify deployment status:

```bash
docker compose logs -f freellmapi

```

## Step-by-Step Production Deployment

Complete the following steps to deploy FreeLLMAPI using Docker Compose for production:

1. **Clone the repository and navigate to the project directory**:

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

```

2. **Create the environment file with encryption key**:

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

```

3. **Start the service in detached mode**:

```bash
docker compose up -d

```

4. **Verify the healthcheck manually** (optional diagnostic):

```bash
docker compose exec freellmapi node -e "fetch('http://127.0.0.1:3001/api/ping').then(r=>console.log('Status:', r.status)).catch(e=>console.error('Health check failed'))"

```

5. **Expose to LAN** (if needed for network access):

```bash
docker compose down
HOST_BIND=0.0.0.0 docker compose up -d

```

## Summary

Deploying FreeLLMAPI in production using Docker Compose provides a reproducible, isolated environment with the following characteristics:

- **Secure by default**: Runs as non-root `node` user with localhost-only binding (`127.0.0.1`)
- **Persistent storage**: Uses named volume `freellmapi-data` mounted at `/app/server/data` for SQLite encryption keys
- **Production hardened**: Multi-stage build excludes development dependencies, `.dockerignore` prevents secret leakage
- **Health monitored**: Built-in healthcheck polls `/api/ping` to verify service availability
- **Network flexible**: Supports `host.docker.internal` for proxy access and configurable `HOST_BIND` for LAN exposure

## Frequently Asked Questions

### How do I backup the FreeLLMAPI SQLite database?

The SQLite database resides in the Docker volume mounted at `/app/server/data`. To backup, use `docker compose cp` to copy the file from the running container: `docker compose cp freellmapi:/app/server/data ./backup-$(date +%Y%m%d)`. Alternatively, backup the named volume `freellmapi-data` directly using standard Docker volume backup procedures.

### Can I use a pre-built image instead of building locally?

Yes. The [`docker-compose.yml`](https://github.com/tashfeenahmed/freellmapi/blob/main/docker-compose.yml) references `ghcr.io/tashfeenahmed/freellmapi:latest` by default. You can pull this image directly without cloning the repository, though you still need the [`docker-compose.yml`](https://github.com/tashfeenahmed/freellmapi/blob/main/docker-compose.yml) file and an `.env` file with your `ENCRYPTION_KEY` to run the container.

### Why does the container default to localhost-only binding?

The `HOST_BIND` environment variable defaults to `127.0.0.1` in the application configuration to prevent accidental exposure of API endpoints to the public internet. This security measure ensures that without explicitly setting `HOST_BIND=0.0.0.0`, the service remains accessible only from the host machine, protecting your LLM provider API keys from network-based attacks.

### How do I update FreeLLMAPI to a newer version?

Pull the latest image and recreate the container while preserving your data volume: `docker compose pull && docker compose up -d`. The named volume `freellmapi-data` persists your encrypted provider configuration, so the new container instance retains all settings after the update.