FreeLLMAPI / docs /install.sh
Nryn215's picture
Upload folder using huggingface_hub
077865a verified
Raw
History Blame Contribute Delete
5.75 kB
#!/usr/bin/env bash
#
# FreeLLMAPI one-line installer (#250, idea by @StealthTensor).
#
# curl -fsSL https://tashfeenahmed.github.io/freellmapi/install.sh | bash
#
# What it does:
# 1. Checks for Docker + Compose.
# 2. Creates an install dir (default ~/freellmapi, override FREELLMAPI_DIR).
# 3. Writes a docker-compose.yml pinned to ghcr.io/tashfeenahmed/freellmapi:latest
# and a .env with a freshly generated ENCRYPTION_KEY (kept on re-runs).
# 4. Pulls the image, starts the container, waits for the health endpoint.
#
# Options (env vars):
# FREELLMAPI_DIR install directory (default: ~/freellmapi)
# PORT host port for the dashboard (default: 3001)
# HOST_BIND host interface to publish on (default: 127.0.0.1).
# Set HOST_BIND=0.0.0.0 to reach it from your LAN β€” only on
# a trusted network; the proxy is single-user.
#
# Re-running is safe: existing .env (and your encryption key) is preserved,
# the compose file is refreshed, and the container is updated to :latest.
set -euo pipefail
INSTALL_DIR="${FREELLMAPI_DIR:-$HOME/freellmapi}"
PORT="${PORT:-3001}"
HOST_BIND="${HOST_BIND:-127.0.0.1}"
IMAGE="ghcr.io/tashfeenahmed/freellmapi:latest"
say() { printf '\033[1;32m==>\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33mWARN\033[0m %s\n' "$*" >&2; }
die() { printf '\033[1;31mERROR\033[0m %s\n' "$*" >&2; exit 1; }
# ── 1. prerequisites ─────────────────────────────────────────────────────────
command -v docker >/dev/null 2>&1 || die "Docker is required. Install it from https://docs.docker.com/get-docker/ and re-run."
if docker compose version >/dev/null 2>&1; then
COMPOSE=(docker compose)
elif command -v docker-compose >/dev/null 2>&1; then
COMPOSE=(docker-compose)
else
die "Docker Compose is required (the 'docker compose' plugin or 'docker-compose')."
fi
docker info >/dev/null 2>&1 || die "Docker daemon isn't running (or you lack permission). Start Docker and re-run."
# ── 2. install dir ───────────────────────────────────────────────────────────
mkdir -p "$INSTALL_DIR"
cd "$INSTALL_DIR"
say "Installing into $INSTALL_DIR"
# ── 3. .env (generated once β€” your at-rest encryption key lives here) ────────
if [ -f .env ]; then
say "Keeping existing .env (encryption key preserved)"
else
if command -v openssl >/dev/null 2>&1; then
KEY="$(openssl rand -hex 32)"
else
# /dev/urandom fallback when openssl isn't installed
KEY="$(od -vN 32 -An -tx1 /dev/urandom | tr -d ' \n')"
fi
umask 077
cat > .env <<EOF
# Generated by install.sh on $(date -u +%Y-%m-%dT%H:%M:%SZ)
# At-rest encryption key for your stored provider keys. Losing it means
# re-entering your provider keys, so back this file up.
ENCRYPTION_KEY=$KEY
PORT=$PORT
HOST_BIND=$HOST_BIND
EOF
umask 022
say "Wrote .env with a fresh ENCRYPTION_KEY"
fi
# ── 4. compose file (image-only; refreshed on every run) ─────────────────────
cat > docker-compose.yml <<EOF
# Written by install.sh β€” safe to edit; re-running the installer rewrites it.
services:
freellmapi:
image: $IMAGE
env_file:
- .env
environment:
NODE_ENV: production
PORT: 3001
# Bound to localhost by default β€” FreeLLMAPI is single-user and must not
# be exposed to the internet. Set HOST_BIND=0.0.0.0 in .env for LAN access.
ports:
- "\${HOST_BIND:-127.0.0.1}:\${PORT:-3001}:3001"
volumes:
- freellmapi-data:/app/server/data
restart: unless-stopped
healthcheck:
test:
[
"CMD",
"node",
"-e",
"fetch('http://127.0.0.1:3001/api/ping').then((res) => { if (!res.ok) process.exit(1); }).catch(() => process.exit(1));"
]
interval: 30s
timeout: 5s
start_period: 15s
retries: 3
volumes:
freellmapi-data:
EOF
# ── 5. pull + start ──────────────────────────────────────────────────────────
say "Pulling $IMAGE"
"${COMPOSE[@]}" pull --quiet 2>/dev/null || "${COMPOSE[@]}" pull
say "Starting FreeLLMAPI"
"${COMPOSE[@]}" up -d
# ── 6. wait for health ───────────────────────────────────────────────────────
say "Waiting for the server to come up"
HEALTH_HOST="127.0.0.1"
[ "$HOST_BIND" != "0.0.0.0" ] && [ "$HOST_BIND" != "127.0.0.1" ] && HEALTH_HOST="$HOST_BIND"
for i in $(seq 1 30); do
if curl -fsS "http://$HEALTH_HOST:$PORT/api/ping" >/dev/null 2>&1; then
HEALTHY=1
break
fi
sleep 2
done
if [ "${HEALTHY:-0}" = "1" ]; then
cat <<EOF
FreeLLMAPI is running.
Dashboard http://localhost:$PORT
Next steps add provider keys on the Keys page, then grab your
unified API key from the page header β€” that's the
Bearer token your OpenAI-compatible client uses.
API endpoint http://localhost:$PORT/v1
Update cd $INSTALL_DIR && docker compose pull && docker compose up -d
Logs cd $INSTALL_DIR && docker compose logs -f
Uninstall cd $INSTALL_DIR && docker compose down -v && cd .. && rm -rf $INSTALL_DIR
EOF
else
warn "Container started but the health endpoint didn't answer within 60s."
warn "Check logs with: cd $INSTALL_DIR && ${COMPOSE[*]} logs -f"
exit 1
fi