- docker/Dockerfile: multi-stage uv wheel build; runtime runs `familiar serve` as a non-root user (uid 1000) on port 8080, /data volume for the encrypted token store, and a HEALTHCHECK against /healthz. No token is baked in — serve refuses to start without FAMILIAR_HTTP_TOKEN, so it's supplied at run time. - docker-compose.yml: joins an external `proxy` network (Traefik labels for TLS, nginx/Caddy documented as alternatives), read-only rootfs, tmpfs /tmp, no-new-privileges, cap_drop ALL, non-root, /healthz healthcheck. Pulls the published FAMILIAR_IMAGE with a local build as fallback; writes default off. - .env.example: every FAMILIAR_* knob incl. the bearer token, auth mode, the OAuth resource-server settings, and the insecure-bind escape hatch. - .dockerignore: keep local state (data/, *.enc, key) and dev files out.
60 lines
2.1 KiB
Docker
60 lines
2.1 KiB
Docker
# syntax=docker/dockerfile:1
|
|
|
|
# --------------------------------------------------------------------------- #
|
|
# Build stage: build a wheel from the source tree with uv.
|
|
# --------------------------------------------------------------------------- #
|
|
FROM python:3.12-slim AS builder
|
|
|
|
ENV PYTHONDONTWRITEBYTECODE=1 \
|
|
PYTHONUNBUFFERED=1 \
|
|
PIP_DISABLE_PIP_VERSION_CHECK=1
|
|
|
|
WORKDIR /app
|
|
|
|
RUN pip install --no-cache-dir uv
|
|
|
|
# Files needed to build the wheel (pyproject references README.md + LICENSE).
|
|
COPY pyproject.toml README.md LICENSE ./
|
|
COPY src ./src
|
|
|
|
RUN uv build --wheel --out-dir /dist
|
|
|
|
# --------------------------------------------------------------------------- #
|
|
# Runtime stage: minimal, non-root image that runs `familiar serve` (HTTP).
|
|
# --------------------------------------------------------------------------- #
|
|
FROM python:3.12-slim
|
|
|
|
ENV PYTHONDONTWRITEBYTECODE=1 \
|
|
PYTHONUNBUFFERED=1 \
|
|
PIP_DISABLE_PIP_VERSION_CHECK=1 \
|
|
# Bind on all interfaces inside the container; a TLS reverse proxy fronts it.
|
|
FAMILIAR_HTTP_HOST=0.0.0.0 \
|
|
FAMILIAR_HTTP_PORT=8080 \
|
|
FAMILIAR_HTTP_PATH=/mcp \
|
|
# Token store + audit log + write toggle live here — mount a persistent volume.
|
|
FAMILIAR_DATA_DIR=/data
|
|
# FAMILIAR_HTTP_TOKEN is intentionally NOT baked in: `familiar serve` refuses
|
|
# to start without it, so it must be supplied at run time (docker run -e ...).
|
|
# Writes stay off by default over HTTP; set FAMILIAR_ENABLE_WRITES=true to opt in.
|
|
|
|
WORKDIR /app
|
|
|
|
# Non-root runtime user; /data owned by it so token refresh can write.
|
|
RUN useradd -m -u 1000 -s /usr/sbin/nologin familiar \
|
|
&& mkdir -p /data \
|
|
&& chown -R familiar:familiar /data
|
|
|
|
COPY --from=builder /dist/*.whl /tmp/
|
|
RUN pip install --no-cache-dir /tmp/*.whl && rm -f /tmp/*.whl
|
|
|
|
USER familiar
|
|
|
|
EXPOSE 8080
|
|
VOLUME ["/data"]
|
|
|
|
# Liveness against the unauthenticated /healthz route (no token needed).
|
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
|
|
CMD python -c "import os,httpx; httpx.get(f\"http://127.0.0.1:{os.environ.get('FAMILIAR_HTTP_PORT','8080')}/healthz\", timeout=5).raise_for_status()" || exit 1
|
|
|
|
CMD ["familiar", "serve"]
|