Skip to content

[idea] Docker 05/14: First CPU image and compose, without local model runtimes #2105

Open

Description

@AnishSarkar22

Which part? Desktop app

What would you like?

Step 5 of 14 of #2100: running SurfSense in Docker. Depends on #2104; start once it is merged.

The first container that works end to end: upload, ingest, search and chat through a remote connection. Local model runtimes come in later steps.

Design: Docker proposal, Files

What to change

  • docker/local/Dockerfile, context surfsense_local/, Ubuntu 24.04: frontend built with VITE_API_BASE=/api, backend uv sync into a venv, the bundled models (bge-small, the Docling pack), the headless entry, Caddy, tini.
  • docker/local/Dockerfile.dockerignore.
  • docker/local/caddy/Caddyfile and caddy/access-local.caddy: serve the frontend, proxy /api/* with the prefix stripped, refuse /api/internal/*, answer only localhost, 127.0.0.1 and [::1].
  • electron/src/headless/caddy.ts: Caddy as one more SidecarSpec, started after /health.
  • docker/local/docker-compose.yml and .env.example: one service, port 127.0.0.1:3929, a /data volume. SURFSENSE_LOCAL_CORS_ORIGINS set empty.
  • A HEALTHCHECK on /health. Nothing outside docker/local/ in docker/ changes.

Done when

  • docker compose -f docker/local/docker-compose.yml up --build, then at http://localhost:3929: upload a PDF, it reaches ready, and a chat through a remote connection answers with citations.
  • Restarting the container keeps the data.
  • A request with Host: 192.168.1.10 is refused.
  • docker stop shuts down cleanly within the timeout.

Scope: code and tests only. Don't edit docs/ or AGENTS.md, except to fix a link this change breaks; the maintainers update the docs when the Docker work ships.

Why would it help?

Each step leaves something that runs, so the Docker image is built and reviewed one piece at a time.

Activity

  1. added
    enhancementNew feature or request
    help wantedExtra attention is needed
    desktopSurfSense desktop app (surfsense_local)
    dockerRunning SurfSense in Docker
    on Oct 1, 2026
  2. ybai08 commented on Oct 10, 2026

    @ybai08
    Contributor

    I would like to take this step once #2104 is merged. #2103 is with @llwand1 (#2215) and #2104 with @Protocol-zero-0, so I will not start on the headless side before theirs lands; caddy.ts will use whatever SidecarSpec and boot context #2104 ends with.

    What I would deliver, following the proposal's Files section:

    • docker/local/Dockerfile (context surfsense_local/, Ubuntu 24.04; stages frontend with VITE_API_BASE=/api, backend with uv sync, models for bge-small and the Docling pack through the desktop's fetch scripts, runtime with Node, Caddy and tini), Dockerfile.dockerignore, and a HEALTHCHECK on /health.
    • caddy/Caddyfile and caddy/access-local.caddy: the frontend, /api/* with the prefix stripped, /api/internal/* refused, and only localhost, 127.0.0.1 and [::1] answered.
    • electron/src/headless/caddy.ts with node:test tests beside it, started after /health.
    • docker-compose.yml and .env.example: one service on 127.0.0.1:3929, the /data volume, SURFSENSE_LOCAL_CORS_ORIGINS empty.

    How I would check the four boxes: on Docker on an Apple Silicon Mac, so the image I run is linux/arm64. I can build linux/amd64 under emulation to see that it builds, but I would not call it tested until step 6's CI runs it. For the chat box I would add an OpenAI-compatible connection pointing at a server on the host, and say in the PR which one and which model. The Host: 192.168.1.10 refusal, data surviving a restart and the docker stop time I would show with the commands and their output.

    Until #2104 merges I can prepare the parts that do not depend on it (the Dockerfile stages, the Caddy files with a check of the host rule against a stub upstream) and keep them local.

  3. ybai08 commented on Oct 11, 2026

    @ybai08
    Contributor

    While waiting for #2104 I built the three stages that do not depend on it (frontend, backend, models) on dev at 52f51cb, as linux/arm64, to see what the step will meet. Two things are worth knowing before anyone writes the Dockerfile; neither is in the proposal.

    1. The backend no longer builds from the surfsense_local/ context alone. backend/pyproject.toml takes surfsense-plugin-manifest by path from ../../plugins/bundles/core/manifest (since the plugins work), which is outside the context the proposal names. uv sync --frozen stops with Distribution not found at: file:///plugins/bundles/core/manifest.

    What worked here, keeping surfsense_local/ as the context: pass plugins/ as a second, named build context and install the package into the venv as a built package, so the runtime stage needs the venv and not that directory.

    COPY --from=plugins bundles/core/manifest /plugins/bundles/core/manifest
    WORKDIR /app/backend
    RUN uv sync --frozen --no-dev --no-editable

    built with --build-context plugins=plugins (Compose: additional_contexts). The other way is to make the repository root the context. That is your call, since it changes a line of the proposal; I would take the named context.

    2. A minimal Ubuntu 24.04 needs three packages for the parser. Docling's OCR brings opencv-python, and import cv2 fails without libxcb1, libgl1 and libglib2.0-0t64 (found by adding them one at a time until the import worked). Without them scripts/fetch_docling_models.py fails in the models stage, and ingest would fail the same way at run time.

    With both in place, on arm64:

    stage result
    frontend pnpm build with VITE_API_BASE=/api: 20 MB of dist
    backend uv sync from the lock: torch 2.14.0+cpu, the venv is 1.5 GB, import api.main works
    models bge-small and the Docling pack by the desktop's two fetch scripts: 462 MB, missing_parser_folders() returns []

    Not done: linux/amd64, the runtime stage, Caddy inside the image, and anything that needs the headless entry. One thing about my network and not the image: the campus resolver here does not resolve cdn-lfs-cn-1.modelscope.cn, where Docling's downloader gets the RapidOCR weights, so I pinned that host for my build. CI will tell whether a runner sees it.

  4. Protocol-zero-0 commented on Oct 12, 2026

    @Protocol-zero-0

    Thanks for the arm64 build notes. I checked the backend pyproject and confirmed that surfsense-plugin-manifest points outside the proposed surfsense_local/ context. I have recorded that build-context dependency and your reported Ubuntu parser libraries for the image acceptance checks.

    For #2104, I am following the shared boot interface now on dev: it returns apiUrl, waitHealthy(), the sidecar handles and stopWatching(). That gives #2105 a concrete handoff: wire Caddy to the returned API URL after the health gate, while the headless entry handles watcher/child cleanup on shutdown. Docker packaging and Caddy stay in your #2105 scope. I will link the #2104 implementation when it is ready; this note does not claim that I have reproduced the image build.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    desktopSurfSense desktop app (surfsense_local)dockerRunning SurfSense in DockerenhancementNew feature or requesthelp wantedExtra attention is neededsecurity

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions