Skip to content
Merged
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
104 changes: 56 additions & 48 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
# build: { context: .., dockerfile: docker/Dockerfile }).

# ---- build stage ----
# Stays on Alpine: the binary is built with CGO_ENABLED=0 (fully static), so it
# runs unchanged on the Debian runtime stage below.
FROM golang:1.25-alpine AS build
WORKDIR /src

Expand All @@ -18,22 +20,30 @@ RUN CGO_ENABLED=0 go build -ldflags "-s -w" -o /out/odek ./cmd/odek
# ---- whisper stage ----
# Build whisper.cpp's CLI and fetch the `tiny` model so the `transcribe` tool
# (and Telegram voice auto-transcription) work out of the box — no host
# install, no first-run model download. Same alpine base as the runtime stage
# so the musl ABI matches; OpenMP is disabled to keep the runtime dependency
# surface down to just libstdc++. To ship a different model, override the build
# install, no first-run model download. Same Debian base as the runtime stage
# so the glibc ABI matches; OpenMP is disabled to keep the runtime dependency
# surface down to just libstdc++6. To ship a different model, override the build
# arg: `--build-arg WHISPER_MODEL=base` (tiny | base | small | medium) — size
# and RAM grow accordingly. WHISPER_VERSION pins the whisper.cpp release so the
# build is reproducible — bump it deliberately rather than tracking master.
FROM alpine:latest AS whisper
FROM debian:bookworm-slim AS whisper
ARG WHISPER_MODEL=tiny
ARG WHISPER_VERSION=v1.8.6
RUN apk add --no-cache git cmake make g++ musl-dev curl
RUN apt-get update && apt-get install -y --no-install-recommends \
git cmake make g++ curl ca-certificates \
&& rm -rf /var/lib/apt/lists/*
RUN git clone --depth 1 --branch "${WHISPER_VERSION}" https://github.com/ggerganov/whisper.cpp /whisper
WORKDIR /whisper
# GGML_NATIVE=OFF: don't pass -mcpu=native. On Debian's GCC 12 the native
# arm64 path enables FP16 NEON intrinsics (vfmaq_f16) that fail to inline
# ("target specific option mismatch") when the host CPU's fp16 feature isn't
# resolved under Docker. A fixed baseline arch sidesteps that, builds on both
# amd64 and arm64, and keeps the image reproducible (no host-CPU dependence).
RUN cmake -B build \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=OFF \
-DGGML_OPENMP=OFF \
-DGGML_NATIVE=OFF \
-DWHISPER_BUILD_TESTS=OFF \
-DWHISPER_BUILD_EXAMPLES=ON \
&& cmake --build build -j "$(nproc)" --target whisper-cli
Expand All @@ -45,13 +55,26 @@ RUN mkdir -p /models \
"https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-${WHISPER_MODEL}.bin"

# ---- runtime stage ----
FROM alpine:latest
FROM debian:bookworm-slim
# Tooling the agent commonly needs inside the sandbox container.
# Trim or extend this list to taste. git + the GitHub CLI (`gh`, from the
# Alpine community repo) are included so the agent can clone/PR/release.
# ffmpeg converts Telegram's OGG/Opus voice notes to WAV for whisper.cpp;
# libstdc++ is the only shared lib the bundled whisper-cli needs at runtime.
RUN apk add --no-cache ca-certificates git github-cli bash coreutils curl jq ffmpeg libstdc++
# Trim or extend this list to taste. git + the GitHub CLI (`gh`) are included so
# the agent can clone/PR/release. ffmpeg converts Telegram's OGG/Opus voice
# notes to WAV for whisper.cpp; libstdc++6 is the only shared lib the bundled
# whisper-cli needs at runtime.
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates git bash coreutils curl jq ffmpeg libstdc++6 \
&& rm -rf /var/lib/apt/lists/*

# GitHub CLI (`gh`) is not in Debian's default repos — add the official apt
# repository and install it.
RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
-o /usr/share/keyrings/githubcli-archive-keyring.gpg \
&& chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
> /etc/apt/sources.list.d/github-cli.list \
&& apt-get update && apt-get install -y --no-install-recommends gh \
&& rm -rf /var/lib/apt/lists/*

# Bundle the whisper CLI + model from the whisper stage so `transcribe` works
# with zero setup. whisper-cli lands on PATH; the model goes to a stable image
Expand All @@ -61,44 +84,29 @@ COPY --from=whisper /models/ /usr/local/share/whisper/models/

# ── Adding extra dependencies the agent can use ──────────────────────────
# The agent runs shell commands INSIDE this image, so any runtime or CLI it
# should call must be installed here. Uncomment what you need, then rebuild
# should call must be installed here. Trim or extend to taste, then rebuild
# with `docker compose --profile <name> up --build`.
#
# IMPORTANT: keep all installs ABOVE the `USER odek` line below — apk, npm,
# and the Bun installer need root.
#
# Alpine packages (musl libc — small and fast):
# RUN apk add --no-cache nodejs npm # Node.js + npm
# RUN apk add --no-cache python3 py3-pip # Python 3 + pip
# RUN apk add --no-cache go # Go toolchain
# RUN apk add --no-cache make gcc g++ musl-dev # build-essential equivalents
# RUN apk add --no-cache ripgrep fd jq yq # search / JSON-YAML tools
#
# JS package managers (once npm is present):
# RUN npm install -g pnpm yarn
#
# Bun (official installer; needs bash + unzip; BUN_INSTALL puts it on PATH).
# Uses Bun's musl build on Alpine; libstdc++ is required at runtime:
# RUN apk add --no-cache bash unzip libstdc++ \
# && curl -fsSL https://bun.sh/install | BUN_INSTALL=/usr/local bash
# # → installs /usr/local/bin/bun
#
# ── Heavy / glibc-only toolchains: switch to a Debian base ───────────────
# If a tool won't run on Alpine's musl, replace `FROM alpine:latest` above
# with a Debian-based image and use apt-get instead:
#
# FROM debian:bookworm-slim
# RUN apt-get update && apt-get install -y --no-install-recommends \
# ca-certificates git bash curl jq python3 python3-pip nodejs npm \
# && rm -rf /var/lib/apt/lists/*
#
# …or start from an official language image that already bundles the runtime:
# FROM node:20-bookworm-slim # Node.js + npm preinstalled
# FROM python:3.12-slim # Python preinstalled
#
# On a Debian base, the user-creation line below differs — replace
# `adduser -D -u 1000 odek` with `useradd -m -u 1000 odek`
# (the mkdir/chown, ENV HOME, USER, and WORKDIR lines all work unchanged).
# IMPORTANT: keep all installs ABOVE the `USER odek` line below — apt and the
# Bun installer need root.

# Extra agent toolchains: Go, Python 3, and Bun.
# - Python 3 comes from Debian's apt (3.11 on bookworm); python3-venv lets the
# agent create virtualenvs since system pip is externally managed (PEP 668).
# - Go is installed from the official tarball so the agent gets a current
# toolchain (Debian's golang-go lags badly). Override with --build-arg
# GO_VERSION=x.y.z. dpkg --print-architecture yields amd64/arm64, which
# match Go's release naming.
# - Bun uses its official installer (glibc build); it needs unzip. BUN_INSTALL
# puts bun on PATH at /usr/local/bin/bun.
ARG GO_VERSION=1.25.4
RUN apt-get update && apt-get install -y --no-install-recommends \
python3 python3-pip python3-venv unzip \
&& rm -rf /var/lib/apt/lists/* \
&& curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-$(dpkg --print-architecture).tar.gz" \
| tar -C /usr/local -xz \
&& curl -fsSL https://bun.sh/install | BUN_INSTALL=/usr/local bash
ENV PATH=/usr/local/go/bin:$PATH

# Scheduled tasks ("cron") are handled natively by odek itself — the bot hosts
# an in-process scheduler, and `odek schedule daemon` runs one headless. No
Expand All @@ -108,7 +116,7 @@ COPY --from=whisper /models/ /usr/local/share/whisper/models/
# Pre-create ~/.odek owned by the user so it's writable for config, sessions,
# the Telegram lock, and schedules.json (whether backed by an image dir or a
# mounted folder).
RUN adduser -D -u 1000 odek \
RUN useradd -m -u 1000 odek \
&& mkdir -p /home/odek/.odek /workspace \
&& chown -R odek:odek /home/odek/.odek /workspace

Expand Down
Loading