Replaces the docker_container playbook, which published 5432 on 0.0.0.0 and kept its data in /var/lib/postgresql. Postgres is now the base Quadlet unit, the same shape as Caddy: it knows nothing about its clients, and a service attaches itself by dropping a file into postgres.container.d/ via postgres/roles/attach. Nothing here changes when a service is added or removed. Pinned to 18, which is two decisions rather than one tag. Postgres 18 moved both the image's VOLUME (/var/lib/postgresql/data -> /var/lib/postgresql) and its default PGDATA (/var/lib/postgresql/data/pgdata -> /var/lib/postgresql/18/docker), and the 18 entrypoint refuses to start when it finds a cluster in the old layout. So postgres_major is the single knob: it picks the image tag and the PGDATA path together, and the mount is now the parent directory. That also settles the migration: a PGDATA copy cannot cross a major version, so monoco's 17.5 cluster comes across as a pg_dumpall. Three things that bit us and are worth keeping: - PGDATA and its version directory are created by the role, not left to the container. The entrypoint runs as container-root, which under keep-id is a subuid on the host, and it only chowns PGDATA and its contents -- so ~/podman-data/postgres/18 would have been stranded subuid-owned, and the host user could not even rm -rf it without podman unshare. - NetworkAlias= applies to every network the container joins, which is what a database hub wants, but it is one fixed value in a shared drop-in rather than per-service: two services writing their own would make Quadlet pass --network-alias twice. It is not in postgres.container itself because on a fresh host that unit has no Network= at all. - The credentials live in a 0600 file read with EnvironmentFile=, not in Environment= lines: the unit is world-readable in ~/.config. The play no longer uses become, and does not apply the rootless-Podman prerequisites -- those are the shared task file at caddy/roles/podman, and the three variables they need are duplicated at the top of vars/default.yml. migrate-from-monoco.md records the one-time 17.5 -> 18.6 move: the dump, transfer and restore commands, the checks that prove the restore is complete (per-database table counts against the source), and the verified result. The dead nginx/php-fpm templates in postgres/files/ are removed.
95 lines
4.6 KiB
YAML
95 lines
4.6 KiB
YAML
---
|
|
# ---------------------------------------------------------------------------
|
|
# Rootless Podman / Quadlet
|
|
# ---------------------------------------------------------------------------
|
|
# Duplicated on purpose from caddy/vars/default.yml: a play cannot include that
|
|
# file wholesale, because it also sets `host_directory` — to the Caddy data
|
|
# directory — and every playbook defines that for itself. Keep these three in
|
|
# sync with caddy/vars/default.yml. (Its fourth, podman_unprivileged_port_start,
|
|
# belongs to the podman tasks, which this play does not run: Postgres listens on
|
|
# 5432 and publishes nothing.)
|
|
#
|
|
# podman_user is derived from the connection: this play carries no become, so
|
|
# the Quadlet units belong to whoever Ansible connects as. Requires gather_facts
|
|
# (set in playbook.yml).
|
|
podman_user: "{{ ansible_facts['user_id'] }}"
|
|
podman_data_dir: "{{ ansible_facts['user_dir'] }}/podman-data"
|
|
quadlet_dir: "{{ ansible_facts['user_dir'] }}/.config/containers/systemd"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# PostgreSQL
|
|
# ---------------------------------------------------------------------------
|
|
# The name the rest of the repo already uses for this container in its
|
|
# `postgres_container` vars, and the name every app resolves over the network.
|
|
postgres_container: postgres
|
|
postgres_unit: "{{ postgres_container }}.service"
|
|
|
|
# The major version is the one knob, because the image tag and the PGDATA
|
|
# directory have to agree.
|
|
#
|
|
# 18 is deliberate. monoco runs 17.5 and the data is brought across with a
|
|
# logical dump — a PGDATA copy cannot cross a major version. See
|
|
# migrate-from-monoco.md for the one-time move.
|
|
#
|
|
# `-alpine` still floats within 18.x, which is what you want for security
|
|
# patches; pin a digest here if the image itself must be immutable.
|
|
postgres_major: '18'
|
|
|
|
# Fully qualified on purpose: rootless Podman resolves short names against
|
|
# unqualified-search-registries, and there is no TTY to answer a prompt when
|
|
# the systemd unit starts.
|
|
postgres_image: "docker.io/library/postgres:{{ postgres_major }}-alpine"
|
|
|
|
# The uid the server actually writes as — not the image's configured user. The
|
|
# Alpine image creates the "postgres" account at uid/gid 70 ("70 is the standard
|
|
# uid/gid for postgres in Alpine"), and the entrypoint starts as root, chowns
|
|
# PGDATA to it, then drops privileges. keep-id maps that uid onto podman_user,
|
|
# so PGDATA ends up owned by podman_user on the host and never by a subuid. That
|
|
# is what keeps backups working without podman unshare or sudo.
|
|
postgres_uid: 70
|
|
postgres_gid: 70
|
|
|
|
host_directory: "{{ podman_data_dir }}/{{ postgres_container }}"
|
|
|
|
# Postgres 18 moved both the image's VOLUME and its default PGDATA:
|
|
#
|
|
# 17 Volume /var/lib/postgresql/data PGDATA /var/lib/postgresql/data/pgdata
|
|
# 18+ Volume /var/lib/postgresql PGDATA /var/lib/postgresql/<major>/docker
|
|
#
|
|
# So the mount is now the parent directory and the version lives in the path.
|
|
# That is what the 18 entrypoint expects: it refuses to start — "there appears
|
|
# to be PostgreSQL data in ..." — if it finds a cluster in the old layout, and
|
|
# the versioned subdirectory is what makes a later `pg_upgrade --link` work
|
|
# without mount-boundary trouble.
|
|
postgres_mount: /var/lib/postgresql
|
|
|
|
# Everything under the mount that identifies this cluster, kept as one value so
|
|
# the container path and the host path cannot drift apart.
|
|
postgres_versioned_subdir: "{{ postgres_major }}/docker"
|
|
|
|
pgdata: "{{ postgres_mount }}/{{ postgres_versioned_subdir }}"
|
|
pgdata_host_dir: "{{ host_directory }}/{{ postgres_versioned_subdir }}"
|
|
|
|
# Secrets do not go in the unit file: it is world-readable, and `podman inspect`
|
|
# prints the environment anyway. They go in a 0600 file the unit reads with
|
|
# EnvironmentFile=.
|
|
postgres_env_file: "{{ host_directory }}/postgres.env"
|
|
|
|
# Quadlet drop-in directory for the Postgres unit. Services attach themselves
|
|
# here; see the "How services attach themselves" section of readme.md.
|
|
# Postgres itself never lists its app networks.
|
|
postgres_dropin_dir: "{{ quadlet_dir }}/{{ postgres_container }}.container.d"
|
|
|
|
# Credentials. This is the placeholder the app playbooks already embed in their
|
|
# db_url / POSTGRES_PASSWORD, kept as-is so a migrated app connects unchanged.
|
|
# monoco's cluster uses the same one, so the dump restores and the apps connect
|
|
# with no password reset. Replace it here and in those playbooks together when
|
|
# the fleet moves to a real secret.
|
|
pg_user: postgres
|
|
pg_password: '123456'
|
|
pg_database: postgres
|
|
|
|
# The first start runs initdb, and a database wants time to shut down cleanly.
|
|
postgres_timeout_start: 120
|
|
postgres_timeout_stop: 60
|