Quickstart & Self-Hosting
Get a working Synaplan running on your own machine — and skip the four things that trip people up most: the local-AI model download, Qdrant, Text-to-Speech, and the production database (Galera) cluster.
If you only want to use the API or the widget against the hosted instance, you don't need any of this — grab a key from web.synaplan.com and jump to Code Examples. This page is for running your own instance.
TL;DR — the smooth path
The single most common complaint about self-hosting is "I ran docker compose up and chat
didn't work for ten minutes." That's the standard install pulling ~14 GB of local AI
models in the background. If you want a working chat in ~2 minutes, start with the
minimal install + a cloud key instead, and add local AI later:
git clone https://github.com/metadist/synaplan.git
cd synaplan
echo "GROQ_API_KEY=your_key" >> backend/.env # free key: https://console.groq.com
docker compose -f docker-compose-minimal.yml up -d
Open http://localhost:5173 and log in with [email protected] / admin123.
Everything below explains why this is the smooth path, and what each optional service (local models, Qdrant, TTS, Galera) actually buys you.
Prerequisites
- Docker + Docker Compose v2 (Docker Desktop, or Docker Engine + the Compose plugin)
- Git
- 8 GB RAM minimum — 16 GB recommended if you run the local-AI (standard) install
- ~9 GB free disk for the standard install, ~5 GB for minimal
- Free TCP ports:
5173,8000,8082,8025,3307,6333,11435
Apple Silicon (M1–M4): the images are published for
linux/amd64and run under emulation. In Docker Desktop → Settings → General, enable "Use Rosetta for x86/amd64 emulation on Apple Silicon" for much faster, more stable containers. It works without it — just slower, with a longer first build.
Two ways to install
| Mode | Command | Size | Best for |
|---|---|---|---|
| Minimal | docker compose -f docker-compose-minimal.yml up -d |
~5 GB | Cloud AI only (Groq / OpenAI / Anthropic / Gemini). Fast first boot. |
| Standard | docker compose up -d |
~9 GB | Full features including local AI via Ollama. |
Pick minimal for your first run; switch to standard once you actually want offline models. Both modes start the same supporting services (database, Redis, Centrifugo, worker, Qdrant, Tika) — the only difference is whether Ollama is included.
After startup you get:
| Service | URL |
|---|---|
| App (Vue SPA) | http://localhost:5173 |
| API (Symfony) | http://localhost:8000 |
| API Docs (Swagger) | http://localhost:8000/api/doc |
| phpMyAdmin | http://localhost:8082 |
| MailHog (email testing) | http://localhost:8025 |
Default logins: [email protected] / admin123 (ADMIN) · [email protected] /
demo123 (PRO) · [email protected] / test123 (unverified).
Pitfall 1 — the local-AI model download
This is the big one. The standard install runs an ollama container and, on first
boot, pulls two models in the background:
| Model | Role | Approx size |
|---|---|---|
gpt-oss:20b |
Local chat | large |
bge-m3 |
Embeddings (RAG) | smaller |
Together that's ~14 GB. The web UI is up in ~2 minutes, but chat that uses a local model only starts working once the download finishes — which on a normal connection can take a while. The UI looking ready before the models are ready is exactly what makes this feel broken when it isn't.
Watch the progress instead of guessing:
docker compose logs -f backend # shows model pull progress
docker compose logs -f ollama # raw Ollama download logs
Ways to avoid the wait:
- Use the minimal install and a cloud provider (the TL;DR above). No local download at all — chat works as soon as the key is set.
- Pull a smaller model yourself after startup and select it in the admin panel:
docker compose exec ollama ollama pull llama3.2:3b
- Point at an existing Ollama on your host instead of the bundled container — set this
in
backend/.envand you reuse models you already downloaded:
OLLAMA_BASE_URL=http://host.docker.internal:11434
Adding a cloud key after the fact? Set it in
backend/.env(GROQ_API_KEY,OPENAI_API_KEY,ANTHROPIC_API_KEY,GOOGLE_GEMINI_API_KEY) and rundocker compose restart backend. The new models then appear in the model selector under System Config → AI Models. Setting the key before the first boot avoids that restart entirely.
Pitfall 2 — do I have to manage Qdrant?
No. Qdrant starts automatically with both compose files and needs zero configuration locally. You do not run, install, or babysit it yourself — it's just another container in the stack.
What it powers:
- RAG document search — semantic search over indexed documents
- AI Memories — long-term user context and profiling
- Feedback system — false-positive detection and learning
And the key relief: Synaplan runs fully without it. If Qdrant is unavailable, memories and vector search are simply disabled — nothing else breaks. For basic vector operations MariaDB VECTOR can serve as an alternative, so a minimal deployment doesn't strictly need a separate vector database at all.
In short: leave the default Qdrant container running and forget about it, or drop it and fall back to MariaDB VECTOR. Either way there's nothing to manage by hand.
The connection is controlled by a single variable if you ever point at an external Qdrant:
QDRANT_URL=http://qdrant:6333
Pitfall 3 — Text-to-Speech is optional and separate
Voice output (TTS) is not part of the core stack — it's an optional companion service in its own repository, synaplan-tts. Synaplan runs perfectly without it and auto-enables voice output when it detects the service running, so there's nothing to toggle in the app.
To add it, run it alongside Synaplan:
git clone https://github.com/metadist/synaplan-tts.git
cd synaplan-tts
docker compose up -d
Synaplan finds it via SYNAPLAN_TTS_URL (default http://host.docker.internal:10200).
Prefer a cloud voice over the local Piper engine? Set ELEVENLABS_API_KEY in
backend/.env instead.
That's the whole story: don't run it and you have no TTS; run it and TTS turns on by itself.
Pitfall 4 — the database (Galera) cluster is a production concern only
For local development and single-server deployments you use the single MariaDB container that ships in the compose files. There is no Galera cluster to set up to try Synaplan, develop against it, or run a modest instance. Don't let the production architecture scare you off the quickstart.
The MariaDB Galera cluster only enters the picture for high-availability, multi-node production, where it's the reference setup for a synchronously-replicated database behind several backend nodes. It is deliberately kept out of the default install. Two things make clustering far less painful than it sounds:
- Synaplan already uses a read/write split (
DATABASE_WRITE_URLandDATABASE_READ_URL), so pointing writes at the primary and reads at replicas/the cluster endpoint is a config change, not a code change. - Redis carries the cross-node coordination (sessions, locks, queues, realtime fan-out), so the database cluster doesn't have to. Scaling out is "add a node, point it at the same Redis and database" — see Architecture & Realtime.
When you're actually ready for HA, use the maintained deployment configs rather than hand-rolling Galera:
- synaplan-platform — production deployment configs (multi-node, Galera, shared Redis).
- synaplan-charts — Helm charts for Kubernetes.
Clustered-realtime checklist (the parts people forget): one shared Redis for every node,
identical REALTIME_TOKEN_SECRET and REALTIME_API_KEY across nodes, a
WebSocket-friendly load balancer (forward Upgrade, idle timeout > 60s), and a real
REALTIME_ALLOWED_ORIGINS (never *). Full detail in
Architecture & Realtime.
Verify it's healthy
# All containers up?
docker compose ps
# Redis is mandatory infrastructure — this must report PONG
docker compose exec redis redis-cli ping
# Health endpoint (reports Redis availability among others)
curl -s http://localhost:8000/api/health
# Background worker should show messenger:consume activity
docker compose logs -f worker
If /api/health returns 503, Redis is unreachable — verify the redis container is up
and REDIS_DSN is correct. More symptoms and fixes are in the
Developer FAQ → Common Issues.
Configuration in one place
All settings live in backend/.env, created from a documented template:
cp backend/.env.example backend/.env
You only need the defaults already in the example to get a working install — everything else (provider keys, email, channels, realtime secrets) is opt-in. The Developer FAQ breaks down each section and how to add provider keys.
Next steps
- Developer FAQ — install options,
.envreference, adding API keys, troubleshooting - Architecture & Realtime — the full service map, Redis, Centrifugo, clustering
- Code Examples — call the API in cURL, JavaScript, PHP
- Widget Integration — embed the chat widget on any site
- Plugins & Integrations — Synaads, Synaform, TTS, Nextcloud, OpenCloud