Local Deployment
Outpost per Docker Compose selbst betreiben — der Weg für Self-Hosting-Profis
Diese Seite beschreibt den Docker-Weg: einen selbst betriebenen Outpost auf deiner eigenen Hardware. Danach hast du eine laufende, mit meinGPT verbundene Outpost-Instanz auf deinem Server. Stelle sicher, dass die Voraussetzungen erfüllt sind, bevor du startest.
Nur auf einem Arbeitsplatz-Rechner?
Wenn du keinen eigenen Server betreibst, ist die Desktop-App meist der einfachere Weg: geführte Kopplung, Embeddings bereits integriert, automatische Updates — ganz ohne Docker. Dieser Docker-Weg ist für Teams, die volle Kontrolle über jeden Baustein wollen.
Überblick
Der lokale Outpost besteht aus diesen Diensten, verwaltet per Docker Compose:
| Dienst | Zweck |
|---|---|
| Outpost | API-Server — beantwortet Anfragen und liefert Datei-Downloads |
| Outpost-worker | Ingestion-Pipeline — verarbeitet und indexiert Dokumente |
| database | PostgreSQL mit VectorChord — speichert Ingestion-Metadaten und Vektor-Embeddings |
| ollama | Lokaler Embedding-Server (OpenAI-kompatible API) — eine von mehreren Optionen |
| piko | Der gebündelte Tunnel-Sidecar — verbindet den Outpost mit meinGPT, ohne Ports öffentlich zu machen |
Tunnel: Sidecar heute, Go-Bridge als Richtung
Der Docker-Weg nutzt heute den gebündelten Tunnel-Sidecar (die piko-Komponente) als Standard-Transport. Wir migrieren schrittweise auf den Go-Bridge-Agenten, den die Desktop-App bereits einsetzt. Halte dich vorerst an den hier gezeigten Sidecar.
Dein Projektverzeichnis sieht am Ende so aus:
datavault-local/
├── config/
│ ├── vault.env # Zugangsdaten und IDs (aus den meinGPT-Einstellungen)
│ └── app_config.yaml # Vault-Konfiguration
├── data/
│ ├── vault/ # Lokale Dokumente für die Ingestion (optional)
│ ├── postgres/ # Postgres-Daten (wird automatisch befüllt)
│ └── ollama/ # Cache des Embedding-Modells (wird automatisch befüllt)
├── documents/ # Optionaler zusätzlicher Read-only-Mount
└── docker-compose.yaml # Dienst-Definitionen
data/vault/ brauchst du nur, wenn du lokale Dateien von der Festplatte indexieren willst. Sind alle Quellen cloud-basiert (SharePoint, Google Drive usw.), kannst du es weglassen.
Schritt 1 — Verzeichnisstruktur anlegen
mkdir datavault-local
cd datavault-local
mkdir -p config data/vault data/postgres data/ollama documentsSchritt 2 — Environment-Datei anlegen
Hole dir Outpost ID und Outpost Secret aus dem meinGPT-Dashboard (siehe Outpost anlegen → ID & Secret) und trage sie unten ein.
VAULT_ID=your-vault-id
VAULT_SECRET=your-vault-secret
MEINGPT_URL=https://app.meingpt.com
POSTGRES_USER=datavault
POSTGRES_PASSWORD=your-postgres-password
OPENAI_BASE_URL=http://ollama:11434/v1
OPENAI_API_KEY=local-dev
OPENAI_EMBEDDING_MODEL=bge-m3
OPENAI_EMBEDDING_DIMENSIONS=1024Schritt 3 — Outpost-Konfiguration anlegen
Werte wie $VAULT_ID verweisen auf die Environment-Variablen aus vault.env — sie werden zur Laufzeit automatisch aufgelöst.
version: 1.0
meingpt_url: $MEINGPT_URL
vault:
id: $VAULT_ID
secret: $VAULT_SECRET
standalone_mode: false
data_dir: ./tmp
ingestion_interval: 300
tasks_batch_size: 3
chunk_size: 256
chunk_overlap: 26
metadata:
# 'deployment_type' ist nur ein Legacy-Label — der Storage nutzt immer PostgreSQL/VectorChord.
# 'vault' und 'vault-worker' müssen auf DIESELBE Postgres zeigen, damit sie sich die Task-Queue teilen.
deployment_type: "cloud"
postgres:
user: $POSTGRES_USER
password: $POSTGRES_PASSWORD
host: database
port: 5432
database: datavault
embedding_model:
provider: "openai"
model: $OPENAI_EMBEDDING_MODEL
base_url: $OPENAI_BASE_URL
api_key: $OPENAI_API_KEY
embedding_dimensions: $OPENAI_EMBEDDING_DIMENSIONS
rpm: 1000
tpm: 100000
logging:
log_level: "INFO"
log_to_file: true
log_file_path: "logs/app.log"
uvicorn_log_file_path: "logs/uvicorn.log"
data_pools:
- id: your-datapool-id-from-meinGPT
type: local
base_path: /data/vaultHinweis
Lass standalone_mode: false und den piko-Dienst aktiv. Das ist der Tunnel-Sidecar, der deinen lokalen Outpost mit meinGPT verbindet.
Schritt 4 — Docker-Compose-Datei anlegen
Hinweis
Ersetze your-vault-id im piko-Kommando unten durch der Outpost ID aus vault.env.
services:
vault:
image: meingpt/vault:latest
ports:
- 8080:8080
depends_on:
database-init:
condition: service_completed_successfully
ollama:
condition: service_healthy
restart: unless-stopped
networks:
- vault_network
volumes:
- ./config:/etc/vault:ro
- ./data/vault:/data/vault
# Optional: lokales Verzeichnis für die Ingestion einbinden
- ./documents:/app/documents:ro
environment:
- VAULT_CONFIG_FILE_PATH=/etc/vault/app_config.yaml
env_file:
- ./config/vault.env
vault-worker:
image: meingpt/vault:worker-latest
ports:
- 8081:8080
depends_on:
database-init:
condition: service_completed_successfully
ollama:
condition: service_healthy
vault:
condition: service_started
restart: unless-stopped
networks:
- vault_network
volumes:
- ./config:/etc/vault:ro
- ./data/vault:/data/vault
# Optional: lokales Verzeichnis für die Ingestion einbinden
- ./documents:/app/documents:ro
environment:
- VAULT_CONFIG_FILE_PATH=/etc/vault/app_config.yaml
env_file:
- ./config/vault.env
database:
image: tensorchord/vchord-suite:pg17-20260401
command:
- postgres
- -c
- shared_preload_libraries=vchord.so,vchord_bm25.so,pg_tokenizer.so
- -c
- search_path="$$user", public, bm25_catalog, tokenizer_catalog
ports:
- 5432:5432
env_file:
- ./config/vault.env
environment:
- POSTGRES_DB=datavault
volumes:
- ./data/postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d datavault"]
interval: 5s
timeout: 5s
retries: 20
networks:
- vault_network
database-init:
image: tensorchord/vchord-suite:pg17-20260401
depends_on:
database:
condition: service_healthy
env_file:
- ./config/vault.env
environment:
- POSTGRES_DB=datavault
entrypoint: ["/bin/sh", "-c"]
command:
- |
PGPASSWORD="$${POSTGRES_PASSWORD}" psql \
-h database \
-U "$${POSTGRES_USER}" \
-d "$${POSTGRES_DB}" <<'SQL'
\set ON_ERROR_STOP on
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS vchord CASCADE;
CREATE EXTENSION IF NOT EXISTS pg_tokenizer CASCADE;
CREATE EXTENSION IF NOT EXISTS vchord_bm25 CASCADE;
SET search_path TO "$$user", public, bm25_catalog, tokenizer_catalog;
DO $$$$
BEGIN
EXECUTE format(
'ALTER DATABASE %I SET search_path TO "$$user", public, bm25_catalog, tokenizer_catalog',
current_database()
);
END
$$$$;
DO $$$$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM tokenizer_catalog.tokenizer
WHERE name = 'chunks_token'
) THEN
PERFORM create_tokenizer('chunks_token', $$tokenizer$$
model = "llmlingua2"
$$tokenizer$$);
END IF;
END
$$$$;
SQL
restart: "no"
networks:
- vault_network
ollama:
image: ollama/ollama:latest
# Beim ersten Start das Embedding-Modell vorab laden, damit vault es sofort nutzen kann.
entrypoint: ["/bin/sh", "-c"]
command:
- |
ollama serve &
SERVE_PID=$$!
export OLLAMA_HOST=http://127.0.0.1:11434
until ollama list >/dev/null 2>&1; do sleep 1; done
ollama pull bge-m3
wait $$SERVE_PID
volumes:
- ./data/ollama:/root/.ollama
networks:
- vault_network
healthcheck:
# Wird erst „healthy", wenn das Embedding-Modell vollständig geladen ist —
# das gated den Start von vault/vault-worker, damit erste Embed-Calls nicht fehlschlagen.
test: ["CMD-SHELL", "OLLAMA_HOST=http://127.0.0.1:11434 ollama list | grep -q bge-m3"]
interval: 10s
timeout: 5s
retries: 60
start_period: 30s
piko:
# Der gebündelte Tunnel-Sidecar (heute Piko; Migration auf den Go-Bridge-Agenten läuft).
image: ghcr.io/andydunstall/piko:latest
command:
- agent
- http
# "your-vault-id" durch die Vault ID aus vault.env ersetzen
- your-vault-id
- vault:8080
- --connect.url
- https://vault-proxy.meingpt.com
env_file:
- ./config/vault.env
depends_on:
vault:
condition: service_started
networks:
- vault_network
networks:
vault_network:Embedding-Modell: Wahl, kein Muss
Diese Konfiguration nutzt bge-m3 (1024 Dim, mehrsprachig — funktioniert gut für Deutsch) über den lokalen ollama-Dienst. Das ist eine bequeme Standardwahl für Docker, kein Muss: Du kannst jeden OpenAI-kompatiblen Endpunkt einsetzen — etwa nomic-embed-text (768 Dim), mxbai-embed-large (1024 Dim) oder einen Cloud-Anbieter. Passe dazu OPENAI_BASE_URL, OPENAI_EMBEDDING_MODEL und OPENAI_EMBEDDING_DIMENSIONS in vault.env an. Alle Optionen: Embedding Models.
Anders bei der Desktop-App: Dort ist ein offline nutzbares, für Deutsch optimiertes Embedding-Modell bereits integriert — kein Ollama, keine GPU, keine Einrichtung.
GPU: Ollama läuft in Docker standardmäßig nur auf der CPU. Mit einer NVIDIA-GPU auf dem Host ergänze deploy: { resources: { reservations: { devices: [{ driver: nvidia, count: all, capabilities: [gpu] }] } } } im ollama-Dienst für deutlich mehr Durchsatz.
Schritt 5 — Deployen
- Wenn du lokale Dateien indexieren willst, lege sie in
data/vault/ab - Images holen:
docker compose pull - Dienste starten:
docker compose up -d - Health prüfen:
curl http://localhost:8080/health/ - Logs beobachten:
docker compose logs -f database ollama vault vault-worker
Beim ersten Start lädt ollama das Modell bge-m3. vault und vault-worker warten, bis es verfügbar ist.
Troubleshooting
- Dienst-Status prüfen:
docker compose ps - API-Logs ansehen:
docker compose logs vault - Ingestion-Logs ansehen:
docker compose logs vault-worker - Postgres testen:
docker compose exec database pg_isready -U datavault -d datavault - Ollama testen:
docker compose exec ollama sh -c "OLLAMA_HOST=http://127.0.0.1:11434 ollama list" - Dienste neu starten:
docker compose restart
Häufiger Stolperstein: Ingestion läuft nicht
Wenn die Ingestion nie startet (keine Fehler, aber Dokumente bleiben un-indexiert), prüfe, ob der metadata.postgres-Block in app_config.yaml vorhanden ist und beide — vault und vault-worker — denselben database-Host auflösen. Sie müssen sich eine Postgres teilen, damit der Worker die Tasks sieht, die die API einreiht.
Häufiger Stolperstein: Datenbank falsch initialisiert
Wenn du einmal mit falschem Datenbank-Image oder falschem Volume-Pfad gestartet hast, entferne das kaputte Datenverzeichnis und starte neu:
docker compose down
rm -rf data/postgres
mkdir -p data/postgres
docker compose up -d