Docker templatekdeps asset
kdeps-docker-image
v1.0.0kdeps dockerfile: image
Install
A kdeps asset: kdeps installs it on first run and updates it without a new release.
kdeps update docker/image
Pin a version with kdeps update docker/image@<version>.
Contents
alpine.Dockerfile.tmpl
FROM {{ .BaseImage }}
# Set environment variables
ENV PYTHONUNBUFFERED=1 \
PATH=/root/.local/bin:/opt/venv/bin:$PATH \
KDEPS_BIND_HOST=0.0.0.0 \
KDEPS_ASSETS_DIR=/app/.kdeps/assets \
{{- if .LlamafileModels }}
KDEPS_MODELS_DIR=/app/.kdeps/models \
{{- end }}
{{- if .InstallOllama }}
OLLAMA_HOST=127.0.0.1 \
OLLAMA_PORT={{ .BackendPort }} \
OLLAMA_MODELS=/app/.ollama/models \
{{- end }}
BACKEND_PORT={{ .BackendPort }}
{{- if .Env }}
# User-defined environment variables
{{- range $key, $value := .Env }}
ENV {{ $key }}="{{ $value }}"
{{- end }}
{{- end }}
# Install base dependencies (upgrade first to pick up security patches
# published after the base image was built)
RUN apk upgrade --no-cache && \
apk add --no-cache \
zstd \
python3 \
py3-pip \
curl \
wget \
bash \
supervisor \
ca-certificates \
libstdc++ \
ncurses \
rsync
{{- if .UsePrepackagedBinary }}
# Install prepackaged kdeps binary (self-contained, workflow embedded).
# TARGETARCH is set automatically by `docker buildx build --platform`; for a
# plain `docker build` it falls back to the host arch via `uname -m`.
ARG TARGETARCH
{{- if .PrepackagedAMD64 }}
COPY kdeps-linux-amd64 /tmp/kdeps-linux-amd64
{{- end }}
{{- if .PrepackagedARM64 }}
COPY kdeps-linux-arm64 /tmp/kdeps-linux-arm64
{{- end }}
RUN ARCH="${TARGETARCH:-$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')}" && \
if [ -f "/tmp/kdeps-linux-${ARCH}" ]; then \
mv "/tmp/kdeps-linux-${ARCH}" /usr/local/bin/kdeps; \
else \
curl -LsSf https://raw.githubusercontent.com/kdeps/kdeps/{{ .InstallerRef }}/install.sh | sh -s -- -b /usr/local/bin {{ .InstallerRef }}; \
fi && \
rm -f /tmp/kdeps-linux-amd64 /tmp/kdeps-linux-arm64 && \
chmod +x /usr/local/bin/kdeps
{{- else }}
# Install kdeps binary
RUN curl -LsSf https://raw.githubusercontent.com/kdeps/kdeps/{{ .InstallerRef }}/install.sh | sh -s -- -b /usr/local/bin {{ .InstallerRef }}
{{- end }}
{{- if .OSPackages }}
# Install OS packages
RUN apk add --no-cache{{ range .OSPackages }} {{ . }}{{ end }}
{{- end }}
# Install LLM backend
{{ .BackendInstall }}
{{- if .InstallUV }}
# Install uv for Python package management
COPY --from=ghcr.io/astral-sh/uv:{{ .UVImageTag }} /uv /usr/local/bin/uv
RUN chmod +x /usr/local/bin/uv
# Create virtual environment
RUN /usr/local/bin/uv venv /opt/venv
{{- end }}
{{- if .PythonPackages }}
# Install Python packages
RUN /usr/local/bin/uv pip install{{ range .PythonPackages }} {{ . }}{{ end }}
{{- end }}
{{- if .RequirementsFile }}
# Install from requirements file
COPY {{ .RequirementsFile }} /tmp/requirements.txt
RUN /usr/local/bin/uv pip install -r /tmp/requirements.txt
{{- end }}
# kdeps assets (harness, tools, model registries, ...) from the build host,
# so the container needs no download at start
COPY kdeps-assets/ /app/.kdeps/assets/
# Copy workflow files
{{- if not .UsePrepackagedBinary }}
COPY workflow.yaml /app/workflow.yaml
{{- if .HasResources }}
COPY resources/ /app/resources/
{{- end }}
{{- if .HasData }}
COPY data/ /app/data/
{{- end }}
{{- end }}
# Copy entrypoint and supervisor config
COPY entrypoint.sh /entrypoint.sh
COPY supervisord.conf /etc/supervisord.conf
RUN chmod +x /entrypoint.sh
# Ensure log directory exists
RUN mkdir -p /var/log
{{- if and .OfflineMode .Models .InstallOllama }}
# Download models during build time for offline mode
RUN mkdir -p /llm
# Download Ollama models
RUN (OLLAMA_HOST=127.0.0.1 /usr/local/bin/ollama serve &) && \
sleep 10 && \
{{- range $i, $model := .Models }}{{ if $i }} && \{{ end }}
OLLAMA_HOST=127.0.0.1 /usr/local/bin/ollama pull {{ $model }}{{ end }} && \
pkill -f ollama && \
sleep 2 && \
# Copy to the image layer
cp -r /root/.ollama/* /llm/
{{- end }}
{{- if .LlamafileModels }}
# Pre-bake llamafile models (file backend)
RUN mkdir -p /app/.kdeps/models && \
{{- range .LlamafileModels }}
curl -fL --retry 3 -o /app/.kdeps/models/{{ .File }} "{{ .URL }}" && \
chmod +x /app/.kdeps/models/{{ .File }} && \
{{- end }}
true
{{- end }}
WORKDIR /app
# Run as non-root (supervisord + kdeps; Ollama models live under /app/.ollama).
RUN addgroup -S kdeps && adduser -S -G kdeps kdeps && \
{{- if .InstallOllama }}
mkdir -p /app/.ollama/models && \
{{- end }}
chown -R kdeps:kdeps /app /var/log /entrypoint.sh /etc/supervisord.conf
USER kdeps
# Expose ports
{{- if .HasAPIServer }}
EXPOSE {{ .APIPort }}
{{- end }}
{{- if .HasWebServer }}
EXPOSE {{ .WebServerPort }}
{{- end }}
{{- if .InstallOllama }}
EXPOSE {{ .BackendPort }}
{{- end }}
{{- if .HasAPIServer }}
# Healthcheck (API server serves /health)
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD wget -qO- http://localhost:{{ .APIPort }}/health || exit 1
{{- else if .HasWebServer }}
# Healthcheck (web server has no /health endpoint; check the TCP socket)
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD bash -c 'exec 3<>/dev/tcp/127.0.0.1/{{ .WebServerPort }}' || exit 1
{{- end }}
# Use entrypoint for backend management
ENTRYPOINT ["/entrypoint.sh"]
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
backend_install.tmpl
{{- if .InstallOllama }}
# Ollama included in base image
{{- else }}
# No LLM backend to install
{{- end }}
entrypoint.sh.tmpl
#!/bin/bash
set -euo pipefail
echo "=== KDeps Docker Container Starting ==="
{{- if .InstallOllama }}
echo "Ollama enabled on port: ${BACKEND_PORT:-11434}"
{{- else if .LlamafileModels }}
echo "LLM backend: file (llamafile, self-serving)"
{{- else }}
echo "No local LLM backend installed"
{{- end }}
{{- if .InstallOllama }}
# Function to wait for Ollama to be ready
wait_for_ollama() {
local port=$1
local max_attempts=30
local attempt=0
echo "Waiting for Ollama to be ready on port $port..."
while [ $attempt -lt $max_attempts ]; do
if curl -s http://localhost:$port/api/tags >/dev/null 2>&1; then
echo "Ollama is ready!"
return 0
fi
attempt=$((attempt + 1))
echo "Attempt $attempt/$max_attempts: Ollama not ready yet..."
sleep 2
done
echo "WARNING: Ollama did not become ready after $max_attempts attempts"
return 1
}
{{- end }}
# Handle models based on offline mode
{{- if and .OfflineMode .Models .InstallOllama }}
if [ -d "/llm/models" ]; then
echo "Offline mode: Copying pre-downloaded models to Ollama directory..."
MODELS_DIR="${OLLAMA_MODELS:-/app/.ollama/models}"
mkdir -p "$MODELS_DIR"
rsync -avrPtz --human-readable /llm/models/. "$MODELS_DIR/" || \
cp -r /llm/models/* "$MODELS_DIR/" 2>/dev/null || true
echo "Ollama models copied successfully"
else
echo "WARNING: No pre-downloaded Ollama models found in /llm/models"
fi
{{- else if and .Models .InstallOllama }}
# Download models if specified (not in offline mode)
echo "Pre-pulling Ollama models..."
# Start Ollama temporarily in the background if it's not already running
# so we can pull models during the entrypoint phase.
TEMP_OLLAMA_PID=""
if ! curl -s http://localhost:${BACKEND_PORT:-11434}/api/tags >/dev/null 2>&1; then
echo "Starting Ollama server temporarily for model pulling..."
/usr/local/bin/ollama serve > /tmp/ollama_entrypoint.log 2>&1 &
TEMP_OLLAMA_PID=$!
wait_for_ollama ${BACKEND_PORT:-11434}
fi
{{- range .Models }}
/usr/local/bin/ollama pull {{ . }} || echo "Failed to pull {{ . }}, will retry at runtime"
{{- end }}
# Kill the temporary Ollama process if we started one
if [ -n "$TEMP_OLLAMA_PID" ]; then
echo "Stopping temporary Ollama server..."
kill $TEMP_OLLAMA_PID || true
sleep 2
fi
{{- end }}
# Ensure the workflow binds to all interfaces inside containers
# (overrides any hostIp: "127.0.0.1" in the workflow YAML)
if [ -f /app/workflow.yaml ]; then
sed -i 's/hostIp: "127.0.0.1"/hostIp: "0.0.0.0"/g' /app/workflow.yaml
sed -i 's/hostIp: 127.0.0.1/hostIp: 0.0.0.0/g' /app/workflow.yaml
fi
# Execute the command passed to docker run
exec "$@"
supervisord.conf.tmpl
[supervisord]
nodaemon=true
logfile=/var/log/supervisord.log
pidfile=/tmp/supervisord.pid
loglevel=info
{{- if .InstallOllama }}
[program:ollama]
command=/usr/local/bin/ollama serve
autostart=true
autorestart=true
startretries=10
stderr_logfile=/var/log/ollama.err.log
stdout_logfile=/var/log/ollama.out.log
priority=10
stopwaitsecs=30
environment=OLLAMA_HOST="127.0.0.1",OLLAMA_MODELS="/app/.ollama/models"
{{- end }}
[program:kdeps]
{{- if .UsePrepackagedBinary }}
command=/usr/local/bin/kdeps
{{- else }}
command=/usr/local/bin/kdeps run /app/workflow.yaml
{{- end }}
autostart=true
autorestart=true
startretries=3
stderr_logfile=/var/log/kdeps.err.log
stdout_logfile=/var/log/kdeps.out.log
priority=20
environment=KDEPS_BIND_HOST="0.0.0.0"
{{- if .InstallOllama }}
depends_on=ollama
{{- end }}
ubuntu.Dockerfile.tmpl
FROM {{ .BaseImage }}
# Set environment variables
ENV DEBIAN_FRONTEND=noninteractive \
PYTHONUNBUFFERED=1 \
PATH=/opt/venv/bin:$PATH \
KDEPS_BIND_HOST=0.0.0.0 \
KDEPS_ASSETS_DIR=/app/.kdeps/assets \
{{- if .LlamafileModels }}
KDEPS_MODELS_DIR=/app/.kdeps/models \
{{- end }}
{{- if .InstallOllama }}
OLLAMA_HOST=127.0.0.1 \
OLLAMA_PORT={{ .BackendPort }} \
OLLAMA_MODELS=/app/.ollama/models \
{{- end }}
BACKEND_PORT={{ .BackendPort }}
{{- if .Env }}
# User-defined environment variables
{{- range $key, $value := .Env }}
ENV {{ $key }}="{{ $value }}"
{{- end }}
{{- end }}
# Install base dependencies (upgrade first to pick up security patches
# published after the base image was built)
RUN apt-get update && \
apt-get upgrade -y && \
apt-get install -y \
zstd \
python3 \
python3-venv \
python3-pip \
curl \
wget \
ca-certificates \
supervisor \
bash \
rsync && \
rm -rf /var/lib/apt/lists/*
{{- if .UsePrepackagedBinary }}
# Install prepackaged kdeps binary (self-contained, workflow embedded).
# TARGETARCH is set automatically by `docker buildx build --platform`; for a
# plain `docker build` it falls back to the host arch via `uname -m`.
ARG TARGETARCH
{{- if .PrepackagedAMD64 }}
COPY kdeps-linux-amd64 /tmp/kdeps-linux-amd64
{{- end }}
{{- if .PrepackagedARM64 }}
COPY kdeps-linux-arm64 /tmp/kdeps-linux-arm64
{{- end }}
RUN ARCH="${TARGETARCH:-$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')}" && \
if [ -f "/tmp/kdeps-linux-${ARCH}" ]; then \
mv "/tmp/kdeps-linux-${ARCH}" /usr/local/bin/kdeps; \
else \
curl -LsSf https://raw.githubusercontent.com/kdeps/kdeps/{{ .InstallerRef }}/install.sh | sh -s -- -b /usr/local/bin {{ .InstallerRef }}; \
fi && \
rm -f /tmp/kdeps-linux-amd64 /tmp/kdeps-linux-arm64 && \
chmod +x /usr/local/bin/kdeps
{{- else }}
# Install kdeps binary
RUN curl -LsSf https://raw.githubusercontent.com/kdeps/kdeps/{{ .InstallerRef }}/install.sh | sh -s -- -b /usr/local/bin {{ .InstallerRef }}
{{- end }}
{{- if .OSPackages }}
# Install OS packages
RUN apt-get update && \
apt-get install -y{{ range .OSPackages }} {{ . }}{{ end }} && \
rm -rf /var/lib/apt/lists/*
{{- end }}
# Install LLM backend
{{ .BackendInstall }}
{{- if .InstallUV }}
# Install uv for Python package management
COPY --from=ghcr.io/astral-sh/uv:{{ .UVImageTag }} /uv /usr/local/bin/uv
RUN chmod +x /usr/local/bin/uv
# Create virtual environment
RUN /usr/local/bin/uv venv /opt/venv
{{- end }}
{{- if .PythonPackages }}
# Install Python packages
RUN /usr/local/bin/uv pip install{{ range .PythonPackages }} {{ . }}{{ end }}
{{- end }}
{{- if .RequirementsFile }}
# Install from requirements file
COPY {{ .RequirementsFile }} /tmp/requirements.txt
RUN /usr/local/bin/uv pip install -r /tmp/requirements.txt
{{- end }}
# kdeps assets (harness, tools, model registries, ...) from the build host,
# so the container needs no download at start
COPY kdeps-assets/ /app/.kdeps/assets/
# Copy workflow files
{{- if not .UsePrepackagedBinary }}
COPY workflow.yaml /app/workflow.yaml
{{- if .HasResources }}
COPY resources/ /app/resources/
{{- end }}
{{- if .HasData }}
COPY data/ /app/data/
{{- end }}
{{- end }}
# Copy entrypoint and supervisor config
COPY entrypoint.sh /entrypoint.sh
COPY supervisord.conf /etc/supervisord.conf
RUN chmod +x /entrypoint.sh
# Ensure log directory exists
RUN mkdir -p /var/log
{{- if and .OfflineMode .Models .InstallOllama }}
# Download models during build time for offline mode
RUN mkdir -p /llm
# Download Ollama models
RUN (OLLAMA_HOST=127.0.0.1 /usr/local/bin/ollama serve &) && \
sleep 10 && \
{{- range $i, $model := .Models }}{{ if $i }} && \{{ end }}
OLLAMA_HOST=127.0.0.1 /usr/local/bin/ollama pull {{ $model }}{{ end }} && \
pkill -f ollama && \
sleep 2 && \
# Copy to the image layer
cp -r /root/.ollama/* /llm/
{{- end }}
{{- if .LlamafileModels }}
# Pre-bake llamafile models (file backend)
RUN mkdir -p /app/.kdeps/models && \
{{- range .LlamafileModels }}
curl -fL --retry 3 -o /app/.kdeps/models/{{ .File }} "{{ .URL }}" && \
chmod +x /app/.kdeps/models/{{ .File }} && \
{{- end }}
true
{{- end }}
WORKDIR /app
# Run as non-root (supervisord + kdeps; Ollama models live under /app/.ollama).
RUN groupadd -r kdeps && useradd -r -g kdeps -d /app -s /usr/sbin/nologin kdeps && \
{{- if .InstallOllama }}
mkdir -p /app/.ollama/models && \
{{- end }}
chown -R kdeps:kdeps /app /var/log /entrypoint.sh /etc/supervisord.conf
USER kdeps
# Expose ports
{{- if .HasAPIServer }}
EXPOSE {{ .APIPort }}
{{- end }}
{{- if .HasWebServer }}
EXPOSE {{ .WebServerPort }}
{{- end }}
{{- if .InstallOllama }}
EXPOSE {{ .BackendPort }}
{{- end }}
{{- if .HasAPIServer }}
# Healthcheck (API server serves /health)
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD wget -qO- http://localhost:{{ .APIPort }}/health || exit 1
{{- else if .HasWebServer }}
# Healthcheck (web server has no /health endpoint; check the TCP socket)
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD bash -c 'exec 3<>/dev/tcp/127.0.0.1/{{ .WebServerPort }}' || exit 1
{{- end }}
# Use entrypoint for backend management
ENTRYPOINT ["/entrypoint.sh"]
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
version: 1.0.0
files:
alpine.Dockerfile.tmpl: |
FROM {{ .BaseImage }}
# Set environment variables
ENV PYTHONUNBUFFERED=1 \
PATH=/root/.local/bin:/opt/venv/bin:$PATH \
KDEPS_BIND_HOST=0.0.0.0 \
KDEPS_ASSETS_DIR=/app/.kdeps/assets \
{{- if .LlamafileModels }}
KDEPS_MODELS_DIR=/app/.kdeps/models \
{{- end }}
{{- if .InstallOllama }}
OLLAMA_HOST=127.0.0.1 \
OLLAMA_PORT={{ .BackendPort }} \
OLLAMA_MODELS=/app/.ollama/models \
{{- end }}
BACKEND_PORT={{ .BackendPort }}
{{- if .Env }}
# User-defined environment variables
{{- range $key, $value := .Env }}
ENV {{ $key }}="{{ $value }}"
{{- end }}
{{- end }}
# Install base dependencies (upgrade first to pick up security patches
# published after the base image was built)
RUN apk upgrade --no-cache && \
apk add --no-cache \
zstd \
python3 \
py3-pip \
curl \
wget \
bash \
supervisor \
ca-certificates \
libstdc++ \
ncurses \
rsync
{{- if .UsePrepackagedBinary }}
# Install prepackaged kdeps binary (self-contained, workflow embedded).
# TARGETARCH is set automatically by `docker buildx build --platform`; for a
# plain `docker build` it falls back to the host arch via `uname -m`.
ARG TARGETARCH
{{- if .PrepackagedAMD64 }}
COPY kdeps-linux-amd64 /tmp/kdeps-linux-amd64
{{- end }}
{{- if .PrepackagedARM64 }}
COPY kdeps-linux-arm64 /tmp/kdeps-linux-arm64
{{- end }}
RUN ARCH="${TARGETARCH:-$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')}" && \
if [ -f "/tmp/kdeps-linux-${ARCH}" ]; then \
mv "/tmp/kdeps-linux-${ARCH}" /usr/local/bin/kdeps; \
else \
curl -LsSf https://raw.githubusercontent.com/kdeps/kdeps/{{ .InstallerRef }}/install.sh | sh -s -- -b /usr/local/bin {{ .InstallerRef }}; \
fi && \
rm -f /tmp/kdeps-linux-amd64 /tmp/kdeps-linux-arm64 && \
chmod +x /usr/local/bin/kdeps
{{- else }}
# Install kdeps binary
RUN curl -LsSf https://raw.githubusercontent.com/kdeps/kdeps/{{ .InstallerRef }}/install.sh | sh -s -- -b /usr/local/bin {{ .InstallerRef }}
{{- end }}
{{- if .OSPackages }}
# Install OS packages
RUN apk add --no-cache{{ range .OSPackages }} {{ . }}{{ end }}
{{- end }}
# Install LLM backend
{{ .BackendInstall }}
{{- if .InstallUV }}
# Install uv for Python package management
COPY --from=ghcr.io/astral-sh/uv:{{ .UVImageTag }} /uv /usr/local/bin/uv
RUN chmod +x /usr/local/bin/uv
# Create virtual environment
RUN /usr/local/bin/uv venv /opt/venv
{{- end }}
{{- if .PythonPackages }}
# Install Python packages
RUN /usr/local/bin/uv pip install{{ range .PythonPackages }} {{ . }}{{ end }}
{{- end }}
{{- if .RequirementsFile }}
# Install from requirements file
COPY {{ .RequirementsFile }} /tmp/requirements.txt
RUN /usr/local/bin/uv pip install -r /tmp/requirements.txt
{{- end }}
# kdeps assets (harness, tools, model registries, ...) from the build host,
# so the container needs no download at start
COPY kdeps-assets/ /app/.kdeps/assets/
# Copy workflow files
{{- if not .UsePrepackagedBinary }}
COPY workflow.yaml /app/workflow.yaml
{{- if .HasResources }}
COPY resources/ /app/resources/
{{- end }}
{{- if .HasData }}
COPY data/ /app/data/
{{- end }}
{{- end }}
# Copy entrypoint and supervisor config
COPY entrypoint.sh /entrypoint.sh
COPY supervisord.conf /etc/supervisord.conf
RUN chmod +x /entrypoint.sh
# Ensure log directory exists
RUN mkdir -p /var/log
{{- if and .OfflineMode .Models .InstallOllama }}
# Download models during build time for offline mode
RUN mkdir -p /llm
# Download Ollama models
RUN (OLLAMA_HOST=127.0.0.1 /usr/local/bin/ollama serve &) && \
sleep 10 && \
{{- range $i, $model := .Models }}{{ if $i }} && \{{ end }}
OLLAMA_HOST=127.0.0.1 /usr/local/bin/ollama pull {{ $model }}{{ end }} && \
pkill -f ollama && \
sleep 2 && \
# Copy to the image layer
cp -r /root/.ollama/* /llm/
{{- end }}
{{- if .LlamafileModels }}
# Pre-bake llamafile models (file backend)
RUN mkdir -p /app/.kdeps/models && \
{{- range .LlamafileModels }}
curl -fL --retry 3 -o /app/.kdeps/models/{{ .File }} "{{ .URL }}" && \
chmod +x /app/.kdeps/models/{{ .File }} && \
{{- end }}
true
{{- end }}
WORKDIR /app
# Run as non-root (supervisord + kdeps; Ollama models live under /app/.ollama).
RUN addgroup -S kdeps && adduser -S -G kdeps kdeps && \
{{- if .InstallOllama }}
mkdir -p /app/.ollama/models && \
{{- end }}
chown -R kdeps:kdeps /app /var/log /entrypoint.sh /etc/supervisord.conf
USER kdeps
# Expose ports
{{- if .HasAPIServer }}
EXPOSE {{ .APIPort }}
{{- end }}
{{- if .HasWebServer }}
EXPOSE {{ .WebServerPort }}
{{- end }}
{{- if .InstallOllama }}
EXPOSE {{ .BackendPort }}
{{- end }}
{{- if .HasAPIServer }}
# Healthcheck (API server serves /health)
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD wget -qO- http://localhost:{{ .APIPort }}/health || exit 1
{{- else if .HasWebServer }}
# Healthcheck (web server has no /health endpoint; check the TCP socket)
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD bash -c 'exec 3<>/dev/tcp/127.0.0.1/{{ .WebServerPort }}' || exit 1
{{- end }}
# Use entrypoint for backend management
ENTRYPOINT ["/entrypoint.sh"]
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
backend_install.tmpl: |-
{{- if .InstallOllama }}
# Ollama included in base image
{{- else }}
# No LLM backend to install
{{- end }}
entrypoint.sh.tmpl: |
#!/bin/bash
set -euo pipefail
echo "=== KDeps Docker Container Starting ==="
{{- if .InstallOllama }}
echo "Ollama enabled on port: ${BACKEND_PORT:-11434}"
{{- else if .LlamafileModels }}
echo "LLM backend: file (llamafile, self-serving)"
{{- else }}
echo "No local LLM backend installed"
{{- end }}
{{- if .InstallOllama }}
# Function to wait for Ollama to be ready
wait_for_ollama() {
local port=$1
local max_attempts=30
local attempt=0
echo "Waiting for Ollama to be ready on port $port..."
while [ $attempt -lt $max_attempts ]; do
if curl -s http://localhost:$port/api/tags >/dev/null 2>&1; then
echo "Ollama is ready!"
return 0
fi
attempt=$((attempt + 1))
echo "Attempt $attempt/$max_attempts: Ollama not ready yet..."
sleep 2
done
echo "WARNING: Ollama did not become ready after $max_attempts attempts"
return 1
}
{{- end }}
# Handle models based on offline mode
{{- if and .OfflineMode .Models .InstallOllama }}
if [ -d "/llm/models" ]; then
echo "Offline mode: Copying pre-downloaded models to Ollama directory..."
MODELS_DIR="${OLLAMA_MODELS:-/app/.ollama/models}"
mkdir -p "$MODELS_DIR"
rsync -avrPtz --human-readable /llm/models/. "$MODELS_DIR/" || \
cp -r /llm/models/* "$MODELS_DIR/" 2>/dev/null || true
echo "Ollama models copied successfully"
else
echo "WARNING: No pre-downloaded Ollama models found in /llm/models"
fi
{{- else if and .Models .InstallOllama }}
# Download models if specified (not in offline mode)
echo "Pre-pulling Ollama models..."
# Start Ollama temporarily in the background if it's not already running
# so we can pull models during the entrypoint phase.
TEMP_OLLAMA_PID=""
if ! curl -s http://localhost:${BACKEND_PORT:-11434}/api/tags >/dev/null 2>&1; then
echo "Starting Ollama server temporarily for model pulling..."
/usr/local/bin/ollama serve > /tmp/ollama_entrypoint.log 2>&1 &
TEMP_OLLAMA_PID=$!
wait_for_ollama ${BACKEND_PORT:-11434}
fi
{{- range .Models }}
/usr/local/bin/ollama pull {{ . }} || echo "Failed to pull {{ . }}, will retry at runtime"
{{- end }}
# Kill the temporary Ollama process if we started one
if [ -n "$TEMP_OLLAMA_PID" ]; then
echo "Stopping temporary Ollama server..."
kill $TEMP_OLLAMA_PID || true
sleep 2
fi
{{- end }}
# Ensure the workflow binds to all interfaces inside containers
# (overrides any hostIp: "127.0.0.1" in the workflow YAML)
if [ -f /app/workflow.yaml ]; then
sed -i 's/hostIp: "127.0.0.1"/hostIp: "0.0.0.0"/g' /app/workflow.yaml
sed -i 's/hostIp: 127.0.0.1/hostIp: 0.0.0.0/g' /app/workflow.yaml
fi
# Execute the command passed to docker run
exec "$@"
supervisord.conf.tmpl: |
[supervisord]
nodaemon=true
logfile=/var/log/supervisord.log
pidfile=/tmp/supervisord.pid
loglevel=info
{{- if .InstallOllama }}
[program:ollama]
command=/usr/local/bin/ollama serve
autostart=true
autorestart=true
startretries=10
stderr_logfile=/var/log/ollama.err.log
stdout_logfile=/var/log/ollama.out.log
priority=10
stopwaitsecs=30
environment=OLLAMA_HOST="127.0.0.1",OLLAMA_MODELS="/app/.ollama/models"
{{- end }}
[program:kdeps]
{{- if .UsePrepackagedBinary }}
command=/usr/local/bin/kdeps
{{- else }}
command=/usr/local/bin/kdeps run /app/workflow.yaml
{{- end }}
autostart=true
autorestart=true
startretries=3
stderr_logfile=/var/log/kdeps.err.log
stdout_logfile=/var/log/kdeps.out.log
priority=20
environment=KDEPS_BIND_HOST="0.0.0.0"
{{- if .InstallOllama }}
depends_on=ollama
{{- end }}
ubuntu.Dockerfile.tmpl: |
FROM {{ .BaseImage }}
# Set environment variables
ENV DEBIAN_FRONTEND=noninteractive \
PYTHONUNBUFFERED=1 \
PATH=/opt/venv/bin:$PATH \
KDEPS_BIND_HOST=0.0.0.0 \
KDEPS_ASSETS_DIR=/app/.kdeps/assets \
{{- if .LlamafileModels }}
KDEPS_MODELS_DIR=/app/.kdeps/models \
{{- end }}
{{- if .InstallOllama }}
OLLAMA_HOST=127.0.0.1 \
OLLAMA_PORT={{ .BackendPort }} \
OLLAMA_MODELS=/app/.ollama/models \
{{- end }}
BACKEND_PORT={{ .BackendPort }}
{{- if .Env }}
# User-defined environment variables
{{- range $key, $value := .Env }}
ENV {{ $key }}="{{ $value }}"
{{- end }}
{{- end }}
# Install base dependencies (upgrade first to pick up security patches
# published after the base image was built)
RUN apt-get update && \
apt-get upgrade -y && \
apt-get install -y \
zstd \
python3 \
python3-venv \
python3-pip \
curl \
wget \
ca-certificates \
supervisor \
bash \
rsync && \
rm -rf /var/lib/apt/lists/*
{{- if .UsePrepackagedBinary }}
# Install prepackaged kdeps binary (self-contained, workflow embedded).
# TARGETARCH is set automatically by `docker buildx build --platform`; for a
# plain `docker build` it falls back to the host arch via `uname -m`.
ARG TARGETARCH
{{- if .PrepackagedAMD64 }}
COPY kdeps-linux-amd64 /tmp/kdeps-linux-amd64
{{- end }}
{{- if .PrepackagedARM64 }}
COPY kdeps-linux-arm64 /tmp/kdeps-linux-arm64
{{- end }}
RUN ARCH="${TARGETARCH:-$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')}" && \
if [ -f "/tmp/kdeps-linux-${ARCH}" ]; then \
mv "/tmp/kdeps-linux-${ARCH}" /usr/local/bin/kdeps; \
else \
curl -LsSf https://raw.githubusercontent.com/kdeps/kdeps/{{ .InstallerRef }}/install.sh | sh -s -- -b /usr/local/bin {{ .InstallerRef }}; \
fi && \
rm -f /tmp/kdeps-linux-amd64 /tmp/kdeps-linux-arm64 && \
chmod +x /usr/local/bin/kdeps
{{- else }}
# Install kdeps binary
RUN curl -LsSf https://raw.githubusercontent.com/kdeps/kdeps/{{ .InstallerRef }}/install.sh | sh -s -- -b /usr/local/bin {{ .InstallerRef }}
{{- end }}
{{- if .OSPackages }}
# Install OS packages
RUN apt-get update && \
apt-get install -y{{ range .OSPackages }} {{ . }}{{ end }} && \
rm -rf /var/lib/apt/lists/*
{{- end }}
# Install LLM backend
{{ .BackendInstall }}
{{- if .InstallUV }}
# Install uv for Python package management
COPY --from=ghcr.io/astral-sh/uv:{{ .UVImageTag }} /uv /usr/local/bin/uv
RUN chmod +x /usr/local/bin/uv
# Create virtual environment
RUN /usr/local/bin/uv venv /opt/venv
{{- end }}
{{- if .PythonPackages }}
# Install Python packages
RUN /usr/local/bin/uv pip install{{ range .PythonPackages }} {{ . }}{{ end }}
{{- end }}
{{- if .RequirementsFile }}
# Install from requirements file
COPY {{ .RequirementsFile }} /tmp/requirements.txt
RUN /usr/local/bin/uv pip install -r /tmp/requirements.txt
{{- end }}
# kdeps assets (harness, tools, model registries, ...) from the build host,
# so the container needs no download at start
COPY kdeps-assets/ /app/.kdeps/assets/
# Copy workflow files
{{- if not .UsePrepackagedBinary }}
COPY workflow.yaml /app/workflow.yaml
{{- if .HasResources }}
COPY resources/ /app/resources/
{{- end }}
{{- if .HasData }}
COPY data/ /app/data/
{{- end }}
{{- end }}
# Copy entrypoint and supervisor config
COPY entrypoint.sh /entrypoint.sh
COPY supervisord.conf /etc/supervisord.conf
RUN chmod +x /entrypoint.sh
# Ensure log directory exists
RUN mkdir -p /var/log
{{- if and .OfflineMode .Models .InstallOllama }}
# Download models during build time for offline mode
RUN mkdir -p /llm
# Download Ollama models
RUN (OLLAMA_HOST=127.0.0.1 /usr/local/bin/ollama serve &) && \
sleep 10 && \
{{- range $i, $model := .Models }}{{ if $i }} && \{{ end }}
OLLAMA_HOST=127.0.0.1 /usr/local/bin/ollama pull {{ $model }}{{ end }} && \
pkill -f ollama && \
sleep 2 && \
# Copy to the image layer
cp -r /root/.ollama/* /llm/
{{- end }}
{{- if .LlamafileModels }}
# Pre-bake llamafile models (file backend)
RUN mkdir -p /app/.kdeps/models && \
{{- range .LlamafileModels }}
curl -fL --retry 3 -o /app/.kdeps/models/{{ .File }} "{{ .URL }}" && \
chmod +x /app/.kdeps/models/{{ .File }} && \
{{- end }}
true
{{- end }}
WORKDIR /app
# Run as non-root (supervisord + kdeps; Ollama models live under /app/.ollama).
RUN groupadd -r kdeps && useradd -r -g kdeps -d /app -s /usr/sbin/nologin kdeps && \
{{- if .InstallOllama }}
mkdir -p /app/.ollama/models && \
{{- end }}
chown -R kdeps:kdeps /app /var/log /entrypoint.sh /etc/supervisord.conf
USER kdeps
# Expose ports
{{- if .HasAPIServer }}
EXPOSE {{ .APIPort }}
{{- end }}
{{- if .HasWebServer }}
EXPOSE {{ .WebServerPort }}
{{- end }}
{{- if .InstallOllama }}
EXPOSE {{ .BackendPort }}
{{- end }}
{{- if .HasAPIServer }}
# Healthcheck (API server serves /health)
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD wget -qO- http://localhost:{{ .APIPort }}/health || exit 1
{{- else if .HasWebServer }}
# Healthcheck (web server has no /health endpoint; check the TCP socket)
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD bash -c 'exec 3<>/dev/tcp/127.0.0.1/{{ .WebServerPort }}' || exit 1
{{- end }}
# Use entrypoint for backend management
ENTRYPOINT ["/entrypoint.sh"]
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
Versions
| Version | Published | Status |
|---|---|---|
| 1.0.0 | 10/13/2026 | active |
Details
- Author
- kdeps
- License
- Apache-2.0
- Latest Version
- 1.0.0
- Published
- 10/13/2026
Tags
kdepsdockerfile