Self-hosted Nextcloud + Collabora Online — internal document hub and wiki for the team. Upload PDFs / Word / Excel and view & edit them in the browser, alongside markdown wiki pages. Local-first dev, moves to a small VPS for production.
Stack: Nextcloud 30 · Collabora Online (CODE) · MariaDB 11 · Redis 7 · Docker Compose · local accounts auth
- What this is
- What you get out of the box
- Architecture at a glance
- Quick start (TL;DR)
- Prerequisites
- First-time setup, step by step
- Day-to-day usage
- User and permission management
- AI on top (Google Gemini)
- Backup and restore
- Updating Nextcloud
- Command cheatsheet
- Troubleshooting
- Secrets
- Project layout
- Roadmap
A working Nextcloud + Collabora Online setup you run locally with one command, then later promote to a small VPS with TLS and access control.
Unlike a pure wiki tool, this stack treats uploaded documents as
first-class content: you can upload a PDF and read it in-browser, upload a
.docx and edit it collaboratively in-browser, alongside markdown wiki
pages. All of it is searchable, shareable, version-tracked, and ACL-aware.
Why this stack:
- Nextcloud — file management, sharing, ACL, versioning, search, mobile/desktop sync clients
- Collabora Online (CODE) — in-browser PDF / Word / Excel / PowerPoint viewing and real-time multi-cursor editing
- Collectives app (installed in Nextcloud) — markdown-based wiki pages with linked navigation
- All open-source, self-hostable, single docker-compose
| Capability | Included |
|---|---|
| Drag-and-drop upload of any file type | Yes |
| Folder hierarchy with per-folder permissions | Yes |
| In-browser PDF viewer | Yes (Nextcloud native) |
| In-browser image / video / audio preview | Yes |
In-browser .docx / .xlsx / .pptx viewing and editing |
Yes (via Collabora) |
| Real-time multi-cursor editing of Office docs | Yes (Collabora) |
| File versioning with restore | Yes |
| Tags + comments on files | Yes |
| Activity feed (who did what) | Yes |
| Full-text search inside documents | Yes (Nextcloud Full Text Search app) |
| Trash / recycle bin | Yes |
| Capability | Included |
|---|---|
| Markdown pages organised in nested "collectives" | Yes (Collectives app) |
| Page-level permissions | Yes |
| Live preview while editing | Yes |
| Mermaid diagrams, callouts, mentions | Yes |
| Page revisions | Yes |
| Capability | Included |
|---|---|
| Local user accounts (email + password) | Yes (configured) |
| Group-based ACL | Yes |
| Share with a specific user | Yes |
| Public link share with optional password + expiry + download counter | Yes |
| External share without account | Yes |
| Federated sharing (between Nextcloud instances) | Available |
| SSO (Google / Microsoft / SAML / LDAP) | Available, deferred |
| Capability | Included |
|---|---|
| File-level AI tasks (summarise, translate, rewrite) | Yes (via Gemini, OpenAI compat) |
Smart Picker - / AI in any text field |
Yes |
| Free-prompt chat (Assistant) | Yes |
| RAG chat over all KB files with citations | Yes (Context Chat + cc-backend) |
| Scope chat context: single folder / by tag / by owner / specific files | Yes (Sources picker) |
| Switch LLM (Gemini → OpenAI / Anthropic / local Ollama) | Yes (one occ config change) |
| Capability | Included |
|---|---|
| Backup script (DB + data volume) | Yes |
| Maintenance mode toggle | Yes |
occ admin CLI |
Yes |
| Mobile / desktop sync clients | Yes (download separately) |
Browser
│
├──► http://localhost:8080 (Nextcloud — files & UI)
├──► http://localhost:9980 (Collabora — Office editor iframe)
└──► http://localhost:10034 (Context Chat backend — internal API)
┌──────────────────────────────────────────────────────────────┐
│ Docker network: kb_default │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌────────────┐ │
│ │ kb-app │◄─►│ kb-db │ │ kb-redis │ │
│ │ Nextcloud │ │ MariaDB │ │ Redis 7 │ │
│ │ Apache+PHP │ │ 11.4 │ │ sessions │ │
│ └──┬──┬───┬───┘ └─────────────┘ │ + cache │ │
│ │ │ │ └────────────┘ │
│ │ │ │ WOPI │
│ │ │ ▼ │
│ │ │ ┌──────────────────┐ │
│ │ │ │ kb-collabora │ In-browser Office editor │
│ │ │ │ collabora/code │ (PDF.js + LO core) │
│ │ │ └──────────────────┘ │
│ │ │ │
│ │ │ HTTPS to Google │
│ │ └─────► Gemini API (via OpenAI compat) │
│ │ │
│ │ AppAPI / HTTP │
│ ▼ │
│ ┌────────────────────┐ │
│ │ kb-cc-backend │ RAG: embeddings + vector store │
│ │ context_chat_ │ Indexes files, returns chunks │
│ │ backend │ for the chat to ground answers │
│ └────────────────────┘ │
│ │
│ Persistent volumes: │
│ app_data — Nextcloud code, config, USER FILES │
│ db_data — MariaDB data dir │
│ redis_data — Redis persistence │
│ cc_backend_data — vector index + embedding model cache │
└───────────────────────────────────────────────────────────────┘
kb-appruns Nextcloud (PHP/Apache) — the UI and file API.kb-dbholds users, groups, permissions, file metadata, activity.kb-rediscaches sessions and handles file locking.kb-collaborarenders PDFs and serves the Office editor.kb-cc-backendowns the vector index + embedding model for RAG. The Context Chat Nextcloud app talks to it over HTTP via AppAPI.- Persistent volumes are the only place your data lives. Lose them, you lose everything. Back them up (see §10).
# 1. Generate secrets (Git Bash on Windows, or any shell with openssl)
bash scripts/gen-secrets.sh > .env
# 2. Paste your Google AI Studio key into .env (replace REPLACE_ME):
# GOOGLE_AI_API_KEY=AIza...your-key...
# Get one at https://aistudio.google.com/apikey
# 3. Start everything — containers, AI wiring, RAG wiring — in one shot:
bash scripts/start.sh
# 4. When the launcher prints "Nextcloud is running":
# Open http://localhost:8080
# Log in with the username/password the script printedstart.sh orchestrates AI (configure-ai.sh) and RAG (configure-rag.sh)
automatically once Nextcloud is responding. Both are idempotent — re-runs
skip already-wired steps. First boot of the RAG container downloads an
embedding model (~5-10 min); subsequent runs are seconds.
That's it. The rest of this README is reference.
You need Docker Desktop for Windows or macOS, or docker + docker compose on Linux. On Windows 11 it uses WSL 2 — accept the default
during install.
-
Download from https://www.docker.com/products/docker-desktop/.
-
Run the installer. Reboot if prompted.
-
Open Docker Desktop and wait for the whale icon in the tray to settle. Green = running.
-
Verify in a terminal (Git Bash, PowerShell, or Terminal):
docker --version docker compose version
| Hardware | Verdict |
|---|---|
| 16 GB RAM, 4+ cores | Comfortable for the full stack: Nextcloud + Collabora + cc-backend |
| 8 GB RAM | Works for Nextcloud + Collabora; stop cc-backend (RAG) when not in use to free ~2 GB |
| < 8 GB RAM | Not recommended |
The full stack (with cc-backend running) idles around 4-5 GB RAM. The
cc-backend container alone holds a ~2 GB embedding model in memory.
Disk usage starts ~5 GB after first pull, grows with content and the
vector index (figure ~2× the size of indexed text).
The stack idles at ~2.5 GB RAM and uses CPU bursts when documents are opened or saved.
cd /c/Users/DanielPetrov/Desktop/Work/internal-kb
bash scripts/gen-secrets.sh > .envThis writes a gitignored .env with three random secrets and the admin
username (admin).
bash scripts/start.shThe launcher:
- Verifies Docker is up
- Validates
.envkeys are set - Pulls images (~1.5 GB first time)
- Brings up four containers (
db,redis,app,collabora) - Waits for
http://localhost:8080/status.phpto respond - Prints the URL + admin credentials
First boot takes 2–3 minutes (Nextcloud initialises the DB schema on first run, which is the slow part).
Open http://localhost:8080.
Credentials are the ones printed by start.sh. They're also stored in
.env under NEXTCLOUD_ADMIN_USER / NEXTCLOUD_ADMIN_PASSWORD.
Once in:
- Top-right avatar → Personal settings → set your real name + email.
- Security → enable 2FA (TOTP via Authy / Bitwarden / 1Password).
- Top-right avatar → + Apps.
- Search for Nextcloud Office → Download and enable.
- Top-right avatar → Administration settings → Office.
- Pick Use your own server → URL:
http://host.docker.internal:9980 - Save. The "Test connection" should turn green.
Now .docx, .xlsx, .pptx files open in the browser with full Office
editing.
- + Apps → search Collectives → Download and enable.
- A new "Collectives" entry appears in the left nav.
- Create your first collective ("Knowledge Base", say) — it becomes a wiki-style tree of markdown pages.
Inside Nextcloud → Files, create these top-level folders:
| Folder | Purpose |
|---|---|
01 - Onboarding |
New-joiner orientation, setup, accounts |
02 - Use Cases |
Product scenarios with steps and outcomes |
03 - Troubleshooting |
Known failures and their fixes |
04 - Support Playbooks |
Step-by-step runbooks for support handoff |
05 - Architecture |
Diagrams, ADRs, design rationale |
06 - Integrations |
Third-party hookups, API contracts, config |
07 - FAQ |
Short-form answers and gotchas |
08 - Release Notes (optional) |
What changed and when |
Inside each folder, mix uploaded PDFs/Office docs with custom markdown
files (.md) as needed. Use Collectives separately for structured wiki
content where you want cross-page linking.
Two complementary content surfaces:
| Surface | Best for | Example |
|---|---|---|
| Files (folders + uploads) | Documents you receive or produce — PDFs, Word, Excel, diagrams, archives | Vendor SOWs, exported reports, signed contracts, raw screenshots |
| Collectives (markdown wiki) | Living team knowledge that you author from scratch | Runbooks, ADRs, FAQs, onboarding guides |
You can interlink them: a Collective page can embed or link to files in the Files area, and vice versa.
| You want to | How |
|---|---|
| Upload one or many files | Drag from desktop onto the Files page |
| Upload a folder | Drag the folder; structure is preserved |
| Upload from the URL bar | + New → Upload file |
| Create a file in-browser | + New → New document / spreadsheet / presentation / Plain text |
| Resume a big upload | Nextcloud chunks uploads automatically — refresh and re-drop the same file |
Default per-file upload limit is 512 MB (set via PHP_UPLOAD_LIMIT in the
compose). Raise it in docker-compose.yml if you need to attach larger
binaries.
| File type | What happens when you click | Editable in-browser |
|---|---|---|
.pdf |
Opens in PDF.js viewer (zoom, search, print) | View only |
.docx, .odt |
Opens in Collabora — full Word-style editor | Yes (collaborative) |
.xlsx, .ods |
Opens in Collabora — full Excel-style editor | Yes |
.pptx, .odp |
Opens in Collabora — full PowerPoint editor | Yes |
.md |
Opens in Markdown editor with live preview | Yes |
.txt, .json, .yaml, source code |
Opens in the Text editor | Yes |
.png, .jpg, .svg, .webp |
Opens in image viewer | View only |
.mp4, .mp3, .webm |
Opens in media player | View only |
Collaborative editing: when two people open the same .docx, both
see each other's cursor in real time. Changes auto-save. The Office file
on disk reflects the latest saved state.
Version history: right-click a file → Versions. Restore any previous version with one click. Major edits create snapshots automatically.
A collective is a tree of markdown pages backed by a shared folder. The editor is markdown with live preview, plus a navigation tree, search, and page templates.
| You want to | How |
|---|---|
| Create a new collective | Collectives → + New collective |
| Create a sub-page | Open a parent page → + Add page |
| Link to another page | Type [[ and start typing — autocomplete pops up |
| Embed an image | Drag-drop into the editor — uploaded to the collective's folder |
| Use a template | Open page settings → Use a template |
| Search | Top-bar search includes collective content |
The actual markdown files live under Files → <collective name>/. You
can edit them from either place — changes sync.
Internal — share with a teammate
- Right-click file or folder → Details → Sharing tab.
- Type a username; pick permission (read, edit, share, delete).
- They see it in their Shared with you view immediately.
External — share with a public link
- Same sharing panel → + Share link → Copy link.
- Click the ⋯ next to the link to configure:
- Read-only / allow editing / allow upload (file drop)
- Password protect
- Set expiration date
- Hide download (preview only)
- Disable downloading by viewers
- Send the link. Recipients don't need an account.
Per-folder permissions for whole groups
Settings → Users → create a group → assign users. Then share a folder with the group with a chosen permission set.
The top-bar search is unified:
- File names
- Tags
- File contents (PDF, Office, markdown, text) — once the Full Text Search app is enabled
- Collective pages
- Activity / mentions
To enable content search:
- + Apps → search Full text search → enable.
- Also enable Full text search - Files and Full text search - Elasticsearch (skip Elasticsearch — uses bundled index by default).
- Admin Settings → Full text search → Index files.
Indexing runs in the background; new uploads index automatically.
| Action | How |
|---|---|
| Add a tag to a file | Right-click → Details → Tags |
| Browse all files with a tag | Left nav → Tags → click a tag |
| Restrict who sees a tag | Tag visibility: public / restricted / invisible |
| Filter file list by tag | Tags menu in the Files header |
Useful patterns:
team:support— owning teamstatus:draft/status:finalseverity:p1— incident docsclient:foo— per-client folders
Optional but powerful — Nextcloud has native sync apps so files appear locally on team members' machines:
- Desktop: https://nextcloud.com/install/#install-clients (Windows / macOS / Linux)
- Mobile: Nextcloud app in App Store / Play Store
- Settings → connect to
http://localhost:8080(locally) orhttps://kb.your-domain.com(production) with the same credentials
Users get a Nextcloud/ folder that mirrors their shared files. Edits
sync both ways.
- Top-right avatar → Users → + New user.
- Set username, name, email, password.
- Add to one or more groups.
- Save.
The new user can log in immediately. Share credentials securely (Bitwarden, 1Password, Signal — not email).
When we move to production we'll wire SMTP so Nextcloud sends a welcome / password-reset email instead.
Groups are how you batch-assign permissions:
- Create groups under Users → Groups (left side).
- Assign users to groups when creating / editing them.
- When sharing, share with a group rather than each user.
Suggested initial groups:
| Group | Members |
|---|---|
team |
The 4 core team members |
support |
The support staff |
admins |
People allowed to change settings |
Nextcloud doesn't have "roles" in the BookStack sense — instead, group admins can manage their group's users, and a dedicated admin group controls site settings. Per-file/folder ACL is set via the sharing UI.
Off by default. Don't enable the Registration app unless you explicitly want anyone to sign up — for an internal KB that's almost always wrong.
The local stack ships AI-ready. You add a Google AI Studio API key to .env,
run one script, and Nextcloud gets file-level AI tasks plus a free-prompt
chat - powered by Gemini through Google's OpenAI-compatible endpoint.
| Capability | Where you trigger it |
|---|---|
| Summarise a file (PDF / Word / text / markdown) | Right-click the file → AI Tasks → Summarize |
| Translate a file | Right-click → AI Tasks → Translate |
| Free-prompt chat | Top-right Assistant icon → type your question |
| Smart Picker — AI inside any text field | Type / in any editor → pick "Ask AI" or other task |
| Ad-hoc Q&A over a single document | Open the file → Assistant icon → ask, with the doc auto-loaded as context |
For "ask the KB anything and get a cited answer", the stack also ships the
Context Chat Backend (cc-backend container) plus the Nextcloud
Context Chat frontend app. Embeddings + vector store run locally
inside cc-backend; completions go to Gemini via the OpenAI integration
configured in 9.3.
RAG is wired automatically by start.sh once AI is configured. Under
the hood, start.sh calls configure-rag.sh, which installs AppAPI,
registers cc-backend as an ExApp using the shared CC_BACKEND_SECRET
from .env, installs Context Chat, and triggers the initial index.
You can also run it standalone:
bash scripts/configure-rag.sh # idempotent, skips if already wired
bash scripts/configure-rag.sh --reset # unregister to start cleanFirst boot of
cc-backenddownloads an embedding model (~5-10 min).start.shwill block while it waits — that's expected.
How to scope a conversation
In the Context Chat panel (Assistant → "Chat with your KB"), the Sources picker controls what files the AI can see:
| Want to chat against… | How |
|---|---|
| Everything you can access | Leave Sources empty (default) |
| One folder | Sources → Add → pick the folder |
| All files with a given tag | Sources → Add → filter by tag name |
| All files owned by a given person | Sources → Add → filter by owner |
| A specific set of files | Sources → Add → pick the files individually |
ACL is always enforced - the chat never returns content the user can't already see via Files.
Re-index manually
docker compose exec -u www-data app php occ context_chat:scan --allNew files added later index automatically in the background.
Step 1 — get a Google AI Studio key
Visit https://aistudio.google.com/apikey while logged into your Google
account. Click Create API key, copy the value (starts with AIza…).
The free tier is generous (millions of tokens / month on
gemini-3.5-flashas of writing); for production with regular team use, upgrade to a paid Google Cloud billing-enabled key for higher rate limits.
Step 2 — paste it into .env
GOOGLE_AI_API_KEY=AIza...your-key...
Step 3 — bash scripts/start.sh (or re-run if already up)
start.sh invokes configure-ai.sh automatically once Nextcloud is
responding. The script:
- Installs (or enables) the
assistantandintegration_openaiNextcloud apps. - Points the OpenAI integration's service URL at Google's OpenAI-compatible endpoint (
https://generativelanguage.googleapis.com/v1beta/openai/). - Saves your API key into Nextcloud's app config.
- Sets the model named in
AI_MODEL(defaultgemini-3.5-flash) as the default chat model.
Both are idempotent — safe to re-run after rotating the key, swapping models, or upgrading apps. Skipped automatically when already wired.
After the script finishes:
- Upload a PDF or
.docxto Files → right-click → AI Tasks → Summarize. You'll get a short summary in a few seconds. - Open a text file → click the Assistant icon top-right → type a free-form prompt about it.
- Open Files or any text field → type
/→ pick Ask AI → ask anything.
Default is gemini-3.5-flash — cheap and fast, good enough for summaries
and Q&A. To switch model, edit AI_MODEL in .env and re-run
bash scripts/start.sh. configure-ai.sh will overwrite the stored
model on each run, so the change takes effect immediately.
# .env
AI_MODEL=gemini-3.5-pro # slower but higher quality
bash scripts/start.sh # picks up the new valueYou can also apply the change without restarting via occ:
docker compose exec -u www-data app php occ \
config:app:set integration_openai default_completion_model_id \
--value="gemini-3.5-pro"Any name supported by Google's OpenAI-compatible endpoint works
(gemini-3.5-flash, gemini-3.5-pro, gemini-2.5-*, ...).
gemini-3.5-flash pricing (Google AI Studio, mid-2026) is roughly
$0.30 per 1M input tokens and $2.50 per 1M output tokens. For a 6-person
team doing ~50 summaries / chats per day, expect under $10 / month. Free
tier covers typical evaluation usage.
By default everything you AI-summarise or chat about is sent to Google's endpoint. For sensitive folders:
- Don't run AI on them. Tag sensitive content and avoid the AI tasks menu on those files.
- Or switch to a local LLM later - swap the endpoint URL to a self-hosted Ollama on your VPS, no app code change required. Tradeoff: slower, lower quality.
- Or use Azure OpenAI with your enterprise tenant for EU data residency and contractual data-handling.
The OpenAI integration app accepts any OpenAI-compatible endpoint, so
switching providers is one occ config:app:set integration_openai url
away.
| Data | Where | Persists across docker compose down |
|---|---|---|
| File contents (your uploads) | kb_app_data volume → /var/www/html/data/ |
Yes |
| Users, groups, ACL, metadata | kb_db_data volume → MariaDB |
Yes |
| Nextcloud config | kb_app_data volume → /var/www/html/config/ |
Yes |
| Sessions / cache | kb_redis_data volume |
Yes (lossy is OK to drop) |
| Secrets | .env in the repo |
Yes (gitignored) |
docker compose down -vdeletes the volumes. Don't run with-vunless you mean to wipe everything.
bash scripts/backup.shThe script:
- Puts Nextcloud into maintenance mode (read-only) so the DB and files stay consistent.
- Dumps MariaDB to
backups/<stamp>/db.sql.gz. - Tars the entire
app_datavolume tobackups/<stamp>/data.tar.gz. - Releases maintenance mode.
The backups/ folder is gitignored.
For production we cron this + push the artefacts to Backblaze B2 or
Cloudflare R2 with rclone.
# Assuming a fresh `bash scripts/start.sh` against empty volumes,
# and BackupDIR=backups/2026-05-26_1432:
source .env
# 1. Put the freshly-started Nextcloud into maintenance mode
docker compose exec -u www-data app php occ maintenance:mode --on
# 2. Restore the database
gunzip -c "$BackupDIR/db.sql.gz" | \
docker compose exec -T db \
mariadb -u root -p"$DB_ROOT_PASSWORD" nextcloud
# 3. Restore the data + config
docker run --rm \
-v kb_app_data:/data \
-v "$(pwd)/$BackupDIR:/backup" \
alpine sh -c "cd /data && tar -xzf /backup/data.tar.gz"
# 4. Release maintenance mode + restart the app
docker compose exec -u www-data app php occ maintenance:mode --off
docker compose restart appdocker compose pull # fetch latest images
docker compose up -d # recreate containers
docker compose exec -u www-data app php occ upgrade # if prompted
docker compose logs -f app # watch for any migration outputNextcloud publishes a new major version about twice a year and frequent patch releases. Always back up before upgrading — once a major version is applied, downgrading is not supported.
Read release notes at https://nextcloud.com/changelog/.
| Action | Command |
|---|---|
| Start the whole stack (with checks) | bash scripts/start.sh |
| Start without the launcher | docker compose up -d |
| Stop (keep data) | docker compose stop |
| Stop + remove containers (keep data) | docker compose down |
| Stop + wipe all data | docker compose down -v |
| Restart just Nextcloud | docker compose restart app |
| Restart just Collabora | docker compose restart collabora |
| Tail Nextcloud logs | docker compose logs -f app |
| Tail Collabora logs | docker compose logs -f collabora |
| Tail DB logs | docker compose logs -f db |
| Pull newer images | docker compose pull |
| Shell into the app container | docker compose exec app bash |
Run an occ (Nextcloud CLI) command |
docker compose exec -u www-data app php occ <cmd> |
| List apps | docker compose exec -u www-data app php occ app:list |
| Enable an app | docker compose exec -u www-data app php occ app:enable <id> |
| Reset a user password from CLI | docker compose exec -u www-data app php occ user:resetpassword <user> |
| Run a backup now | bash scripts/backup.sh |
| Regenerate secrets | bash scripts/gen-secrets.sh > .env |
| Wire AI (after first boot) | bash scripts/configure-ai.sh |
| Wire RAG / Context Chat | bash scripts/configure-rag.sh |
| Re-index all files (RAG) | docker compose exec -u www-data app php occ context_chat:scan --all |
| RAG index stats | docker compose exec -u www-data app php occ context_chat:scan --stats |
| Unregister cc-backend (reset RAG) | bash scripts/configure-rag.sh --reset |
| Switch AI model | docker compose exec -u www-data app php occ config:app:set integration_openai default_completion_model_id --value="gemini-3.5-pro" |
| Symptom | Likely cause | Fix |
|---|---|---|
port 8080 already allocated |
Another service is on 8080 | Change the host port in docker-compose.yml and update OVERWRITEHOST to match |
| First boot stuck on "Initializing Nextcloud..." | DB not ready or wrong creds | docker compose logs db + docker compose logs app. If creds drifted, docker compose down -v + start.sh again (wipes data) |
| Login page loops / "Access through untrusted domain" | Browser URL not in NEXTCLOUD_TRUSTED_DOMAINS |
Add it to the env var in compose, then docker compose up -d |
| Collabora connect test fails | URL mismatch | In Office settings, use http://host.docker.internal:9980 (not http://localhost:9980) |
.docx opens but iframe is blank |
aliasgroup1 in Collabora doesn't match Nextcloud's URL |
Edit aliasgroup1 in compose to match the URL you load Nextcloud at (e.g. http://localhost:8080); docker compose up -d |
| Searching doesn't find content inside PDFs | Full Text Search not enabled / not indexed | Apps → enable Full text search + Files; Admin → Full text search → Index |
| Big upload fails with 413 | Hit the body / PHP limit | Raise PHP_UPLOAD_LIMIT (and client_max_body_size if behind a proxy) |
| Sync client says "untrusted certificate" | Local dev uses HTTP, sync clients want HTTPS | Use sync clients only after we set up TLS on production |
| Nextcloud says "PHP modules missing" in admin overview | Optional perf extension absent | Safe to ignore for local dev; we'll resolve in the production image |
configure-rag.sh errors with "cc-backend not responding" |
First boot is downloading the embedding model | Wait, watch docker compose logs -f cc-backend. Can take 5-10 min on first run. |
| RAG chat answers "I couldn't find anything in the indexed files" | Indexing hasn't run yet, or your account hasn't uploaded the files | docker compose exec -u www-data app php occ context_chat:scan --all and wait for it to finish |
| ExApp registration fails with "secret mismatch" | CC_BACKEND_SECRET in .env differs from what AppAPI stored |
bash scripts/configure-rag.sh --reset, then re-run configure-rag.sh |
app_api:daemon:register says "already exists" |
Re-running configure-rag.sh after a partial run | Safe to ignore — the script's "already registered, skipping" message handles this |
If a fix isn't here, check the logs first:
docker compose logs --tail 200 app
docker compose logs --tail 100 collabora
docker compose logs --tail 100 cc-backend
docker compose logs --tail 100 dbThen the Nextcloud admin manual at https://docs.nextcloud.com/server/30/admin_manual/.
.env is gitignored. Initial values were generated with openssl rand.
To rotate them, regenerate and overwrite:
bash scripts/gen-secrets.sh > .envThen recreate the containers so they pick up the new env:
docker compose down
bash scripts/start.shBe careful rotating the DB password in-place. The MariaDB image uses the value at first init; later changes need an
ALTER USERinside the DB. For a local dev stack it's easier todown -v+ restart from a clean state.
The initial Nextcloud admin password in
.envonly matters on first boot. After Nextcloud creates the admin user, change it through the UI; rotating it in.envlater has no effect on the existing account.
internal-kb/
├── docker-compose.yml Stack definition: db + redis + app + collabora
├── .env Generated secrets (gitignored)
├── .env.example Template + instructions for filling in .env
├── .gitignore Keeps secrets, backups, IDE noise out of git
├── README.md This file
├── scripts/
│ ├── start.sh Launcher: validates env, pulls, boots, prints URL + creds
│ ├── gen-secrets.sh Regenerate DB / admin / RAG secrets
│ ├── backup.sh Maintenance-mode-safe DB + data volume backup
│ ├── configure-ai.sh One-shot wiring of Google Gemini via OpenAI compat
│ └── configure-rag.sh Activate Context Chat (RAG over all KB files)
└── backups/ (created on first backup; gitignored)
Local dev:
- Docker Compose stack: MariaDB + Redis + Nextcloud + Collabora
- Local accounts auth (no SSO yet)
- Secrets generated, gitignored
- Backup script (maintenance-mode safe)
- One-command launcher (
scripts/start.sh) - AI wiring script (
scripts/configure-ai.sh— Google Gemini via OpenAI compat) - First boot verified
- Nextcloud Office app installed + Collabora URL set
- Collectives app installed
- Full Text Search app installed + initial index
- Google AI Studio API key in
.env+configure-ai.shrun - Suggested folder structure seeded
-
team/support/adminsgroups created - First user added (besides admin)
AI:
- Single-file AI tasks via Gemini (Assistant + OpenAI integration)
- Context Chat Backend container in compose
-
configure-rag.shactivator script (AppAPI + ExApp registration + index) - First RAG conversation tested with citations
- Source-scoping verified: folder, tag, owner filters working in chat
- Decide on local-LLM fallback (Ollama) for sensitive folders
Production:
- Hetzner CX31 (8 GB RAM) VPS provisioned
- Docker installed; non-root deploy user
- Caddy reverse proxy with Let's Encrypt TLS
- DNS for
kb.<your-domain> - Cloudflare Access (or simple login-walled) for off-network protection
- Daily backup cron + push to Backblaze B2 / Cloudflare R2
- SMTP wired (Resend / SendGrid) for invites and password reset
- UptimeRobot ping on the public URL
- Trusted domains and
OVERWRITEPROTOCOL=httpsset for prod - Collabora
aliasgroup1pointed at production HTTPS URL
Track progress in commits.
Internal use only. Not for external distribution.