Installing SurfSense on Docker

I set up SurfSense on my lab machine so I could run a self-hosted AI research agent without sending data to third parties. This is the Docker setup I used, along with the bits that needed fixing and the checks I ran along the way.

Get Docker running first. Install Docker Engine or Docker Desktop and make sure the daemon is active on the host. I use a named volume so data survives upgrades.

docker run -d -p 3000:3000 -p 8000:8000 \
  -v surfsense-data:/data \
  --name surfsense \
  --restart unless-stopped \
  ghcr.io/modsetter/surfsense:latest

On Windows PowerShell, the line continuation is different:

docker run -d -p 3000:3000 -p 8000:8000 `
  -v surfsense-data:/data `
  --name surfsense `
  --restart unless-stopped `
  ghcr.io/modsetter/surfsense:latest

If you prefer Docker Compose, define the same ports and a named volume. Make sure ports 3000 and 8000 are free. If they clash, change the host side of the mapping, for example 3001:3000.

Once the container starts, open http://localhost:3000. The web UI is where you add data sources, change RBAC settings and attach LLM backends. I linked GitHub and Notion for research material, and pointed SurfSense at a local SearxNG instance for private web search. I enabled one local LLM first, checked that queries returned, then added the remote connectors. Keeping the default data volume in place preserves indexes and uploads across recreates.

Custom settings live in the dashboard and in environment variables if you use Compose. Common variables are API keys, allowed origins and paths to local model sockets. If the local LLM runs on the same host, point SurfSense at the local socket or HTTP port and set the model names in the UI. For embeddings, hosted providers and local models both work; I tested with a small corpus first so I could adjust embedding and reranker settings without reindexing everything. For ingestion, SurfSense accepts many file types. I dropped in a couple of PDFs and a directory of markdown files, then watched the ingestion logs to see the pipeline process them.

Expect a few problems on first run. If the container exits immediately, check docker logs -f surfsense for stack traces. Common causes are Docker not being allowed to bind ports, SELinux or AppArmor blocking volume writes, or missing environment variables for required connectors. Run docker ps to confirm the container is running. If you see permission errors on /data, stop the container and change the volume ownership or mount a host directory with consistent UID mapping. To upgrade, pull the new image, stop and remove the old container, then recreate it with the same volume name so data stays intact.

docker pull ghcr.io/modsetter/surfsense:latest
docker stop surfsense
docker rm surfsense

Then run the docker run command again.

Verify it by opening the dashboard, checking that the health endpoints respond on port 8000 if you use them, and running a sample query against the local LLM. If search results look poor, rerun ingestion for the affected documents and check the embedding model selection.

SurfSense was quick to get running in Docker. The main things were keeping Docker healthy, reserving ports 3000 and 8000, and using a named volume for persistent data. Starting with a single local LLM and a small connector set saved me from reindexing a large corpus while I was still tuning it.

Related posts

Weekly Tech Digest | 06 Jul 2026

Stay updated with the latest in tech! This digest covers AI ethics, auto industry shifts, and the impact of politics on technology, exploring today's pressing issues.

wolfCOSE zero-allocation parsing in embedded C

wolfCOSE looks sensible only if you care about what your firmware actually has to carry. I like that, because on small targets the wrong crypto feature can cost more than the message itself, and there...

restic | v0.19.1

restic v0 19 1: safer FUSE mounts and mountpoint checks, robust backup source and exclude handling, clearer CLI JSON output, Windows SFTP deletion fixes