feat(docker): image, compose, and env for remote-HTTP deployment
- 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.
This commit is contained in:
4 files changed
+211
No files matched your search
@@ -0,0 +1,59 @@
|
||||
# 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"]
|
||||
Reference in new issue
Block a user