Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ RUN chmod +x /entrypoint.sh

ENV PORT=8090

HEALTHCHECK --interval=5s --timeout=3s --start-period=60s \
HEALTHCHECK --interval=5s --timeout=3s --start-period=180s \
CMD curl -sf http://127.0.0.1:${PORT:-8090}/health || exit 1

EXPOSE 8090
Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ SDK requests hit the localaik proxy, which translates Gemini, OpenAI, or Anthrop
docker run -d -p 8090:8090 gokhalh/localaik
```

The port starts accepting connections before the model has loaded, so wait for `GET /health` to return 200 rather than for the port to open. A TCP liveness check will let requests through too early. Docker Compose users want `condition: service_healthy`.

Or with Docker Compose:

```yaml
Expand Down Expand Up @@ -207,8 +209,9 @@ clients send to localaik are still discarded and never forwarded. It is attached
only to requests whose host matches `LK_UPSTREAM`, and while it is set a
redirect from your upstream is returned to the caller rather than followed.

`/health` returns 503 until your upstream answers, so existing healthchecks and
CI wait loops work unchanged.
`/health` returns 503 until your upstream answers, so any wait loop that polls it
for a 200 works unchanged. As with the model-bundled tags, the port opens before
the upstream is reachable, so a TCP liveness check is not enough.

### Security

Expand Down Expand Up @@ -406,5 +409,6 @@ docker build --target proxy -t gokhalh/localaik:proxy .
- Intended for tests and development, not production
- Image size is dominated by model weights (not applicable to the `proxy` tag, which ships none)
- Cold starts can take tens of seconds while the model loads
- `/health` reports 503 during that window, and the container keeps running rather than exiting if the model never finishes loading
- PDF rendering adds latency per page

57 changes: 36 additions & 21 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
#!/bin/sh
set -eu

cleanup() {
jobs -p | xargs -r kill 2>/dev/null || true
wait || true
}

trap 'cleanup; exit 0' INT TERM

LLAMA_SERVER_BIN="${LLAMA_SERVER_BIN:-/app/llama-server}"
if [ ! -x "${LLAMA_SERVER_BIN}" ]; then
if command -v llama-server >/dev/null 2>&1; then
Expand Down Expand Up @@ -35,23 +28,45 @@ SERVER_ARGS="${SERVER_ARGS} --ctx-size ${LK_CTX_SIZE:-8192}"
[ "${LK_CONT_BATCHING:-0}" = "1" ] && SERVER_ARGS="${SERVER_ARGS} --cont-batching"
[ "${LK_MLOCK:-0}" = "1" ] && SERVER_ARGS="${SERVER_ARGS} --mlock"

LLAMA_PID=""
PROXY_PID=""

# Armed before either child exists, and iterating so an unset PID cannot make
# kill swallow the other one.
trap 'for p in ${LLAMA_PID} ${PROXY_PID}; do kill "${p}" 2>/dev/null || true; done; exit 0' INT TERM

# shellcheck disable=SC2086
"${LLAMA_SERVER_BIN}" ${SERVER_ARGS} &
LLAMA_PID=$!

echo "localaik: loading model..."
tries=0
until curl -sf http://127.0.0.1:8080/health >/dev/null 2>&1; do
tries=$((tries + 1))
if [ "${tries}" -ge 120 ]; then
echo "localaik: model failed to load after 120s" >&2
cleanup
exit 1
fi
localaik --port "${PORT:-8090}" --upstream "http://127.0.0.1:8080/v1" &
PROXY_PID=$!

echo "localaik: supervising; /health reports 503 until the model is ready"

while kill -0 "${LLAMA_PID}" 2>/dev/null && kill -0 "${PROXY_PID}" 2>/dev/null; do
sleep 1
done

echo "localaik: model ready"
echo "localaik: listening on port ${PORT:-8090}"
exec localaik \
--port "${PORT:-8090}" \
--upstream "http://127.0.0.1:8080/v1"
if kill -0 "${LLAMA_PID}" 2>/dev/null; then
died="localaik"
dead_pid="${PROXY_PID}"
survivor="${LLAMA_PID}"
else
died="llama-server"
dead_pid="${LLAMA_PID}"
survivor="${PROXY_PID}"
fi

kill "${survivor}" 2>/dev/null || true
# Waited so its last log lines reach docker logs before the container tears down.
wait "${survivor}" 2>/dev/null || true

status=0
wait "${dead_pid}" || status=$?
if [ "${status}" -eq 0 ]; then
status=1
fi

echo "localaik: ${died} exited with status ${status}, stopping the container" >&2
exit "${status}"
Loading
Loading