Ctrl K
ComunidadGuías…

docker-compose.yml

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# lynox — single-instance Docker Compose setup
#
# Quick start:
#   1. Copy .env.example to .env and fill in ANTHROPIC_API_KEY
#   2. Required on native Linux, before the first start: mkdir -p ~/.lynox,
#      then the line under "Who the container runs as" in .env.example
#      (Docker Desktop: only the mkdir)
#   3. docker compose up
#   4. Open http://localhost:3000 and enter the access token:
#      docker compose exec lynox cat /home/lynox/.lynox/.access-token
#      (the log shows only its first 8 characters, on the first start)
#
# Full docs: https://docs.lynox.ai/getting-started/

services:
  lynox:
    image: ghcr.io/lynox-ai/lynox:latest
    # Explicit platform pin — safe default while the multi-arch manifest
    # rolls out. Apple Silicon users can remove this line once :latest is
    # multi-arch to get a native arm64 image instead of Rosetta emulation.
    platform: linux/amd64
    restart: unless-stopped
    read_only: true
    # The user the engine runs as. It has to own ~/.lynox on the host, which
    # is mounted below. Set LYNOX_UID/LYNOX_GID in .env before the first start
    # (see .env.example and https://docs.lynox.ai/setup/docker/). Without them it
    # runs as the image's own user, 1001, which can only write ~/.lynox if that
    # directory belongs to 1001.
    user: "${LYNOX_UID:-1001}:${LYNOX_GID:-1001}"
    ports:
      - "3000:3000"
    tmpfs:
      - /tmp:size=512M
      - /workspace:size=256M,uid=${LYNOX_UID:-1001},gid=${LYNOX_GID:-1001}
      # ONNX embedding cache for the Knowledge Graph (Xenova/transformers.js).
      # Without this writable tmpfs the read_only root filesystem rejects the
      # subdirectory `mkdir` and every web-research call spams the engine log
      # with ENOENT; semantic search has to re-download from huggingface on
      # every restart anyway, so a tmpfs is exactly the right lifetime.
      - /home/lynox/.cache:size=512M,uid=${LYNOX_UID:-1001},gid=${LYNOX_GID:-1001}
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges
    pids_limit: 512
    logging:
      driver: json-file
      options:
        max-size: "20m"
        max-file: "3"
    environment:
      # An id the image has no passwd entry for would otherwise get / as its home.
      - HOME=/home/lynox
      # ── Required ──────────────────────────────────────
      - ANTHROPIC_API_KEY           # Your Anthropic API key (starts with sk-ant-)

      # ── Recommended ──────────────────────────────────
      # Set these explicitly for production. If omitted, they are
      # auto-generated on first start and saved to the volume.
      # - LYNOX_HTTP_SECRET         # Access token for Web UI login
      # - LYNOX_VAULT_KEY           # Encrypt data at rest (generate: openssl rand -base64 48)

      # ── Required for the /login form (CSRF) ──────────
      # SvelteKit's CSRF guard rejects POST forms when the request URL
      # protocol doesn't match the browser's Origin header. adapter-node
      # defaults to https when ORIGIN is unset, so http://localhost gets
      # 403'd on every form submit (e.g. token-based login). Set ORIGIN
      # to your actual bind URL — scheme + host + port, no path.
      - ORIGIN=http://localhost:3000

      # ── Web Search ────────────────────────────────────
      - SEARXNG_URL=http://searxng:8080  # Free, unlimited via SearXNG below.
      # Without SEARXNG_URL set, lynox falls back to a best-effort
      # DuckDuckGo HTML-scrape — works for casual research, but SearXNG
      # is the only full-quality backend.

      # ── Optional features ─────────────────────────────
      # - GOOGLE_CLIENT_ID          # Google Workspace OAuth
      # - GOOGLE_CLIENT_SECRET      # Google Workspace OAuth
      # - LYNOX_BUGSINK_DSN         # Error reporting (opt-in, Bugsink)

    # ⚠ CRITICAL: This volume persists your data AND vault key.
    # Without it, all data and encryption keys are lost on container restart.
    # Create it first: mkdir -p ~/.lynox. The long form makes a missing
    # directory fail the start; the short form would let Docker create it
    # owned by root, which only sudo can repair.
    volumes:
      - type: bind
        source: ${HOME}/.lynox
        target: /home/lynox/.lynox
        bind:
          create_host_path: false
    networks:
      - internal

  # ── SearXNG — free web search ──────────────────────
  searxng:
    image: searxng/searxng:latest
    restart: unless-stopped
    read_only: true
    tmpfs:
      - /tmp:size=64M
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges
    pids_limit: 128
    logging:
      driver: json-file
      options:
        max-size: "5m"
        max-file: "2"
    volumes:
      - ./searxng/settings.yml:/etc/searxng/settings.yml:ro
    networks:
      - internal

# What this network is, and what it is not — measured 2026-08-10, because the
# docs used to call it "network isolation" and it is not that.
#
# It is NOT `internal: true`. Outbound from both containers is unrestricted;
# the engine needs it to reach the LLM API, so `internal: true` would break it.
#
# What it DOES buy is opt-in membership: a service added without a `networks:`
# key joins Compose's implicit `default` network instead and gets no route and
# no DNS to the services here — measured both ways with real containers.
#
# ⚠ Two limits, both measured, so nobody mistakes this for a boundary:
#   - A sidecar with the Docker socket defeats it in one command
#     (`docker network connect`), so it stops accidents, not adversaries.
#   - It cuts BOTH ways. Anything you add that must reach `lynox` — the
#     cloudflared sidecar in setup/remote-access.md is the live example — needs
#     `networks: [internal]`, or it starts happily and silently cannot connect.
networks:
  internal:
    driver: bridge