feat: pg with extensions and open to podmans containers

This commit is contained in:
Clément Désiles 2025-11-11 00:02:15 +01:00
parent ba37edd498
commit e7dbe470da
No known key found for this signature in database
7 changed files with 158 additions and 98 deletions

View File

@ -20,6 +20,24 @@ This Ansible role installs and configures PostgreSQL for local use only. It prov
Available variables with defaults (see `defaults/main.yml`): Available variables with defaults (see `defaults/main.yml`):
```yaml ```yaml
# PostgreSQL admin user
postgres_admin_user: postgres
# PostgreSQL admin password (REQUIRED - must be set explicitly)
# postgres_admin_password: "" # Intentionally undefined
# PostgreSQL data directory
postgres_data_dir: /var/lib/postgres/data
# Network configuration
postgres_listen_addresses: 127.0.0.1 # For container access: "127.0.0.1,{{ podman_subnet_gateway }}"
postgres_port: 5432
# Firewall configuration
postgres_firewall_allowed_sources:
- 127.0.0.0/8 # Localhost
- "{{ podman_subnet | default('10.88.0.0/16') }}" # Podman bridge network
# Performance tuning # Performance tuning
postgres_shared_buffers: 256MB postgres_shared_buffers: 256MB
postgres_effective_cache_size: 1GB postgres_effective_cache_size: 1GB
@ -75,90 +93,38 @@ Each service role (immich, nextcloud, etc.) manages its own:
- Each user has a unique password - Each user has a unique password
- Passwords stored in service role variables (use Ansible Vault for production) - Passwords stored in service role variables (use Ansible Vault for production)
## How to Use from Service Roles
### Pattern for Service Roles
When creating a service role that needs PostgreSQL:
**1. Add postgres as a dependency** (`meta/main.yml`):
```yaml
dependencies:
- role: postgres
```
**2. Define database variables** (`defaults/main.yml`):
```yaml
myservice_db_name: myservice
myservice_db_user: myservice_user
myservice_db_password: changeme # Use Ansible Vault in production!
myservice_db_host: localhost
myservice_db_port: 5432
```
**3. Create database and user** (`tasks/main.yml`):
```yaml
- name: Create PostgreSQL database for myservice
community.postgresql.postgresql_db:
name: "{{ myservice_db_name }}"
state: present
become: true
become_user: "{{ postgres_admin_user }}"
- name: Create PostgreSQL user for myservice
community.postgresql.postgresql_user:
name: "{{ myservice_db_user }}"
password: "{{ myservice_db_password }}"
db: "{{ myservice_db_name }}"
priv: ALL
state: present
become: true
become_user: "{{ postgres_admin_user }}"
- name: Ensure user has no superuser privileges
community.postgresql.postgresql_user:
name: "{{ myservice_db_user }}"
role_attr_flags: NOSUPERUSER,NOCREATEDB,NOCREATEROLE
state: present
become: true
become_user: "{{ postgres_admin_user }}"
```
**Note:** `postgres_admin_user` is provided by the postgres role and defaults to `postgres`.
**4. Configure your service** to connect to:
```
Host: localhost
Port: 5432
Database: myservice
User: myservice_user
Password: changeme
```
### Real Example: Immich
See `roles/immich/` for a complete working example of using this pattern.
## Connection Methods ## Connection Methods
### From Containers ### From Containers
If your service runs in a container (Docker/Podman), you need to: If your service runs in a container (Docker/Podman), you need to configure PostgreSQL to listen on the Podman bridge gateway:
**Option 1: Use host network mode** **Step 1: Configure PostgreSQL in inventory**
```yaml ```yaml
network_mode: host # inventory/host_vars/yourserver.yml
``` postgres_listen_addresses: "127.0.0.1,{{ podman_subnet_gateway }}"
Then connect to `localhost:5432` postgres_firewall_allowed_sources:
- 127.0.0.0/8
**Option 2: Use host.containers.internal (Podman/Docker)** - "{{ podman_subnet }}"
```yaml
DB_HOSTNAME: host.containers.internal
DB_PORT: 5432
``` ```
**Option 3: Bridge with firewall (less secure)** **Step 2: Use host.containers.internal in containers**
Bind postgres to `0.0.0.0` and use container gateway IP. ```yaml
# docker-compose.yml
services:
myservice:
extra_hosts:
- "host.containers.internal:host-gateway"
environment:
DB_HOSTNAME: host.containers.internal
DB_PORT: 5432
```
**What this does:**
- PostgreSQL listens on `127.0.0.1` (localhost) and `10.88.0.1` (Podman gateway)
- UFW firewall allows connections from localhost and Podman subnet
- `pg_hba.conf` automatically configured to allow Podman subnet
- `host.containers.internal` resolves to the gateway IP inside containers
### From System Services ### From System Services
@ -198,11 +164,14 @@ The pattern above ensures users have:
- ❌ Cannot create roles - ❌ Cannot create roles
- ❌ Cannot access other databases - ❌ Cannot access other databases
### 4. Local-Only Access ### 4. Controlled Access
PostgreSQL is configured to listen on `localhost` only: PostgreSQL default configuration:
- No remote connections allowed - Listens on `localhost` only by default
- Services must run on the same host - To allow container access, set `postgres_listen_addresses` to include Podman gateway
- UFW firewall rules automatically configured for allowed sources
- `pg_hba.conf` automatically configured for Podman subnet when enabled
- No remote network access by default
## Troubleshooting ## Troubleshooting
@ -228,7 +197,20 @@ sudo -u postgres psql
### Test connection from service ### Test connection from service
```bash ```bash
# From localhost
psql -h localhost -U immich -d immich psql -h localhost -U immich -d immich
# From Podman gateway (if configured)
psql -h 10.88.0.1 -U immich -d immich
# Check listen addresses
sudo -u postgres psql -c "SHOW listen_addresses;"
# Check firewall rules
sudo ufw status | grep 5432
# Check pg_hba.conf
sudo grep -v "^#" /var/lib/postgres/data/pg_hba.conf | grep -v "^$"
``` ```
### View logs ### View logs

View File

@ -1,15 +1,25 @@
--- ---
# PostgreSQL admin user (used by service roles for database management) # PostgreSQL port
postgres_port: 5432
# PostgreSQL admin user (only for database management)
postgres_admin_user: postgres postgres_admin_user: postgres
# PostgreSQL admin password (REQUIRED - must be set explicitly) # PostgreSQL admin password (REQUIRED - must be set explicitly)
# Set via inventory, host_vars, or ansible-vault # Set via inventory, host_vars, or ansible-vault
# See this file's comments for setup instructions # postgres_admin_password: ""
# postgres_admin_password: "" # Intentionally undefined - role will fail if not set
# PostgreSQL data directory # PostgreSQL data directory
postgres_data_dir: /var/lib/postgres/data postgres_data_dir: /var/lib/postgres/data
# Binding address(es)
postgres_listen_addresses: "127.0.0.1,{{ podman_subnet_gateway | default('10.88.0.1' }}"
# Firewall configuration
postgres_firewall_allowed_sources:
- 127.0.0.0/8 # Localhost
- "{{ podman_subnet | default('10.88.0.0/16') }}" # Podman bridge network
# Performance tuning (adjust based on your hardware) # Performance tuning (adjust based on your hardware)
postgres_shared_buffers: 256MB postgres_shared_buffers: 256MB
postgres_effective_cache_size: 1GB postgres_effective_cache_size: 1GB

View File

@ -0,0 +1,38 @@
---
- name: Check if pgvector is installed
ansible.builtin.command: pacman -Qi pgvector
register: pgvector_installed
changed_when: false
failed_when: false
- name: Install pgvector from AUR
when: pgvector_installed.rc != 0
block:
- name: Disable SUDOERS password prompt for AUR installation
no_log: true
ansible.builtin.lineinfile:
dest: /etc/sudoers
state: present
regexp: "^#?%wheel"
line: "%wheel ALL=(ALL) NOPASSWD: ALL"
validate: /usr/sbin/visudo -cf %s
- name: Install pgvector from AUR
become: false
ansible.builtin.command:
cmd: "paru -S --noconfirm pgvector"
- name: Restore SUDOERS password prompt after AUR installation
no_log: true
ansible.builtin.lineinfile:
dest: /etc/sudoers
state: present
regexp: "^#?%wheel"
line: "%wheel ALL=(ALL:ALL) ALL"
validate: /usr/sbin/visudo -cf %s
- name: Ensure PostgreSQL is initialized
ansible.builtin.command:
cmd: initdb -D {{ postgres_data_dir }}
creates: "{{ postgres_data_dir }}/PG_VERSION"
become_user: "{{ postgres_admin_user }}"

View File

@ -0,0 +1,16 @@
---
- name: Create current version symlink
ansible.builtin.shell:
cmd: set -o pipefail && ln -sf $(ls -1 /etc/postgresql/ | grep -E '^[0-9]+$' | sort -V | tail -n1) /etc/postgresql/current
creates: /etc/postgresql/current
executable: /bin/bash
- name: Get installed PostgreSQL version
ansible.builtin.shell: psql --version | grep -oP '\d+' | head -1
register: postgres_version
changed_when: false
- name: Install pgvector extension
ansible.builtin.package:
name: "postgresql-{{ postgres_version.stdout }}-pgvector"
state: present

View File

@ -20,20 +20,8 @@
name: "{{ postgres_packages }}" name: "{{ postgres_packages }}"
state: present state: present
- name: Create current version symlink (Debian) - name: Include OS-specific tasks
ansible.builtin.shell: ansible.builtin.include_tasks: "{{ ansible_facts['os_family'] | lower }}.yml"
cmd: set -o pipefail && ln -sf $(ls -1 /etc/postgresql/ | grep -E '^[0-9]+$' | sort -V | tail -n1) /etc/postgresql/current
creates: /etc/postgresql/current
executable: /bin/bash
when: ansible_facts['os_family'] == 'Debian'
- name: Ensure PostgreSQL is initialized (Arch)
ansible.builtin.command:
cmd: initdb -D {{ postgres_data_dir }}
creates: "{{ postgres_data_dir }}/PG_VERSION"
become: true
become_user: "{{ postgres_admin_user }}"
when: ansible_facts['os_family'] == 'Archlinux'
- name: Ensure PostgreSQL config directory exists - name: Ensure PostgreSQL config directory exists
ansible.builtin.file: ansible.builtin.file:
@ -60,6 +48,25 @@
mode: "0640" mode: "0640"
notify: Restart PostgreSQL notify: Restart PostgreSQL
- name: Configure pg_hba.conf for Podman subnet access
ansible.builtin.lineinfile:
path: "{{ postgres_hba_path }}"
line: "host all all {{ podman_subnet }} scram-sha-256"
insertafter: "^# IPv4 local connections:"
state: present
when: podman_subnet is defined
notify: Restart PostgreSQL
- name: Setup firewall rules for PostgreSQL
community.general.ufw:
rule: allow
src: "{{ item }}"
port: "{{ postgres_port }}"
proto: tcp
direction: in
comment: "PostgreSQL"
loop: "{{ postgres_firewall_allowed_sources }}"
- name: Enable and start PostgreSQL service - name: Enable and start PostgreSQL service
ansible.builtin.systemd: ansible.builtin.systemd:
name: "{{ postgres_service_name }}" name: "{{ postgres_service_name }}"
@ -71,5 +78,4 @@
name: "{{ postgres_admin_user }}" name: "{{ postgres_admin_user }}"
password: "{{ postgres_admin_password }}" password: "{{ postgres_admin_password }}"
state: present state: present
become: true
become_user: "{{ postgres_admin_user }}" become_user: "{{ postgres_admin_user }}"

View File

@ -1,6 +1,10 @@
# Custom PostgreSQL configuration managed by Ansible # Custom PostgreSQL configuration managed by Ansible
# Override settings from main postgresql.conf # Override settings from main postgresql.conf
# Network configuration
listen_addresses = '{{ postgres_listen_addresses }}'
port = {{ postgres_port }}
# Performance tuning # Performance tuning
shared_buffers = {{ postgres_shared_buffers }} shared_buffers = {{ postgres_shared_buffers }}
effective_cache_size = {{ postgres_effective_cache_size }} effective_cache_size = {{ postgres_effective_cache_size }}

View File

@ -3,6 +3,10 @@ postgres_packages:
- postgresql - postgresql
- python-psycopg2 - python-psycopg2
# AUR packages (installed via paru)
postgres_aur_packages:
- pgvector
postgres_service_name: postgresql postgres_service_name: postgresql
postgres_config_path: "{{ postgres_data_dir }}/postgresql.conf" postgres_config_path: "{{ postgres_data_dir }}/postgresql.conf"
postgres_config_dir: "{{ postgres_data_dir }}/conf.d" postgres_config_dir: "{{ postgres_data_dir }}/conf.d"