--- # Attach a service's network to Postgres. # # Postgres is the *base* unit and knows nothing about its clients, the same way # Caddy knows nothing about its upstreams. A service that needs a database adds # itself by dropping files next to postgres.container, and Quadlet merges them in. # # Include this from a service playbook: # # - name: Attach this service to Postgres # ansible.builtin.include_tasks: "{{ playbook_dir }}/../postgres/roles/attach/tasks/main.yaml" # vars: # network_name: "{{ network_name }}" # # The including play must: # # 1. have created {{ network_name }}.network already — caddy/roles/register # does that, and every service here that needs a database is also behind # Caddy, so its register include comes first; # 2. run the `podman` role (or include its task file and define the same four # vars), for podman_user and quadlet_dir; # 3. define a "Restart Postgres" handler — see # roles/postgres/handlers/main.yaml for the exact snippet; and # 4. flush handlers before starting the app, on a first attach. This include # notifies that restart, so by default it runs at the end of the play — # after the app has already started and failed to resolve `db`. A # `meta: flush_handlers` immediately after this include is enough; see # tandoor/roles/postgres for the working example. # # It runs as the connection user, which must be the rootless podman_user. # # A service may instead write these two files from its own role; the bytes have # to match this file exactly, or the two will overwrite each other on every run. - name: "Check that the {{ network_name }} network unit exists" ansible.builtin.stat: path: "{{ quadlet_dir }}/{{ network_name }}.network" register: postgres_attach_network # A drop-in naming a .network file that does not exist makes Quadlet fail to # generate the whole postgres unit, with a message that does not mention the # drop-in at all. Fail here instead, where the cause is obvious. - name: "Fail if {{ network_name }} has no network unit" ansible.builtin.assert: that: postgres_attach_network.stat.exists fail_msg: >- {{ quadlet_dir }}/{{ network_name }}.network does not exist, so attaching it to Postgres would break the postgres unit. Create the network first (caddy/roles/register does it), then include this file. - name: Ensure the Postgres Quadlet drop-in directory exists ansible.builtin.file: path: "{{ quadlet_dir }}/{{ postgres_container | default('postgres') }}.container.d" state: directory owner: "{{ podman_user }}" group: "{{ podman_user }}" mode: '0755' - name: "Attach {{ network_name }} to Postgres" ansible.builtin.copy: dest: >- {{ quadlet_dir }}/{{ postgres_container | default('postgres') }}.container.d/50-{{ network_name }}.conf owner: "{{ podman_user }}" group: "{{ podman_user }}" mode: '0644' content: | # Added by the {{ network_name }} playbook. Do not edit by hand. # # Deleting this file removes the network from {{ postgres_container | default('postgres') }} on # the next daemon-reload; nothing else changes. [Container] Network={{ network_name }}.network notify: Restart Postgres # NetworkAlias= applies to every network the container joins, which is exactly # right for a hub: every app in this repo reaches postgres as `db` (nextcloud, # gitea, tandoor and outline use the alias; the rest use the container name). # It is a fixed value in one shared file, not a per-service option, so the same # bytes are written no matter how many services attach — two services writing # their own aliases would make Quadlet pass --network-alias twice. - name: "Give {{ postgres_container | default('postgres') }} the shared 'db' alias" ansible.builtin.copy: dest: >- {{ quadlet_dir }}/{{ postgres_container | default('postgres') }}.container.d/10-network-aliases.conf owner: "{{ podman_user }}" group: "{{ podman_user }}" mode: '0644' content: | # Shared container aliases for {{ postgres_container | default('postgres') }}. # Managed by Ansible (the service playbooks that use it). Do not edit. [Container] NetworkAlias=db notify: Restart Postgres