Aller au contenu

Shared Services

Overview

PostgreSQL and Redis run as shared containers in /opt/docker/shared/, serving all environments (dev, staging, prod) and all apps (Aletheia, Helios, Umami, monitoring). Both are on the backend Docker network. App containers connect by container name.

Container Image OOM Score Memory Limit Port
shared_postgres postgres:18-alpine -800 127.0.0.1:5432:5432
shared_redis redis:7-alpine -500

PostgreSQL

Connection details

Parameter Value
Container shared_postgres
Port 5432 (bound to 127.0.0.1 only — not exposed externally)
Admin user admin (credentials in /opt/docker/shared/.env)
Data directory /var/lib/postgresql/18/docker (Docker volume postgres_data)
Init scripts /opt/docker/shared/init-scripts/ (run once on first start)

Databases and users

Created by 01-create-databases.sql on first startup:

Database User Used by
aletheia_prod aletheia_prod Aletheia production
aletheia_staging aletheia_staging Aletheia staging
aletheia_dev aletheia_dev Aletheia development
umami umami Umami analytics
monitoring monitoring Grafana (read-only Prometheus data)

Each app user owns only its database. The admin user has superuser access to all.

Common operations

Connect to a database:

docker exec -it shared_postgres psql -U aletheia_prod -d aletheia_prod

List databases and sizes:

docker exec shared_postgres psql -U admin -c \
  "SELECT datname, pg_size_pretty(pg_database_size(datname)) FROM pg_database WHERE datistemplate = false;"

Check active connections:

docker exec shared_postgres psql -U admin -c \
  "SELECT datname, usename, state, count(*) FROM pg_stat_activity GROUP BY 1,2,3 ORDER BY 1;"

Check for long-running queries:

docker exec shared_postgres psql -U admin -c \
  "SELECT pid, now() - query_start AS duration, query
   FROM pg_stat_activity WHERE state != 'idle' ORDER BY duration DESC LIMIT 10;"

Create a new database (e.g. for a new app):

docker exec shared_postgres psql -U admin -c "CREATE USER new_app WITH PASSWORD 'secure-password';"
docker exec shared_postgres psql -U admin -c "CREATE DATABASE new_app OWNER new_app;"

Then add the credentials to an env file and run make encrypt to persist.

Configuration

PostgreSQL runs with default alpine settings. Key parameters can be tuned via command flags in docker-compose.yml or a custom postgresql.conf mount.

Current defaults that matter:

Parameter Default Notes
shared_buffers 128 MB 25% of RAM is recommended for dedicated servers
work_mem 4 MB Per-sort/hash operation
max_connections 100 Shared across all apps and environments

OOM protection

PostgreSQL has oom_score_adj: -800 in docker-compose.yml, making it the last container the kernel kills under memory pressure. This is critical — losing PostgreSQL would affect all apps simultaneously.

Backup & restore

See backups for the full procedure. Quick reference:

# Manual dump
docker exec shared_postgres pg_dump -U aletheia_prod -Fc aletheia_prod > /tmp/backup.dump

# Restore (see backups.md for the full drop/recreate/restore procedure)
docker exec shared_postgres pg_restore -U aletheia_prod -d aletheia_prod -Fc --no-owner /tmp/backup.dump

Major version upgrades

PostgreSQL major upgrades (e.g. 18 → 19) require a data migration — you cannot simply change the image tag. The procedure:

  1. Take a full backup of all databases
  2. Stop the postgres container
  3. Run pg_upgrade (or dump/restore to a new container)
  4. Update the image tag in shared/docker-compose.yml
  5. Update the PGDATA volume path if it changes
  6. Start the new container and verify all databases
  7. Update the aether repo: make pull && git commit

Warning

Always test major upgrades on a copy first. The PGDATA path includes the major version number (/var/lib/postgresql/18/docker), so changing versions means changing the volume mount.

Redis

Connection details

Parameter Value
Container shared_redis
Port 6379 (not exposed to host — containers only)
Max memory 128 MB (--maxmemory 128mb)
Eviction policy allkeys-lru (least recently used keys evicted when full)
Persistence RDB snapshots to volume redis_data

Database allocation

Redis has 16 databases (0-15). They're allocated by environment:

DB Environment Usage
0 Production Celery broker
1 Production Django cache
2 Staging Celery broker
3 Staging Django cache
4 Dev Celery broker
5 Dev Django cache

This allocation is configured in each app's .env file via REDIS_URL and CACHE_REDIS_URL.

Common operations

Test connectivity:

docker exec shared_redis redis-cli ping
# Expected: PONG

Check memory usage:

docker exec shared_redis redis-cli info memory | grep used_memory_human

Check keys per database:

docker exec shared_redis redis-cli info keyspace

Flush a specific database (e.g. clear dev cache):

docker exec shared_redis redis-cli -n 5 flushdb

Monitor commands in real-time (useful for debugging):

docker exec shared_redis redis-cli monitor
# Ctrl+C to stop

OOM protection

Redis has oom_score_adj: -500 — lower priority than PostgreSQL but higher than app containers. With allkeys-lru eviction, Redis will drop old keys before running out of memory rather than crashing.

Restarting Shared Services

cd /opt/docker/aether/repo && make restart-shared

This runs docker compose up -d in /opt/docker/shared/, which recreates containers only if the compose file or image changed. Existing data in volumes is preserved.

Warning

Restarting shared services drops all active database connections and Celery broker connections across all environments. Apps will reconnect automatically, but active requests and running Celery tasks may fail.

Monitoring

Both services are monitored via Grafana dashboards:

  • PostgreSQL dashboard: connections, query stats, replication, vacuum activity
  • Redis dashboard: memory, keyspace, commands/sec, hit rate
  • Alerts: PostgresDown, PostgresConnectionsHigh (see runbooks)