Status: Accepted
Date: 2026-02-27
Deciders: IT-Stack Architecture Team
IT-Stack runs 20 services. Ten of them require a relational database:
| Service | Database Name | Primary Uses |
|---|---|---|
| Keycloak | keycloak |
Realm config, users, sessions |
| Nextcloud | nextcloud |
File metadata, shares, calendar |
| Mattermost | mattermost |
Messages, channels, users |
| Zammad | zammad |
Tickets, customers, articles |
| SuiteCRM | suitecrm |
Contacts, leads, campaigns |
| Odoo | odoo |
Accounting, HR, inventory |
| OpenKM | openkm |
Document metadata, workflows |
| Taiga | taiga |
Projects, issues, sprints |
| Snipe-IT | snipeit |
Assets, licenses, locations |
| GLPI | glpi |
CMDB, tickets, changes |
- Single DB engine to reduce operational overhead (one set of backup scripts, one monitoring config, one skills requirement)
- Strong relational integrity and foreign-key support
- JSON column support (Keycloak, Nextcloud, Odoo all store JSON blobs)
- Performance at 50–1,000 users
- All 10 upstream applications explicitly support it
- Open-source, no licensing cost
Use a single PostgreSQL 16 instance on lab-db1 (10.0.50.12) for all 10 service databases.
| Parameter | Value | Rationale |
|---|---|---|
| Version | PostgreSQL 16 | Latest LTS; all target apps support it |
| Host | lab-db1 (10.0.50.12) |
Dedicated 32 GB RAM server |
| Shared buffers | 8 GB |
25% of RAM, standard PG tuning |
| Connections | 200 max |
10 services × ~15 connections each + headroom |
| pg_hba.conf | scram-sha-256 |
No md5, no trust; encrypted auth only |
| Listen | 10.0.50.12 |
Bind to LAN IP, not 0.0.0.0 |
| backup | pg_dumpall nightly to /var/backups/postgresql/ |
All databases in one shot |
Each service gets its own database and role with no cross-database privileges:
-- Example pattern (Nextcloud)
CREATE DATABASE nextcloud OWNER nextcloud_user;
CREATE USER nextcloud_user WITH PASSWORD '...vault...';
GRANT ALL PRIVILEGES ON DATABASE nextcloud TO nextcloud_user;
-- nextcloud_user cannot access keycloak or mattermostRedis (lab-db1, port 6379) handles:
- Sessions — Keycloak, Nextcloud, Mattermost, Zammad
- Queues — Mattermost job server, Zammad background workers
- Cache — Nextcloud file metadata, APCu overflow
Redis does not replace PostgreSQL for persistent data.
- One backup target —
pg_dumpallbacks up all 10 databases in a single script - One monitoring config — Zabbix postgresql template covers all databases
- One Ansible role —
roles/postgresqlmanages the entire database tier - One upgrade event — upgrading PostgreSQL upgrades all service databases simultaneously
- Skills concentration — team learns one DB, not 3+ (MySQL, SQLite, MongoDB)
- No MySQL licensing ambiguity — PostgreSQL has an unambiguous open-source license (PostgreSQL License)
- Single point of failure — all 10 services lose database connectivity if
lab-db1goes down- Mitigation: PostgreSQL streaming replication to a standby (Phase 6 / production hardening)
- Resource contention — during heavy Odoo batch jobs, other services may see latency
- Mitigation:
pg_hba.confconnection limits per role;work_mem = 32MBprevents single-query RAM monopoly
- Mitigation:
- Large
pg_dumpallbackups — 10 databases, could exceed 50+ GB at scale- Mitigation:
pg_dumpper-database with compression; WAL archiving for point-in-time recovery
- Mitigation:
- SuiteCRM and GLPI prefer MySQL and have historically had PostgreSQL quirks
- Rejected: Both now fully support PostgreSQL; MySQL's licensing history (Oracle) conflicts with IT-Stack's open-source ethos
- Maximum isolation; one DB failure doesn't affect others
- Requires 10× the operational overhead for backup, monitoring, patching
- Rejected: over-engineered for 50–1,000 users; Phase 6 replication is sufficient
- Some services (Snipe-IT, GLPI) can use SQLite in single-user mode
- No multi-user concurrency; no replication; no
pg_dumpintegration - Rejected: production use requires a proper RDBMS