1ee9a930d3
compose set TZ=Europe/Samara and Go read it, so clock replies and quiet hours were already local. But /etc/localtime in the image pointed at Etc/UTC, so a caller asking the system zone instead of the environment answered UTC. The reminder path shells out to python dateparser, which is such a caller. TZ is now a build arg on the runtime stage. It points the symlink, writes /etc/timezone and sets ENV TZ, so the image is local on its own. Compose passes the zone it already declares, so the zone stays written in one place.
129 lines
6.5 KiB
Docker
129 lines
6.5 KiB
Docker
# syntax=docker/dockerfile:1
|
|
#
|
|
# Single image, all six daemons. docker-compose runs one container per daemon
|
|
# off this image with a different command — the native-lib + toolchain surface
|
|
# is shared, so separate images would duplicate ~all of it. Isolation still
|
|
# holds: each daemon is its own container/namespace, only mavend mounts the key
|
|
# and the db volume.
|
|
#
|
|
# Native deps are the prebuilt artifacts the repo already carries under deps/
|
|
# (libwhisper+ggml-vulkan, onnxruntime, piper/espeak). We do NOT build
|
|
# whisper.cpp from source here — COPY the prebuilt .so and headers.
|
|
# ponytail: prebuilt-lib copy, not a from-source build. Add a whisper.cpp build
|
|
# stage if you ever need reproducibility / a different arch than the host libs.
|
|
|
|
# trixie, not bookworm: the prebuilt deps/lib/*.so are linked against
|
|
# glibc 2.38 + GLIBCXX 3.4.32 (built on the Arch host). bookworm's glibc 2.36
|
|
# is too old and the link fails on missing symbols. trixie ships glibc 2.40.
|
|
# 1.25 to match go.mod's `go 1.25.5` directive: the official golang images pin
|
|
# GOTOOLCHAIN=local, so a 1.24 builder refuses the module instead of fetching a
|
|
# newer toolchain. Bump this in lockstep with the go.mod directive.
|
|
FROM golang:1.25-trixie AS build
|
|
WORKDIR /src
|
|
|
|
# libvulkan-dev: libggml-vulkan.so needs libvulkan.so.1 at link time.
|
|
RUN apt-get update && apt-get install -y --no-install-recommends libvulkan-dev \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# native build inputs (prebuilt libs + headers), then module cache, then source
|
|
COPY deps/lib/ /src/deps/lib/
|
|
COPY deps/piper/ /src/deps/piper/
|
|
COPY deps/include/ /src/deps/include/
|
|
COPY deps/whisper.cpp/ggml/include/ /src/deps/whisper.cpp/ggml/include/
|
|
COPY go.mod go.sum ./
|
|
# vendored deps: the ecosystem clients (hexis) are replace-directed at sibling
|
|
# repos outside the build context, so we can't `go mod download` them here.
|
|
# `go mod vendor` (run on a host where the siblings exist) bakes them into
|
|
# vendor/; with it present, go build uses it and never touches the replace
|
|
# paths. Regenerate with `go mod vendor` whenever a sibling client API changes.
|
|
COPY vendor/ ./vendor/
|
|
COPY cmd/ ./cmd/
|
|
# force rebuild 2026-07-10T16:50
|
|
COPY internal/ ./internal/
|
|
|
|
# CGO wiring mirrors the Makefile; rpath points at the RUNTIME lib location so
|
|
# the binaries find their .so at /opt/maven/lib regardless of LD_LIBRARY_PATH.
|
|
ENV CGO_ENABLED=1 \
|
|
CGO_CFLAGS="-I/src/deps/include -I/src/deps/whisper.cpp/ggml/include" \
|
|
CGO_LDFLAGS="-L/src/deps/lib -L/src/deps/piper -Wl,-rpath,/opt/maven/lib"
|
|
RUN go build -o /out/mavend ./cmd/mavend && \
|
|
go build -o /out/mavsttd ./cmd/mavsttd && \
|
|
go build -o /out/mavttsd ./cmd/mavttsd && \
|
|
go build -o /out/mavweb ./cmd/mavweb && \
|
|
go build -o /out/mavpoll ./cmd/mavpoll && \
|
|
go build -o /out/mavcaldav ./cmd/mavcaldav && \
|
|
go build -o /out/mavmaild ./cmd/mavmaild
|
|
|
|
# llama.cpp Vulkan build — the phraser/router LFM engine (llama-server). Built
|
|
# from source (not a prebuilt vendored blob) so the binary's glibc/GLIBCXX match
|
|
# the trixie runtime and GPU offload rides mesa's RADV Vulkan driver — the same
|
|
# path whisper already uses on homesrv's AMD iGPU (RADV RENOIR). Pinned to b9601 (parity with
|
|
# the host's known-good build). Static (BUILD_SHARED_LIBS=OFF) ⇒ one self-
|
|
# contained binary, no libggml/libllama .so to juggle in the runtime; only
|
|
# libvulkan.so.1 + libgomp (both already in the runtime) are needed at load.
|
|
FROM debian:trixie-slim AS llama
|
|
ARG LLAMA_REF=b9601
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
ca-certificates git cmake build-essential libvulkan-dev \
|
|
glslc glslang-tools spirv-headers spirv-tools \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
RUN git clone --depth 1 --branch ${LLAMA_REF} \
|
|
https://github.com/ggml-org/llama.cpp /src/llama.cpp
|
|
WORKDIR /src/llama.cpp
|
|
RUN cmake -B build \
|
|
-DCMAKE_BUILD_TYPE=Release \
|
|
-DGGML_VULKAN=ON \
|
|
-DBUILD_SHARED_LIBS=OFF \
|
|
-DLLAMA_CURL=OFF \
|
|
-DLLAMA_BUILD_SERVER=ON \
|
|
&& cmake --build build --config Release -j"$(nproc)" --target llama-server
|
|
|
|
FROM debian:trixie-slim AS runtime
|
|
# tzdata so the TZ env (set in compose) resolves — otherwise Go can't load the
|
|
# zone and time.Now() stays UTC, and mavend answers clock/date queries and
|
|
# evaluates quiet-hours in UTC.
|
|
#
|
|
# TZ is a build arg as well as an env because the image was self-inconsistent
|
|
# without it (V-545): compose set TZ=Europe/Samara and Go read it, but
|
|
# /etc/localtime still pointed at Etc/UTC, so anything asking the system zone
|
|
# instead of the environment answered UTC. The reminder path shells out to
|
|
# python dateparser, which is exactly such a caller. Compose passes the same
|
|
# zone it already declares, so the zone is written in one place.
|
|
ARG TZ=Etc/UTC
|
|
ENV TZ=$TZ
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
ca-certificates libvulkan1 mesa-vulkan-drivers libgomp1 tzdata \
|
|
python3 python3-pip && \
|
|
pip3 install --no-cache-dir --break-system-packages 'dateparser==1.4.1' && \
|
|
apt-get purge -y --auto-remove python3-pip && \
|
|
ln -snf "/usr/share/zoneinfo/$TZ" /etc/localtime && \
|
|
echo "$TZ" > /etc/timezone && \
|
|
rm -rf /var/lib/apt/lists/*
|
|
|
|
# runtime native libs: whisper/ggml (incl. vulkan) are real files in deps/lib.
|
|
COPY deps/lib/ /opt/maven/lib/
|
|
COPY deps/piper/ /opt/maven/piper/
|
|
# The router embedder needs onnxruntime 1.26, but deps/lib only carries dangling
|
|
# symlinks to it (they point at an absolute HOST path that isn't in the image).
|
|
# Ship the real 1.26 lib and repoint the symlinks. Piper keeps its OWN
|
|
# onnxruntime 1.14 under /opt/maven/piper (found via piper's $ORIGIN runpath +
|
|
# its exact soname libonnxruntime.so.1.14.1) — do NOT copy piper's .so into
|
|
# /opt/maven/lib, that clobbers 1.26 and mavend dies with "ORT API base: 2".
|
|
COPY deps/onnxruntime-linux-x64-1.26.0/lib/libonnxruntime.so.1.26.0 /opt/maven/lib/
|
|
RUN cd /opt/maven/lib \
|
|
&& ln -sf libonnxruntime.so.1.26.0 libonnxruntime.so \
|
|
&& ln -sf libonnxruntime.so.1.26.0 libonnxruntime.so.1
|
|
COPY --from=build /out/ /opt/maven/bin/
|
|
# the LFM engine: static Vulkan llama-server on PATH; the phraser spawns it by
|
|
# name (bin_path "llama-server"). GPU offload needs /dev/dri passed to mavend.
|
|
COPY --from=llama /src/llama.cpp/build/bin/llama-server /opt/maven/bin/
|
|
|
|
ENV LD_LIBRARY_PATH=/opt/maven/lib PATH=/opt/maven/bin:$PATH
|
|
|
|
# unprivileged; core owns the key + db, modules own nothing.
|
|
RUN useradd -r -u 10001 -m maven \
|
|
&& mkdir -p /run/maven /var/lib/maven \
|
|
&& chown maven:maven /run/maven /var/lib/maven
|
|
USER maven
|
|
WORKDIR /opt/maven
|