Spaces:
Runtime error
Runtime error
File size: 5,748 Bytes
077865a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | #!/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
|