--- title: Docker Installation description: Run SurfSense with Docker in minutes --- Docker is the recommended way to run SurfSense. Everything — database, backend, frontend, background workers, real-time sync, and a reverse proxy — comes pre-configured. **Prerequisites:** [Docker Desktop](https://www.docker.com/products/docker-desktop/) (or Docker Engine with Compose) must be installed and running. ## Option 1: One-Line Install Script (Recommended) **Linux/macOS:** ```bash curl -fsSL https://raw.githubusercontent.com/MODSetter/SurfSense/main/docker/scripts/install.sh | bash ``` **Windows (PowerShell):** ```powershell irm https://raw.githubusercontent.com/MODSetter/SurfSense/main/docker/scripts/install.ps1 | iex ``` The script creates a `./surfsense/` directory with the compose files and a `.env`, generates a secret key, starts all services, and waits until they're healthy. It also: - Detects NVIDIA GPUs and asks whether to use GPU acceleration, picking the compatible backend image automatically. - Asks whether to enable automatic daily updates via [Watchtower](https://github.com/nicholas-fedor/watchtower) (updates can download several GB in the background — pass `--no-watchtower` to skip). - Detects a legacy all-in-one installation and migrates its data automatically (see [Updating](/docs/docker-installation/updating#migrating-from-the-all-in-one-container)). ## Option 2: Manual Docker Compose ```bash git clone https://github.com/MODSetter/SurfSense.git cd SurfSense/docker cp .env.example .env # Edit .env — at minimum set SECRET_KEY (generate with: openssl rand -base64 32) docker compose up -d ``` ## Access SurfSense After starting, everything is served through the bundled Caddy reverse proxy on a single origin: - **SurfSense**: [http://localhost:3929](http://localhost:3929) - **Backend API**: [http://localhost:3929/api/v1](http://localhost:3929/api/v1) - **Zero sync**: `ws://localhost:3929/zero` Sign up with email/password (local auth is the default) and you're in. ## Configuration All configuration lives in a single `.env` file — `surfsense/.env` if you used the install script, `docker/.env` if you cloned manually. The bundled `.env.example` documents every option inline with comments and working defaults; edit the values you need and restart: ```bash docker compose up -d ``` The defaults give you local email/password auth, local document parsing with Docling (no API keys), and local TTS/STT. Common things you might change, all documented in the file: - **Authentication** — switch to Google OAuth login. - **Document parsing** — switch to Unstructured or LlamaCloud (both need API keys). - **Connector credentials** — OAuth apps for [external connectors](/docs/connectors/external) that need them on self-hosted deployments. - **Messaging channels** — Telegram, WhatsApp, Slack, and Discord bots (see [Messaging Channels](/docs/messaging-channels)). ### NVIDIA GPU Acceleration Add these values to `.env` to use the CUDA backend image (the host needs the NVIDIA Container Toolkit): ```dotenv SURFSENSE_VARIANT=cuda COMPOSE_FILE=docker-compose.yml:docker-compose.gpu.yml SURFSENSE_GPU_COUNT=1 ``` Use `SURFSENSE_VARIANT=cuda126` for older NVIDIA driver stacks. On Windows, use `;` instead of `:` in `COMPOSE_FILE`. Then apply: ```bash docker compose pull && docker compose up -d --wait ``` ### Custom Domain / Automatic HTTPS Point DNS at your Docker host and set in `.env`: ```dotenv SURFSENSE_SITE_ADDRESS=surf.example.com LISTEN_HTTP_PORT=80 LISTEN_HTTPS_PORT=443 CERT_EMAIL=you@example.com SURFSENSE_PUBLIC_URL=https://surf.example.com ``` Then `docker compose up -d --wait`. Caddy issues and renews Let's Encrypt certificates automatically — ports 80 and 443 must be reachable from the internet. ### Bring Your Own Proxy If you already run nginx, Traefik, Cloudflare Tunnel, or another ingress, comment out the `proxy` service and route traffic to the internal services with the same path contract: | Public path | Upstream | |-------------|----------| | `/auth/*` | `backend:8000` | | `/api/v1/*` | `backend:8000` | | `/zero/*` | `zero-cache:4848` | | `/*` | `frontend:3000` | Your proxy must preserve WebSocket upgrades for `/zero`, avoid buffering streaming responses, allow long-running requests, and support large uploads. ## What's Running | Service | Description | |---------|-------------| | `proxy` | Caddy reverse proxy — the only public ingress | | `db` | PostgreSQL with pgvector | | `migrations` | Short-lived: runs database migrations, then exits | | `redis` | Message broker for background tasks | | `backend` | FastAPI application server | | `celery_worker` | Background task processing (document indexing, etc.) | | `celery_beat` | Periodic task scheduler | | `zero-cache` | Real-time sync (replicates Postgres to browsers) | | `frontend` | Next.js web application | Migrations run first on every startup; everything else waits for them to succeed, so `docker compose up -d` after an update is always safe. Monitor startup with `docker compose ps` — services go from `(health: starting)` to `(healthy)`. ## Useful Commands ```bash # View logs (all services, or one) docker compose logs -f docker compose logs -f backend # Restart a service docker compose restart backend # Stop all services docker compose down # Stop and remove containers + volumes (destroys your data!) docker compose down -v ``` To update SurfSense, see [Updating](/docs/docker-installation/updating). ## Building from Source (Contributors) If you're contributing to SurfSense and want to build the images from your local checkout, use the dev compose file: ```bash cd SurfSense/docker docker compose -f docker-compose.dev.yml up --build ``` It builds the backend and frontend from source, publishes raw service ports for debugging, and includes pgAdmin at [http://localhost:5050](http://localhost:5050). There's also a `docker-compose.deps-only.yml` that runs just the dependencies (Postgres, Redis, zero-cache) in Docker while you run the backend and frontend natively — see [Manual Installation](/docs/manual-installation#alternative-let-docker-manage-all-dependencies). ## Troubleshooting - **Port already in use**: Change `LISTEN_HTTP_PORT` in `.env` and restart. In domain mode, ports 80/443 are required for certificate issuance. - **Permission errors on Linux**: Prefix `docker` commands with `sudo`, or add your user to the `docker` group. - **Real-time updates not working**: Open DevTools → Console and check for WebSocket errors. The expected URL is `${SURFSENSE_PUBLIC_URL}/zero`. - **Line ending issues on Windows**: Run `git config --global core.autocrlf true` before cloning. ### Migration service exited non-zero The stack halts if migrations fail. Inspect what happened: ```bash docker compose logs migrations ``` Slow disks (Windows / WSL2) may hit the default 900-second migration timeout — set `MIGRATION_TIMEOUT` in `.env` to increase it. ### Zero-cache stuck on `Unknown or invalid publications` This means zero-cache started before migrations created its publication — usually only possible if you started services individually. Recover with: ```bash docker compose down docker volume rm surfsense-zero-cache # wipe the half-built replica docker compose up -d # migrations run first, then zero-cache ``` The same recovery fixes `_zero.tableMetadata` crashes (a half-initialized replica left behind by a previous crash).