--- # Rootless Podman prerequisites. Shared by every container playbook. # # This playbook runs AS the container user; it does not use become to reach it. # Root-only steps elevate individually. Keeping the connection's PAM session # intact is what lets `systemctl --user` work without setting XDG_RUNTIME_DIR. - name: Fail if the connecting user resolved to root ansible.builtin.assert: that: podman_user != 'root' fail_msg: >- podman_user resolved to root, but this playbook configures *rootless* Podman and writes Quadlet units into the user's home. Connect as the unprivileged user that should own the containers, e.g. `ansible-playbook -u tanshu ...`. If this appeared because become was re-enabled at play level, drop it: the root-only tasks below elevate individually. - name: Install Podman and the rootless runtime pieces ansible.builtin.apt: name: - podman # Everything below is only a Recommends, not a Depends, of podman or # netavark. apt installs Recommends by default, so these would normally # arrive on their own; listing them makes the requirement explicit and # keeps it true under --no-install-recommends. # - aardvark-dns # container-name DNS: Caddy resolves every upstream by name # - nftables # netavark's firewall backend; Podman 6 dropped iptables # - passt # pasta, the rootless network backend # - uidmap # newuidmap/newgidmap, required for rootless user namespaces state: present update_cache: true cache_valid_time: 3600 become: true # Without lingering, the user's systemd manager is torn down when the last # session ends: Quadlet units stop, and they never start at boot. - name: Enable systemd lingering for {{ podman_user }} ansible.builtin.command: "loginctl enable-linger {{ podman_user }}" args: creates: "/var/lib/systemd/linger/{{ podman_user }}" become: true # Rootless containers cannot bind host ports below 1024 unless the kernel # floor is lowered. Caddy publishes 80/443. # # This is applied inline rather than via a handler on purpose: handlers only # run at the end of the play, by which point Caddy has already tried (and # failed) to bind port 80. sysctl -w touches the running kernel; the file # makes it survive a reboot. - name: Read the current unprivileged port floor ansible.builtin.command: sysctl -n net.ipv4.ip_unprivileged_port_start register: podman_port_floor changed_when: false - name: Persist the unprivileged port floor ansible.builtin.copy: dest: /etc/sysctl.d/99-podman.conf owner: root group: root mode: "0644" content: | # Managed by Ansible (podman role). net.ipv4.ip_unprivileged_port_start = {{ podman_unprivileged_port_start }} become: true - name: Apply the unprivileged port floor ansible.builtin.command: >- sysctl -w net.ipv4.ip_unprivileged_port_start={{ podman_unprivileged_port_start }} become: true when: podman_port_floor.stdout | int > podman_unprivileged_port_start changed_when: true - name: Ensure the Quadlet unit directory exists ansible.builtin.file: path: "{{ quadlet_dir }}" state: directory owner: "{{ podman_user }}" group: "{{ podman_user }}" mode: "0755"