--- # Attach a service's network to Valkey. # # Valkey is the *base* unit and knows nothing about its clients, the same way # Caddy knows nothing about its upstreams. A service that needs a cache adds # itself by dropping files next to valkey.container, and Quadlet merges them in. # # Include this from a service playbook: # # - name: Attach this service to Valkey # ansible.builtin.include_tasks: "{{ playbook_dir }}/../valkey/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 cache 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 Valkey" handler — see # roles/valkey/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 reach `redis`. A # `meta: flush_handlers` immediately after this include is enough; see # nextcloud/roles/valkey 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: valkey_attach_network # A drop-in naming a .network file that does not exist makes Quadlet fail to # generate the whole valkey 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: valkey_attach_network.stat.exists fail_msg: >- {{ quadlet_dir }}/{{ network_name }}.network does not exist, so attaching it to Valkey would break the valkey unit. Create the network first (caddy/roles/register does it), then include this file. - name: Ensure the Valkey Quadlet drop-in directory exists ansible.builtin.file: path: "{{ quadlet_dir }}/{{ valkey_container | default('valkey') }}.container.d" state: directory owner: "{{ podman_user }}" group: "{{ podman_user }}" mode: '0755' - name: "Attach {{ network_name }} to Valkey" ansible.builtin.copy: dest: >- {{ quadlet_dir }}/{{ valkey_container | default('valkey') }}.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 {{ valkey_container | default('valkey') }} on # the next daemon-reload; nothing else changes. [Container] Network={{ network_name }}.network notify: Restart Valkey # NetworkAlias= applies to every network the container joins, which is exactly # right for a hub: this repo's cache clients reach Valkey as `redis` (nextcloud, # which writes REDIS_HOST=redis into its config, plus outline and overleaf), # while 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 {{ valkey_container | default('valkey') }} the shared 'redis' alias" ansible.builtin.copy: dest: >- {{ quadlet_dir }}/{{ valkey_container | default('valkey') }}.container.d/10-network-aliases.conf owner: "{{ podman_user }}" group: "{{ podman_user }}" mode: '0644' content: | # Shared container aliases for {{ valkey_container | default('valkey') }}. # Managed by Ansible (the service playbooks that use it). Do not edit. [Container] NetworkAlias=redis notify: Restart Valkey