Files
rick-infra/roles/postgresql/README.md
Joakim 4f8da38ca6 Add Nextcloud cloud storage role with split Redis caching strategy
## New Features

- **Nextcloud Role**: Complete cloud storage deployment using Podman Quadlet
  - FPM variant with Caddy reverse proxy and FastCGI
  - PostgreSQL database via Unix socket
  - Valkey/Redis for app-level caching and file locking
  - Automatic HTTPS with Let's Encrypt via Caddy
  - Dual-root pattern: Caddy serves static assets, FPM handles PHP

- **Split Caching Strategy**: Redis caching WITHOUT Redis sessions
  - Custom redis.config.php template for app-level caching only
  - File-based PHP sessions for stability (avoids session lock issues)
  - Prevents cascading failures from session lock contention
  - Documented in role README with detailed rationale

## Infrastructure Updates

- **Socket Permissions**: Update PostgreSQL and Valkey to mode 777
  - Required for containers that switch users (root → www-data)
  - Nextcloud container loses supplementary groups on user switch
  - Security maintained via password authentication (scram-sha-256, requirepass)
  - Documented socket permission architecture in docs/

- **PostgreSQL**: Export client group GID as fact for dependent roles
- **Valkey**: Export client group GID as fact, update socket fix service

## Documentation

- New: docs/socket-permissions-architecture.md
  - Explains 777 vs 770 socket permission trade-offs
  - Documents why group-based access doesn't work for user-switching containers
  - Provides TCP alternative for stricter security requirements

- Updated: All role READMEs with socket permission notes
- New: Nextcloud README with comprehensive deployment, troubleshooting, and Redis architecture documentation

## Configuration

- host_vars: Add Nextcloud vault variables and configuration
- site.yml: Include Nextcloud role in main playbook

## Technical Details

**Why disable Redis sessions?**

The official Nextcloud container enables Redis session handling via REDIS_HOST env var,
which causes severe performance issues:

1. Session lock contention under high concurrency (browser parallel asset requests)
2. Infinite lock retries (default lock_retries=-1) blocking FPM workers
3. Timeout orphaning: reverse proxy kills connection, worker keeps lock
4. Worker pool exhaustion: all 5 default workers blocked on same session lock
5. Cascading failure: new requests queue, more timeouts, more orphaned locks

Solution: Use file-based sessions (reliable, fast for single-server) while keeping
Redis for distributed cache and transactional file locking via custom config file.

This provides optimal performance without the complexity of Redis session debugging.

Tested: Fresh deployment on arch-vps (69.62.119.31)
Domain: https://cloud.jnss.me/
2025-12-14 22:07:08 +01:00

7.4 KiB

PostgreSQL Infrastructure Role

This role provides PostgreSQL as shared database infrastructure for rick-infra applications. It follows the self-contained service architecture where this role provides the database server, and applications manage their own databases and users.

Architecture

PostgreSQL serves as database infrastructure (similar to how Caddy provides web infrastructure):

  • PostgreSQL Role: Installs and configures PostgreSQL server
  • Application Roles: Create their own databases/users via dependency

Features

  • Native Arch Linux installation via pacman
  • Secure configuration with scram-sha-256 authentication
  • SystemD security hardening
  • Data integrity with checksums enabled by default
  • Performance-tuned defaults
  • Comprehensive logging configuration
  • UTF-8 encoding with C.UTF-8 locale

Usage

In Application Roles

Add PostgreSQL as a dependency in your application's meta/main.yml:

dependencies:
  - role: postgresql
    tags: ['postgresql', 'infrastructure']

Then create your application's database and user in your tasks:

- name: Create application database user
  postgresql_user:
    name: "{{ myapp_db_user }}"
    password: "{{ myapp_db_password }}"
    encrypted: yes
  become: yes
  become_user: postgres

- name: Create application database
  postgresql_db:
    name: "{{ myapp_db_name }}"
    owner: "{{ myapp_db_user }}"
    encoding: UTF8
    template: template0
  become: yes
  become_user: postgres

In site.yml

- name: Deploy Infrastructure
  hosts: arch-vps
  become: yes
  
  roles:
    - role: postgresql
      tags: ['postgresql', 'infrastructure', 'database']
    - role: myapp  # Will use PostgreSQL infrastructure
      tags: ['myapp']

Configuration Variables

Basic Configuration

Variable Default Description
postgresql_service_enabled true Enable PostgreSQL service
postgresql_service_state "started" Service state
postgresql_port 5432 PostgreSQL port
postgresql_listen_addresses "localhost" Listen addresses

Security Configuration

Variable Default Description
postgresql_auth_method "scram-sha-256" Authentication method
postgresql_systemd_security true Enable systemd security hardening
postgresql_data_checksums true Enable data checksums
postgresql_ssl false Enable SSL/TLS

Performance Configuration

Variable Default Description
postgresql_max_connections 100 Maximum connections
postgresql_shared_buffers "128MB" Shared buffer size
postgresql_effective_cache_size "1GB" Effective cache size

See defaults/main.yml for all available configuration options.

Security Features

SystemD Hardening

The role implements comprehensive systemd security restrictions:

  • NoNewPrivileges=true
  • PrivateTmp=true
  • ProtectHome=true
  • ProtectSystem=strict
  • Memory execution protection
  • System call filtering

Authentication

  • Secure scram-sha-256 password authentication
  • peer authentication for postgres superuser
  • Local connections only by default
  • Encrypted password storage

Unix Socket Permissions

Current Configuration: Socket permissions are set to 0777 (world-readable/writable)

Rationale:

  • Allows containers running as any UID to access the socket
  • Needed for containers that start as root and switch to unprivileged users (e.g., Nextcloud's www-data)
  • Security is maintained via password authentication (scram-sha-256)
  • Sockets are local-only (not network-exposed)

Security Considerations:

  • Any local process can connect to the socket
  • But still requires valid username + password to authenticate
  • Limited to processes on same host (not network)
  • Passwords stored encrypted with scram-sha-256

Alternative Approach (TCP): If you prefer more restrictive socket permissions, you can use TCP instead:

# In host_vars
postgresql_listen_addresses: "127.0.0.1"  # Listen on localhost TCP
postgresql_unix_socket_permissions: "0770"  # Restrict socket to group

# In application configs
# Use: host=127.0.0.1 port=5432
# Instead of: host=/var/run/postgresql

This provides the same security level (password-authenticated, localhost-only) but uses TCP instead of Unix sockets.

File System Security

  • Proper ownership and permissions
  • Protected configuration files (mode 0600)
  • Separate log directory with appropriate access

Directory Structure

/var/lib/postgres/data/     # PostgreSQL data directory
/var/lib/postgres/          # PostgreSQL home directory  
/var/log/postgresql/        # PostgreSQL logs
/etc/systemd/system/postgresql.service.d/  # SystemD overrides

Database Administration

Connect as postgres superuser:

sudo -u postgres psql

Create application database and user:

CREATE ROLE myapp WITH LOGIN PASSWORD 'secure_password';
CREATE DATABASE myapp WITH OWNER myapp TEMPLATE template0 ENCODING 'UTF8';

List databases:

\l

List users:

\du

Performance Tuning

The role provides conservative performance defaults suitable for most applications. For production workloads, consider adjusting:

  • postgresql_shared_buffers: 25% of system RAM
  • postgresql_effective_cache_size: 75% of system RAM
  • postgresql_max_connections: Based on application needs
  • postgresql_maintenance_work_mem: For large datasets

Monitoring

Service Status

systemctl status postgresql

Logs

journalctl -u postgresql
# or
sudo -u postgres tail -f /var/log/postgresql/postgresql-*.log

Connection Test

sudo -u postgres psql -c "SELECT version();"

Troubleshooting

Common Issues

  1. Permission Denied: Ensure postgres user owns data directory
  2. Connection Refused: Check service status and listen_addresses
  3. Authentication Failed: Verify pg_hba.conf configuration

Debug Mode

Enable detailed logging:

postgresql_log_statement: "all"
postgresql_log_connections: true
postgresql_log_disconnections: true

Examples

Development Setup

postgresql_log_statement: "all"  # Log all statements
postgresql_max_connections: 50   # Lower for development

Production Setup

postgresql_shared_buffers: "256MB"
postgresql_effective_cache_size: "2GB" 
postgresql_log_min_duration_statement: 1000  # Log slow queries
postgresql_log_connections: true  # Audit trail

High Security Setup

postgresql_ssl: true
postgresql_log_connections: true
postgresql_log_disconnections: true
postgresql_log_statement: "ddl"  # Log schema changes

Dependencies

  • Arch Linux PostgreSQL package
  • python-psycopg2 (for Ansible modules)

Compatibility

  • OS: Arch Linux
  • PostgreSQL: Latest stable version from Arch repositories
  • Ansible: >= 2.9

Contributing

When modifying this role:

  1. Update this README for any new features
  2. Test with example applications
  3. Ensure security configurations remain intact
  4. Follow rick-infra coding standards

Integration Examples

See roles that use this infrastructure:

  • gitea: Git repository management
  • nextcloud (planned): File sharing and collaboration

Rick-Infra PostgreSQL Infrastructure Role
Provides secure, performant PostgreSQL database infrastructure for rick-infra applications.