· Datenbank

PostgreSQL: Die Datenbank die alles kann

PostgreSQL ist die erste Wahl wenn relationale Daten, JSON-Dokumente, Volltext-Suche und Vektor-Suche in einer Datenbank zusammenkommen sollen. Robust, erweiterbar, kostenlos – und auf jedem Server in Minuten deployt.

Zuletzt aktualisiert:

SQLpgvectorJSONBDockerBackups

PostgreSQL für Automatisierungen und KI-Projekte

Warum PostgreSQL statt SQLite oder MySQL? Drei überzeugende Gründe für moderne Projekte:

  • JSONB: JSON-Dokumente als natives Typ speichern und indizieren – relationale und dokumentenbasierte Daten in einer Datenbank. Für n8n-Workflow-Daten ideal.
  • pgvector: Extension für Vektor-Suche direkt in PostgreSQL – kein separater Vektordatenbank-Service nötig. Für RAG-Systeme und Embeddings der pragmatischste Ansatz wenn schon PostgreSQL läuft.
  • Full-Text-Search: tsvector und tsquery – deutsche Stemming-Unterstützung eingebaut. Für Suche in eigenen Inhalten ohne Elasticsearch.
-- pgvector: Ähnlichkeitssuche in PostgreSQL CREATE EXTENSION vector; CREATE TABLE dokumente ( id SERIAL PRIMARY KEY, inhalt TEXT, embedding vector(768) ); -- Nearest Neighbor Search SELECT inhalt FROM dokumente ORDER BY embedding <-> '[0.1, 0.2, ...]' LIMIT 5;

PostgreSQL mit Docker betreiben

PostgreSQL als Docker Compose-Service ist der Standard für selbst gehostete Setups:

  • Persistenz: Volume für /var/lib/postgresql/data – ohne Volume gehen Daten beim Container-Neustart verloren
  • Connection Pooling: PgBouncer als Middleware für Anwendungen die viele kurze Verbindungen öffnen – reduziert PostgreSQL-Last massiv
  • Backups: pg_dump -Fc datenbankname > backup.dump für konsistente Backups. Empfehlenswert: automatisiert per Cron und mit einer Kopie außer Haus, etwa bei S3 oder Backblaze.
  • Versionen: Postgres 16 ist aktuell empfohlen. Image: postgres:16-alpine für kleinere Container-Größe

n8n, Gitea, Nextcloud und viele andere Services unterstützen PostgreSQL als Backend – eine Instanz für mehrere Services (separate Datenbanken, separate Nutzer).

Performance und Monitoring

PostgreSQL läuft gut mit Standardkonfiguration – aber ein paar Anpassungen helfen:

  • shared_buffers: 25% des verfügbaren RAMs. Standard sind 128 MB – auf einem 4-GB-Server sollte es 1 GB sein. Das gilt für eine zentrale Datenbank mit Last. Kleine Dienst-Datenbanken laufen bei mir mit den 128 MB problemlos.
  • EXPLAIN ANALYZE: Für langsame Queries – zeigt den Execution Plan und wo Zeit verloren geht. pg_stat_statements Extension zeigt die teuersten Queries.
  • Indizes: Fehlendes Index ist die häufigste Performance-Ursache. CREATE INDEX CONCURRENTLY fügt Indizes ohne Table Lock hinzu.
  • Monitoring: postgres_exporter für Prometheus-Metriken, Grafana-Dashboard für Visualisierung

Eine Postgres-Instanz pro Dienst: So läuft es bei mir

Ich betreibe keine große zentrale PostgreSQL-Instanz. Jeder Dienst, der Postgres braucht, bekommt einen eigenen Container mit postgres:16-alpine (derzeit 16.14 und 16.15). Das hält Updates und Wiederherstellungen getrennt: Wenn ich einen Dienst neu aufsetze, betrifft das keinen anderen.

  • LiteLLM (KI-Gateway, Logs und Nutzung): 785 MB
  • Authentik (Login/SSO): 156 MB
  • SEO-Crawler: 23 MB
  • n8n: 16 MB

Alle vier laufen mit den Standardwerten shared_buffers = 128MB und max_connections = 100. Bei diesen Größen reicht das. So sieht das Grundmuster im Docker-Setup aus:

# docker-compose.yml (Ausschnitt) db: image: postgres:16-alpine environment: POSTGRES_DB: app POSTGRES_USER: app POSTGRES_PASSWORD: ${DB_PASSWORD} volumes: - ./pgdata:/var/lib/postgresql/data # kein ports: – nur im internen Docker-Netz erreichbar

Daneben gibt es eine PostgreSQL-14-Instanz direkt auf dem Host, mit rund 13 GB Datenverzeichnis. Sie trägt pgvector für eine RAG-Suche über ein großes Nachrichtenarchiv. Die Embeddings liegen dort direkt in Postgres, ohne separate Vektordatenbank.

-- Größe jeder Datenbank prüfen SELECT datname, pg_size_pretty(pg_database_size(datname)) FROM pg_database ORDER BY pg_database_size(datname) DESC;

Praxiswerte aus der Werkstatt (Stand: Oktober 2026)

  • Postgres-Container im Einsatz: 4, alle postgres:16-alpine
  • Größte Container-DB: 785 MB (KI-Gateway-Logs)
  • Kleinste Container-DB: 16 MB (n8n)
  • shared_buffers / max_connections: 128 MB / 100, unverändert
  • Host-Instanz mit pgvector: PostgreSQL 14, rund 13 GB
  • Parallel im Einsatz: SQLite für eine zweite n8n-Instanz und Paperless, MariaDB 11.8 für ältere PHP-Anwendungen

Wann Postgres, wann SQLite, und eine Lehre aus MariaDB

Nicht jeder Dienst bekommt bei mir Postgres. Eine zweite n8n-Instanz und Paperless laufen bewusst mit SQLite. Für einen Dienst mit einem schreibenden Prozess ist eine Datei einfacher als ein weiterer Container.

Die lehrreichste Panne kam aus der MariaDB-Ecke. In MariaDB 11.8 ist STRICT_TRANS_TABLES Standard. Ältere PHP-Anwendungen, die leere Strings in Zahlenspalten schrieben, liefen danach in Fatal Errors. Ich habe das je Anwendung im Code behoben, statt den Strict-Modus global abzuschalten. Global abschalten hätte die Fehler nur wieder unsichtbar gemacht.

-- MariaDB strict / PostgreSQL immer INSERT INTO rechnung (betrag) VALUES (''); -- PostgreSQL: ERROR: invalid input syntax for type numeric: ""

Für Postgres heißt das: Es ist von Haus aus so streng. Wer eine alte Anwendung von MySQL oder MariaDB umzieht, sollte mit genau diesen Fehlern rechnen und sie vorher mit Testdaten suchen. Wenn du so etwas nicht selbst betreiben willst, findest du das Angebot unter Hosting.

Häufige Fragen zu PostgreSQL

SQLite für: Einzel-Nutzer-Apps, Prototypen, wenn die App die einzige ist die auf die Daten zugreift. PostgreSQL für: alles was mehr als einen gleichzeitigen Schreiber hat, wenn pgvector oder JSONB gebraucht wird, oder wenn die App produktiv genutzt wird. Wechsel später ist aufwändig – im Zweifel PostgreSQL.

Direkt im Docker Container: `docker exec -it postgres psql -U username datenbankname`. Von außen: `psql "postgresql://user:pass@host:5432/db"`. Grafisches Tool: DBeaver (kostenlos, alle Plattformen) oder pgAdmin 4 als Docker Container.

Nie Port 5432 direkt ins Internet öffnen. Separate DB-User mit minimalen Rechten pro Anwendung (nur SELECT/INSERT/UPDATE, kein DROP). Starke Passwörter, in `.env`-Datei gespeichert. Backups verschlüsseln bevor sie das Haus verlassen.

Migrations-Tool nutzen: Alembic für Python/SQLAlchemy, Flyway oder Liquibase für Java, Prisma Migrate für Node. Nie Schema direkt in Produktion ändern ohne Migration. Immer Backup vor Migration. Rollback-Migration vorbereiten bevor die Forward-Migration ausgeführt wird.

Bei kleinen Dienst-Datenbanken nutze ich je Dienst einen eigenen Container. Die Größen liegen bei mir zwischen 16 und 785 MB, der Mehrbedarf an RAM ist gering, und Updates lassen sich einzeln fahren. Eine gemeinsame Instanz lohnt sich eher, wenn du Monitoring und Backups zentral bündeln willst.

Fragen per WhatsApp