From ad681951fd2925184b2cbdcdb741ccde61f1e08f Mon Sep 17 00:00:00 2001 From: Daniel Akulenok Date: Fri, 5 Sep 2025 23:40:44 +0200 Subject: [PATCH 01/17] unless-stopped --- README.md | 2 +- defaults/main.yml | 4 ++-- tasks/systemd.yml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 95b5610..af81c03 100644 --- a/README.md +++ b/README.md @@ -235,7 +235,7 @@ The role can automatically generate systemd service files for containers and pod podman_systemd_options: new: true # Generate new service files force: true # Overwrite existing files - restart_policy: unless-stopped # Default restart policy + restart_policy: always # Default restart policy time: 120 # Stop timeout in seconds no_header: false # Include header in service files wants: [] # Systemd unit Wants diff --git a/defaults/main.yml b/defaults/main.yml index e4787b5..5156d14 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -619,8 +619,8 @@ podman_systemd_dir: /etc/systemd/system podman_systemd_options: new: true force: true - restart_policy: unless-stopped - time: 120 + restart_policy: always + stop_timeout: 120 no_header: false separator: "" wants: [] diff --git a/tasks/systemd.yml b/tasks/systemd.yml index 4d7b3f1..fe895a6 100644 --- a/tasks/systemd.yml +++ b/tasks/systemd.yml @@ -8,7 +8,7 @@ new: "{{ podman_systemd_options.new }}" force: "{{ podman_systemd_options.force }}" restart_policy: "{{ item.systemd.restart_policy | default(podman_systemd_options.restart_policy) }}" - time: "{{ item.systemd.time | default(podman_systemd_options.time) }}" + stop_timeout: "{{ item.systemd.stop_timeout | default(podman_systemd_options.stop_timeout) }}" no_header: "{{ item.systemd.no_header | default(podman_systemd_options.no_header) }}" separator: "{{ item.systemd.separator | default(podman_systemd_options.separator) }}" wants: "{{ item.systemd.wants | default(podman_systemd_options.wants) }}" @@ -32,7 +32,7 @@ new: "{{ podman_systemd_options.new }}" force: "{{ podman_systemd_options.force }}" restart_policy: "{{ item.systemd.restart_policy | default(podman_systemd_options.restart_policy) }}" - time: "{{ item.systemd.time | default(podman_systemd_options.time) }}" + stop_timeout: "{{ item.systemd.stop_timeout | default(podman_systemd_options.stop_timeout) }}" no_header: "{{ item.systemd.no_header | default(podman_systemd_options.no_header) }}" separator: "{{ item.systemd.separator | default(podman_systemd_options.separator) }}" wants: "{{ item.systemd.wants | default(podman_systemd_options.wants) }}" From 50fdc3689bbe5d3262cc5a996a2de10817dfa7fb Mon Sep 17 00:00:00 2001 From: Daniel Akulenok Date: Tue, 9 Sep 2025 12:35:00 +0200 Subject: [PATCH 02/17] Update podman role --- .ansible/.lock | 0 README.md | 12 ++++----- defaults/main.yml | 10 ++++--- handlers/main.yml | 28 ++++++++++++++++++++ molecule/default/converge.yml | 6 +---- tasks/containers.yml | 50 +++++++++++++++++------------------ tasks/pods.yml | 8 +++--- tasks/systemd.yml | 46 ++++++++++++++++++-------------- vars/Debian.yml | 14 ---------- 9 files changed, 96 insertions(+), 78 deletions(-) create mode 100644 .ansible/.lock delete mode 100644 vars/Debian.yml diff --git a/.ansible/.lock b/.ansible/.lock new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md index af81c03..37585c0 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,9 @@ Requirements Role Variables -------------- -### Installation Variables +### Package Installation -* `podman_install_from_repo`: Install from official repositories (default: `true`) -* `podman_packages`: List of core Podman packages to install -* `podman_additional_packages`: Additional packages for full container support +* `podman_packages`: List of Podman packages to install (includes core and additional packages for Debian/Ubuntu) ### Configuration Variables @@ -236,13 +234,13 @@ podman_systemd_options: new: true # Generate new service files force: true # Overwrite existing files restart_policy: always # Default restart policy - time: 120 # Stop timeout in seconds + stop_timeout: 120 # Stop timeout in seconds no_header: false # Include header in service files wants: [] # Systemd unit Wants after: [] # Systemd unit After requires: [] # Systemd unit Requires - container_prefix: "" # Prefix for container service names - pod_prefix: "" # Prefix for pod service names + container_prefix: "container-" # Prefix for container service names + pod_prefix: "pod-" # Prefix for pod service names restart_sec: 30 # Restart delay in seconds ``` diff --git a/defaults/main.yml b/defaults/main.yml index 5156d14..8c5af08 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -2,10 +2,14 @@ # defaults file for podman # Package installation -podman_install_from_repo: true podman_packages: - podman - crun + - buildah + - skopeo + - fuse-overlayfs + - slirp4netns + - uidmap # Podman configuration podman_configure_registries: true @@ -626,8 +630,8 @@ podman_systemd_options: wants: [] after: [] requires: [] - container_prefix: "" - pod_prefix: "" + container_prefix: "container-" + pod_prefix: "pod-" restart_sec: 30 ### Prune Configuration diff --git a/handlers/main.yml b/handlers/main.yml index e44081e..e079a66 100644 --- a/handlers/main.yml +++ b/handlers/main.yml @@ -10,3 +10,31 @@ - name: Reload systemd ansible.builtin.systemd: daemon_reload: true + +- name: Start Podman pods + ansible.builtin.systemd: + name: "{{ podman_systemd_options.pod_prefix }}{{ item.name }}" + enabled: true + state: started + daemon_reload: true + listen: Reload systemd + loop: "{{ podman_pods }}" + loop_control: + label: "{{ item.name }}" + when: + - podman_pods is defined + - podman_pods | length > 0 + +- name: Start Podman podless containers + ansible.builtin.systemd: + name: "{{ podman_systemd_options.container_prefix }}{{ item.name }}" + enabled: true + state: started + daemon_reload: true + listen: Reload systemd + loop: "{{ podman_containers | rejectattr('pod', 'defined') | list }}" + loop_control: + label: "{{ item.name }}" + when: + - podman_containers is defined + - podman_containers | length > 0 diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml index 998a1b0..cd892c6 100644 --- a/molecule/default/converge.yml +++ b/molecule/default/converge.yml @@ -1,9 +1,7 @@ --- -- name: Converge - apply podman role +- name: Converge hosts: all - become: true vars: - podman_install_from_repo: true podman_packages: - podman podman_configure_registries: true @@ -13,5 +11,3 @@ podman_enable_api_service: false podman_policy_default_type: "insecureAcceptAnything" podman_policy_trusted_registries: [] - roles: - - name: podman diff --git a/tasks/containers.yml b/tasks/containers.yml index a589b0b..16e1524 100644 --- a/tasks/containers.yml +++ b/tasks/containers.yml @@ -15,21 +15,21 @@ hostname: "{{ item.hostname | default(omit) }}" # Container runtime options - detach: "{{ item.detach | default(true) }}" - interactive: "{{ item.interactive | default(false) }}" - tty: "{{ item.tty | default(false) }}" - rm: "{{ item.auto_remove | default(false) }}" - rmi: "{{ item.remove_image | default(false) }}" - privileged: "{{ item.privileged | default(false) }}" - read_only: "{{ item.read_only | default(false) }}" - read_only_tmpfs: "{{ item.read_only_tmpfs | default(true) }}" - init: "{{ item.init | default(false) }}" + detach: "{{ item.detach | bool | default(omit) }}" + interactive: "{{ item.interactive | bool | default(omit) }}" + tty: "{{ item.tty | bool | default(omit) }}" + rm: "{{ item.auto_remove | bool | default(omit) }}" + rmi: "{{ item.remove_image | bool | default(omit) }}" + privileged: "{{ item.privileged | bool | default(omit) }}" + read_only: "{{ item.read_only | bool | default(omit) }}" + read_only_tmpfs: "{{ item.read_only_tmpfs | bool | default(omit) }}" + init: "{{ item.init | bool | default(omit) }}" init_path: "{{ item.init_path | default(omit) }}" # Networking network: "{{ item.networks | default(omit) }}" publish: "{{ item.ports | default(omit) }}" - publish_all: "{{ item.publish_all | default(false) }}" + publish_all: "{{ item.publish_all | bool | default(omit) }}" expose: "{{ item.expose | default(omit) }}" ip: "{{ item.ip | default(omit) }}" ip6: "{{ item.ip6 | default(omit) }}" @@ -39,7 +39,7 @@ dns_option: "{{ item.dns_options | default(omit) }}" dns_search: "{{ item.dns_search | default(omit) }}" etc_hosts: "{{ item.add_hosts | default(omit) }}" - no_hosts: "{{ item.no_hosts | default(false) }}" + no_hosts: "{{ item.no_hosts | bool | default(omit) }}" # Storage and volumes volume: "{{ item.volumes | default(omit) }}" @@ -51,10 +51,10 @@ # Environment variables env: "{{ item.env | default(omit) }}" env_file: "{{ item.env_files | default(omit) }}" - env_host: "{{ item.env_host | default(false) }}" + env_host: "{{ item.env_host | bool | default(omit) }}" env_merge: "{{ item.env_merge | default(omit) }}" unsetenv: "{{ item.unsetenv | default(omit) }}" - unsetenv_all: "{{ item.unsetenv_all | default(false) }}" + unsetenv_all: "{{ item.unsetenv_all | bool | default(omit) }}" # Resource constraints memory: "{{ item.memory | default(omit) }}" @@ -72,7 +72,7 @@ cpuset_mems: "{{ item.cpuset_mems | default(omit) }}" blkio_weight: "{{ item.blkio_weight | default(omit) }}" blkio_weight_device: "{{ item.blkio_weight_device | default(omit) }}" - oom_kill_disable: "{{ item.oom_kill_disable | default(false) }}" + oom_kill_disable: "{{ item.oom_kill_disable | bool | default(omit) }}" oom_score_adj: "{{ item.oom_score_adj | default(omit) }}" pids_limit: "{{ item.pids_limit | default(omit) }}" @@ -137,7 +137,7 @@ healthcheck_start_period: "{{ item.healthcheck_start_period | default(omit) }}" healthcheck_retries: "{{ item.healthcheck_retries | default(omit) }}" healthcheck_failure_action: "{{ item.healthcheck_failure_action | default(omit) }}" - no_healthcheck: "{{ item.no_healthcheck | default(false) }}" + no_healthcheck: "{{ item.no_healthcheck | bool | default(omit) }}" # Startup health checks health_startup_cmd: "{{ item.health_startup_cmd | default(omit) }}" @@ -161,7 +161,7 @@ # Pull and image options pull: "{{ item.pull | default('missing') }}" - image_strict: "{{ item.image_strict | default(false) }}" + image_strict: "{{ item.image_strict | bool | default(omit) }}" arch: "{{ item.arch | default(omit) }}" os: "{{ item.os | default(omit) }}" platform: "{{ item.platform | default(omit) }}" @@ -180,8 +180,8 @@ # Special options attach: "{{ item.attach | default(omit) }}" detach_keys: "{{ item.detach_keys | default(omit) }}" - sig_proxy: "{{ item.sig_proxy | default(true) }}" - http_proxy: "{{ item.http_proxy | default(true) }}" + sig_proxy: "{{ item.sig_proxy | bool | default(omit) }}" + http_proxy: "{{ item.http_proxy | bool | default(omit) }}" # Advanced options chrootdirs: "{{ item.chrootdirs | default(omit) }}" @@ -193,7 +193,7 @@ preserve_fds: "{{ item.preserve_fds | default(omit) }}" rdt_class: "{{ item.rdt_class | default(omit) }}" requires: "{{ item.requires | default(omit) }}" - rootfs: "{{ item.rootfs | default(false) }}" + rootfs: "{{ item.rootfs | bool | default(omit) }}" sdnotify: "{{ item.sdnotify | default(omit) }}" secrets: "{{ item.secrets | default(omit) }}" timezone: "{{ item.timezone | default(omit) }}" @@ -214,13 +214,13 @@ # Control options cmd_args: "{{ item.cmd_args | default(omit) }}" executable: "{{ item.executable | default('podman') }}" - recreate: "{{ item.recreate | default(false) }}" - force_restart: "{{ item.force_restart | default(false) }}" - force_delete: "{{ item.force_delete | default(true) }}" - delete_depend: "{{ item.delete_depend | default(false) }}" + recreate: "{{ item.recreate | bool | default(omit) }}" + force_restart: "{{ item.force_restart | bool | default(omit) }}" + force_delete: "{{ item.force_delete | bool | default(omit) }}" + delete_depend: "{{ item.delete_depend | bool | default(omit) }}" delete_time: "{{ item.delete_time | default(omit) }}" - delete_volumes: "{{ item.delete_volumes | default(false) }}" - debug: "{{ item.debug | default(false) }}" + delete_volumes: "{{ item.delete_volumes | bool | default(omit) }}" + debug: "{{ item.debug | bool | default(omit) }}" loop: "{{ podman_containers }}" loop_control: label: "{{ item.name }}" diff --git a/tasks/pods.yml b/tasks/pods.yml index 790026e..107f09d 100644 --- a/tasks/pods.yml +++ b/tasks/pods.yml @@ -10,7 +10,7 @@ volume: "{{ item.volumes | default(omit) }}" label: "{{ item.labels | default(omit) }}" hostname: "{{ item.hostname | default(omit) }}" - infra: "{{ item.infra | default(true) }}" + infra: "{{ item.infra | default(omit) }}" infra_image: "{{ item.infra_image | default(omit) }}" infra_command: "{{ item.infra_command | default(omit) }}" infra_name: "{{ item.infra_name | default(omit) }}" @@ -21,7 +21,7 @@ ip: "{{ item.ip | default(omit) }}" ip6: "{{ item.ip6 | default(omit) }}" mac_address: "{{ item.mac_address | default(omit) }}" - no_hosts: "{{ item.no_hosts | default(false) }}" + no_hosts: "{{ item.no_hosts | default(omit) }}" share: "{{ item.share | default(omit) }}" share_parent: "{{ item.share_parent | default(omit) }}" userns: "{{ item.userns | default(omit) }}" @@ -59,8 +59,8 @@ quadlet_filename: "{{ item.quadlet_filename | default(omit) }}" quadlet_file_mode: "{{ item.quadlet_file_mode | default(omit) }}" quadlet_options: "{{ item.quadlet_options | default(omit) }}" - recreate: "{{ item.recreate | default(false) }}" - debug: "{{ item.debug | default(false) }}" + recreate: "{{ item.recreate | default(omit) }}" + debug: "{{ item.debug | default(omit) }}" loop: "{{ podman_pods }}" loop_control: label: "{{ item.name }}" diff --git a/tasks/systemd.yml b/tasks/systemd.yml index fe895a6..dd08695 100644 --- a/tasks/systemd.yml +++ b/tasks/systemd.yml @@ -2,49 +2,55 @@ # Generate systemd service files for Podman containers and pods - name: Generate systemd service files for containers + vars: + systemd_opts: "{{ item.systemd if item.systemd is defined else (item.generate_systemd if item.generate_systemd is defined else {}) }}" containers.podman.podman_generate_systemd: name: "{{ item.name }}" dest: "{{ podman_systemd_dir }}" new: "{{ podman_systemd_options.new }}" force: "{{ podman_systemd_options.force }}" - restart_policy: "{{ item.systemd.restart_policy | default(podman_systemd_options.restart_policy) }}" - stop_timeout: "{{ item.systemd.stop_timeout | default(podman_systemd_options.stop_timeout) }}" - no_header: "{{ item.systemd.no_header | default(podman_systemd_options.no_header) }}" - separator: "{{ item.systemd.separator | default(podman_systemd_options.separator) }}" - wants: "{{ item.systemd.wants | default(podman_systemd_options.wants) }}" - after: "{{ item.systemd.after | default(podman_systemd_options.after) }}" - requires: "{{ item.systemd.requires | default(podman_systemd_options.requires) }}" - container_prefix: "{{ item.systemd.container_prefix | default(podman_systemd_options.container_prefix) }}" - pod_prefix: "{{ item.systemd.pod_prefix | default(podman_systemd_options.pod_prefix) }}" - loop: "{{ podman_containers | selectattr('systemd', 'defined') | list }}" + restart_policy: "{{ systemd_opts.restart_policy | default(podman_systemd_options.restart_policy) }}" + stop_timeout: "{{ systemd_opts.stop_timeout | default(podman_systemd_options.stop_timeout) }}" + no_header: "{{ systemd_opts.no_header | default(podman_systemd_options.no_header) }}" + separator: "{{ systemd_opts.separator | default(podman_systemd_options.separator) }}" + wants: "{{ systemd_opts.wants | default(podman_systemd_options.wants) }}" + after: "{{ systemd_opts.after | default(podman_systemd_options.after) }}" + requires: "{{ systemd_opts.requires | default(podman_systemd_options.requires) }}" + container_prefix: "{{ systemd_opts.container_prefix | default(podman_systemd_options.container_prefix) }}" + pod_prefix: "{{ systemd_opts.pod_prefix | default(podman_systemd_options.pod_prefix) }}" + loop: "{{ podman_containers }}" loop_control: label: "{{ item.name }}" when: - podman_generate_systemd | bool - podman_containers is defined - podman_containers | length > 0 + - (item.systemd is defined) or (item.generate_systemd is defined) notify: Reload systemd - name: Generate systemd service files for pods + vars: + systemd_opts: "{{ item.systemd if item.systemd is defined else (item.generate_systemd if item.generate_systemd is defined else {}) }}" containers.podman.podman_generate_systemd: name: "{{ item.name }}" dest: "{{ podman_systemd_dir }}" new: "{{ podman_systemd_options.new }}" force: "{{ podman_systemd_options.force }}" - restart_policy: "{{ item.systemd.restart_policy | default(podman_systemd_options.restart_policy) }}" - stop_timeout: "{{ item.systemd.stop_timeout | default(podman_systemd_options.stop_timeout) }}" - no_header: "{{ item.systemd.no_header | default(podman_systemd_options.no_header) }}" - separator: "{{ item.systemd.separator | default(podman_systemd_options.separator) }}" - wants: "{{ item.systemd.wants | default(podman_systemd_options.wants) }}" - after: "{{ item.systemd.after | default(podman_systemd_options.after) }}" - requires: "{{ item.systemd.requires | default(podman_systemd_options.requires) }}" - container_prefix: "{{ item.systemd.container_prefix | default(podman_systemd_options.container_prefix) }}" - pod_prefix: "{{ item.systemd.pod_prefix | default(podman_systemd_options.pod_prefix) }}" - loop: "{{ podman_pods | selectattr('systemd', 'defined') | list }}" + restart_policy: "{{ systemd_opts.restart_policy | default(podman_systemd_options.restart_policy) }}" + stop_timeout: "{{ systemd_opts.stop_timeout | default(podman_systemd_options.stop_timeout) }}" + no_header: "{{ systemd_opts.no_header | default(podman_systemd_options.no_header) }}" + separator: "{{ systemd_opts.separator | default(podman_systemd_options.separator) }}" + wants: "{{ systemd_opts.wants | default(podman_systemd_options.wants) }}" + after: "{{ systemd_opts.after | default(podman_systemd_options.after) }}" + requires: "{{ systemd_opts.requires | default(podman_systemd_options.requires) }}" + container_prefix: "{{ systemd_opts.container_prefix | default(podman_systemd_options.container_prefix) }}" + pod_prefix: "{{ systemd_opts.pod_prefix | default(podman_systemd_options.pod_prefix) }}" + loop: "{{ podman_pods }}" loop_control: label: "{{ item.name }}" when: - podman_generate_systemd | bool - podman_pods is defined - podman_pods | length > 0 + - (item.systemd is defined) or (item.generate_systemd is defined) notify: Reload systemd diff --git a/vars/Debian.yml b/vars/Debian.yml deleted file mode 100644 index 2f6e0fa..0000000 --- a/vars/Debian.yml +++ /dev/null @@ -1,14 +0,0 @@ ---- -# OS-specific variables for Debian/Ubuntu - -# Package names may vary between distributions -podman_packages: - - podman - - buildah - - skopeo - -podman_additional_packages: - - crun - - fuse-overlayfs - - slirp4netns - - uidmap From be29c261cc1318000edc7b88ff1bac0fba35cceb Mon Sep 17 00:00:00 2001 From: Daniel Akulenok Date: Tue, 9 Sep 2025 13:48:46 +0200 Subject: [PATCH 03/17] Update author --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 37585c0..f461331 100644 --- a/README.md +++ b/README.md @@ -350,5 +350,4 @@ MIT Author Information ------------------ -Daniel Akulenok -Keepit A/S +Daniel Akulenok From c8203cff052d34890af049bf1da9dc1b0ef3787b Mon Sep 17 00:00:00 2001 From: Daniel Akulenok Date: Wed, 10 Sep 2025 10:45:13 +0200 Subject: [PATCH 04/17] Remove Debian conditional --- tasks/install.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/tasks/install.yml b/tasks/install.yml index bdb40de..6fe469c 100644 --- a/tasks/install.yml +++ b/tasks/install.yml @@ -4,7 +4,6 @@ - name: Update package cache (Debian/Ubuntu) ansible.builtin.apt: cache_valid_time: 3600 - when: ansible_os_family == "Debian" - name: Install Podman and related packages ansible.builtin.package: From 36c88c507b90bd5cdc61c8a2f628ed96d02bdacf Mon Sep 17 00:00:00 2001 From: Daniel Akulenok Date: Wed, 10 Sep 2025 10:48:07 +0200 Subject: [PATCH 05/17] Do not set bool vars unless explicitly defined --- tasks/networks.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tasks/networks.yml b/tasks/networks.yml index 1fae6ff..ec42a5c 100644 --- a/tasks/networks.yml +++ b/tasks/networks.yml @@ -9,17 +9,17 @@ subnet: "{{ item.subnet | default(omit) }}" gateway: "{{ item.gateway | default(omit) }}" ip_range: "{{ item.ip_range | default(omit) }}" - disable_dns: "{{ item.disable_dns | default(false) }}" - internal: "{{ item.internal | default(false) }}" + disable_dns: "{{ item.disable_dns | bool | default(omit) }}" + internal: "{{ item.internal | bool | default(omit) }}" opt: "{{ item.options | default(omit) }}" dns: "{{ item.dns | default(omit) }}" interface_name: "{{ item.interface_name | default(omit) }}" ipam_driver: "{{ item.ipam_driver | default(omit) }}" - ipv6: "{{ item.ipv6 | default(false) }}" + ipv6: "{{ item.ipv6 | bool | default(omit) }}" macvlan: "{{ item.macvlan | default(omit) }}" net_config: "{{ item.net_config | default(omit) }}" route: "{{ item.route | default(omit) }}" - recreate: "{{ item.recreate | default(false) }}" + recreate: "{{ item.recreate | bool | default(omit) }}" loop: "{{ podman_networks }}" loop_control: label: "{{ item.name }}" From 4c058e07722338ed369365a9edcdd173005c382d Mon Sep 17 00:00:00 2001 From: Daniel Akulenok Date: Wed, 10 Sep 2025 10:49:17 +0200 Subject: [PATCH 06/17] Do not set bool vars unless explicitly defined --- tasks/volumes.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasks/volumes.yml b/tasks/volumes.yml index 7ef76fd..656343c 100644 --- a/tasks/volumes.yml +++ b/tasks/volumes.yml @@ -8,8 +8,8 @@ driver: "{{ item.driver | default('local') }}" options: "{{ item.options | default(omit) }}" label: "{{ item.labels | default(omit) }}" - debug: "{{ item.debug | default(false) }}" - recreate: "{{ item.recreate | default(false) }}" + debug: "{{ item.debug | bool | default(omit) }}" + recreate: "{{ item.recreate | bool | default(omit) }}" quadlet_dir: "{{ item.quadlet_dir | default(omit) }}" quadlet_filename: "{{ item.quadlet_filename | default(omit) }}" quadlet_file_mode: "{{ item.quadlet_file_mode | default(omit) }}" From 76725214d1b5d24bcc5bd8984e072ff557732801 Mon Sep 17 00:00:00 2001 From: Daniel Akulenok Date: Wed, 10 Sep 2025 14:20:24 +0200 Subject: [PATCH 07/17] add missing link --- tasks/configure.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tasks/configure.yml b/tasks/configure.yml index 6cb154f..42fb3a9 100644 --- a/tasks/configure.yml +++ b/tasks/configure.yml @@ -1,6 +1,11 @@ --- # Configure Podman +- name: Install python3-cryptography + ansible.builtin.package: + name: python3-cryptography + state: present + - name: Ensure containers configuration directory exists ansible.builtin.file: path: /etc/containers From 0938e4b6016ce67347b436f004fb1706980743f6 Mon Sep 17 00:00:00 2001 From: Daniel Akulenok Date: Wed, 10 Sep 2025 15:10:55 +0200 Subject: [PATCH 08/17] podman --- tasks/containers.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasks/containers.yml b/tasks/containers.yml index 16e1524..09d12f7 100644 --- a/tasks/containers.yml +++ b/tasks/containers.yml @@ -152,7 +152,7 @@ annotation: "{{ item.annotations | default(omit) }}" # Container lifecycle - restart_policy: "{{ item.restart_policy | default('no') }}" + restart_policy: "{{ item.restart_policy | default(omit) }}" restart_time: "{{ item.restart_time | default(omit) }}" stop_signal: "{{ item.stop_signal | default(omit) }}" stop_time: "{{ item.stop_time | default(omit) }}" @@ -160,7 +160,7 @@ timeout: "{{ item.timeout | default(omit) }}" # Pull and image options - pull: "{{ item.pull | default('missing') }}" + pull: "{{ item.pull | default(omit) }}" image_strict: "{{ item.image_strict | bool | default(omit) }}" arch: "{{ item.arch | default(omit) }}" os: "{{ item.os | default(omit) }}" From f4c3c3578a99baf31c07819a762582b604d2eb88 Mon Sep 17 00:00:00 2001 From: Daniel Akulenok Date: Thu, 11 Sep 2025 15:40:38 +0200 Subject: [PATCH 09/17] do not --- tasks/containers.yml | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/tasks/containers.yml b/tasks/containers.yml index 09d12f7..60c90d5 100644 --- a/tasks/containers.yml +++ b/tasks/containers.yml @@ -224,18 +224,4 @@ loop: "{{ podman_containers }}" loop_control: label: "{{ item.name }}" - register: podman_container_results - -- name: Enable and start container systemd services - ansible.builtin.systemd: - name: "container-{{ item.item.name }}" - enabled: true - state: started - daemon_reload: true - loop: "{{ podman_container_results.results }}" - loop_control: - label: "{{ item.item.name }}" - when: - - item.item.generate_systemd is defined - - item.item.generate_systemd - - item.item.state | default('present') in ['present', 'started'] + register: podman_container_results \ No newline at end of file From c462dac0d86e8a4720104ff0f409afcacb6fbd48 Mon Sep 17 00:00:00 2001 From: Daniel Akulenok Date: Fri, 12 Sep 2025 11:12:57 +0200 Subject: [PATCH 10/17] Readme --- README.md | 529 +++++++++++++++++++++++++++++------------------------- 1 file changed, 285 insertions(+), 244 deletions(-) diff --git a/README.md b/README.md index f461331..e140dfa 100644 --- a/README.md +++ b/README.md @@ -1,182 +1,163 @@ -Podman -====== +# Podman Role -This Ansible role installs and configures Podman container runtime, and provides comprehensive container, pod, network, and volume management capabilities. +**Bootstrap containerized applications with Podman in minutes.** -Requirements ------------- - -- Ansible 2.11 or higher -- Target systems: Ubuntu 20.04+, Debian 11+ -- containers.podman collection (for container management tasks) - -Role Variables --------------- - -### Package Installation - -* `podman_packages`: List of Podman packages to install (includes core and additional packages for Debian/Ubuntu) - -### Configuration Variables - -* `podman_configure_registries`: Configure container registries (default: `true`) -* `podman_registries_conf_path`: Path to registries configuration (default: `/etc/containers/registries.conf`) -* `podman_registries_additional`: Additional registry configurations for special cases - -**Note**: Registry configuration is now unified with the image signature policy through `podman_policy_trusted_registries`. Each registry in the policy configuration includes both security settings (signature verification) and registry behavior (insecure, blocked, unqualified search). - -* `podman_configure_storage`: Configure storage settings (default: `true`) -* `podman_storage_conf_path`: Path to storage configuration (default: `/etc/containers/storage.conf`) -* `podman_storage_driver`: Storage driver to use (default: `overlay`) -* `podman_storage_runroot`: Runtime storage path (default: `/run/containers/storage`) -* `podman_storage_graphroot`: Persistent storage path (default: `/var/lib/containers/storage`) - -* `podman_configure_policy`: Configure container policy (default: `true`) -* `podman_policy_path`: Path to policy configuration (default: `/etc/containers/policy.json`) - -### Image Signature Policy Variables - -* `podman_policy_default_type`: Default policy for unlisted registries (`"insecureAcceptAnything"` or `"reject"`) -* `podman_policy_reject_unknown_registries`: Reject images from unlisted registries (default: `false`) -* `podman_policy_trusted_registries`: Unified registry configuration for both policy and registries.conf - -Each registry in `podman_policy_trusted_registries` supports: - -**Security Policy Options:** -- `type`: Verification type (`"insecureAcceptAnything"`, `"signedBy"`, `"reject"`) -- `keyPath`: Path to GPG key file (for `signedBy` type) -- `keyData`: Inline GPG key data (alternative to `keyPath`) - -**Registry Configuration Options:** -- `insecure`: Allow insecure (HTTP) connections (default: `false`) -- `blocked`: Block access to this registry (default: `false`) -- `unqualified_search`: Include in unqualified image searches (default: `true`) -- `mirror`: List of mirror registries for redundancy/performance +## 🚀 Quick Start +### 1. Basic Setup ```yaml -# Unified registry and policy configuration -podman_policy_trusted_registries: - - registry: "docker.io" - # Policy settings - type: "insecureAcceptAnything" - # Registry settings - insecure: false - blocked: false - unqualified_search: true - - - registry: "internal-registry.company.com" - # Policy settings - type: "signedBy" - keyPath: "/etc/pki/containers/company.gpg" - # Registry settings - insecure: false - blocked: false - unqualified_search: true - # Mirror configuration - mirror: - - location: "backup-registry.company.com" - insecure: false - -# Development configuration (default) -podman_policy_default_type: "insecureAcceptAnything" -podman_policy_reject_unknown_registries: false - -# Production configuration with signature verification -podman_policy_default_type: "reject" -podman_policy_reject_unknown_registries: true +- hosts: servers + roles: + - podman ``` -### Service Variables +### 2. Run Your First Container +```yaml +- hosts: servers + roles: + - role: podman + vars: + podman_containers: + - name: nginx + image: nginx:latest + state: started + ports: + - "80:80" +``` -* `podman_enable_socket`: Enable Podman socket service (default: `false`) -* `podman_enable_api_service`: Enable Podman API service (default: `false`) +### 3. Common Patterns -### Container Management Variables +**Web application with database:** +```yaml +podman_containers: + - name: webapp + image: myapp:latest + state: started + ports: + - "8080:8080" + env: + DATABASE_URL: "postgresql://postgres@db:5432/app" + + - name: database + image: postgres:15 + state: started + volumes: + - "db-data:/var/lib/postgresql/data" + env: + POSTGRES_DB: app + POSTGRES_PASSWORD: secret + +podman_volumes: + - name: db-data +``` + +**That's it!** Podman will be installed, configured, and your containers will be running with systemd services automatically created. + +--- + +## 📋 Requirements + +- **Ansible**: 2.11+ +- **Target OS**: Ubuntu 20.04+, Debian 11+ +- **Collection**: `containers.podman` (auto-installed) + +--- + +## 🔧 Complete Feature Reference + +### Container Management ```yaml podman_containers: - - name: nginx - image: docker.io/nginx:latest - state: started + - name: my-app + image: nginx:latest + state: started # started|stopped|present|absent ports: - "8080:80" + - "443:443" volumes: - - "/etc/nginx/conf.d:/etc/nginx/conf.d:ro" + - "/host/path:/container/path" + - "volume-name:/data" env: - NGINX_HOST: example.com - restart_policy: always - user: nginx + ENV_VAR: value networks: - - podman + - app-network + restart_policy: always # no|always|on-failure|unless-stopped + user: "1000:1000" labels: - app: webserver - version: "1.0" + app: web + environment: prod + memory: "1g" + cpu_shares: 1024 + device: + - "/dev/sda:/dev/xvda:rwm" + security_opt: + - "seccomp=unconfined" + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost"] + interval: 30s + timeout: 10s + retries: 3 + # Systemd service generation (optional) + generate_systemd: + path: "/etc/systemd/system" + restart_policy: always + after: ["network.target"] + wants: ["network-online.target"] ``` -### Network Management Variables +### Network Management ```yaml podman_networks: - name: app-network - driver: bridge - subnet: "10.89.0.0/24" - gateway: "10.89.0.1" state: present - internal: false - disable_dns: false + driver: bridge # bridge|macvlan|ipvlan + subnet: "172.20.0.0/16" + gateway: "172.20.0.1" + internal: false # true for isolated networks dns: - "8.8.8.8" + - "1.1.1.1" options: mtu: 1500 vlan: 100 - ipam_driver: "host-local" - interface_name: "podman1" - route: - - "10.10.0.0/16,192.168.1.1" + + # Advanced networking - name: macvlan-net driver: macvlan - macvlan: "eth0" + macvlan: "eth0" # Parent interface subnet: "192.168.1.0/24" + - name: ipv6-net driver: bridge subnet: "fd00::/64" ipv6: true - recreate: false ``` -### Volume Management Variables +### Volume Management ```yaml podman_volumes: - name: app-data state: present - driver: local + driver: local # local|tmpfs labels: - environment: production backup: daily + environment: prod options: - "device=/dev/sdb1" - "type=ext4" - "o=rw" + - name: tmpfs-volume - state: present driver: tmpfs options: - "tmpfs-size=100m" - "tmpfs-mode=1777" - - name: quadlet-volume - state: quadlet - driver: local - quadlet_filename: "custom-volume" - quadlet_file_mode: "0640" - quadlet_options: - - "Group=192" - - "Copy=true" - recreate: false - debug: false ``` -### Pod Management Variables +### Pod Management ```yaml podman_pods: @@ -184,7 +165,6 @@ podman_pods: state: started ports: - "8080:80" - - "3306:3306" networks: - frontend hostname: webapp @@ -192,162 +172,223 @@ podman_pods: - "8.8.8.8" labels: app: webapp - tier: frontend volumes: - "webapp-data:/data" - infra: true - infra_image: "k8s.gcr.io/pause:3.1" memory: "2g" - cpu_shares: "1024" - security_opt: - - "seccomp=unconfined" - add_host: - - "database.local:127.0.0.1" - share: "net,ipc" - userns: "auto" - device: - - "/dev/sda:/dev/xvda:rwm" - sysctl: - net.core.somaxconn: "1024" - exit_policy: "stop" - - name: quadlet-pod - state: quadlet - quadlet_filename: "custom-pod" - quadlet_options: - - "AutoUpdate=registry" - generate_systemd: - path: "/etc/systemd/system" - restart_policy: "always" + cpu_shares: 1024 + share: "net,ipc" # Shared namespaces + infra: true # Use infra container + infra_image: "registry.k8s.io/pause:3.9" ``` -### Systemd Service Generation - -The role can automatically generate systemd service files for containers and pods. This functionality helps in managing container lifecycle through systemd. - -* `podman_generate_systemd`: Enable systemd service generation (default: `true`) -* `podman_systemd_dir`: Directory for generated service files (default: `/etc/systemd/system`) - -**Global Systemd Options** (`podman_systemd_options`): +### Advanced Configuration +#### Registry & Security Policy ```yaml +# Basic registry setup (development) +podman_policy_default_type: "insecureAcceptAnything" +podman_policy_reject_unknown_registries: false + +# Production security (with signatures) +podman_policy_default_type: "reject" +podman_policy_reject_unknown_registries: true +podman_policy_trusted_registries: + - registry: "docker.io" + type: "insecureAcceptAnything" + unqualified_search: true + + - registry: "internal.company.com" + type: "signedBy" + keyPath: "/etc/pki/containers/company.gpg" + insecure: false + mirror: + - location: "backup.company.com" +``` + +#### Systemd Service Generation +```yaml +# Global systemd settings +podman_generate_systemd: true podman_systemd_options: - new: true # Generate new service files - force: true # Overwrite existing files - restart_policy: always # Default restart policy - stop_timeout: 120 # Stop timeout in seconds - no_header: false # Include header in service files - wants: [] # Systemd unit Wants - after: [] # Systemd unit After - requires: [] # Systemd unit Requires - container_prefix: "container-" # Prefix for container service names - pod_prefix: "pod-" # Prefix for pod service names - restart_sec: 30 # Restart delay in seconds + restart_policy: always + stop_timeout: 120 + after: ["network.target"] + wants: ["network-online.target"] + container_prefix: "container-" + pod_prefix: "pod-" ``` -**Per-Container/Pod Configuration:** - -You can override global systemd options for individual containers or pods: - -```yaml -podman_containers: - - name: webapp - image: nginx:latest - systemd: - restart_policy: always - after: ["network.target"] - wants: ["network-online.target"] - restart_sec: 10 - -podman_pods: - - name: database - systemd: - restart_policy: on-failure - requires: ["network.target"] - time: 180 -``` - -When `systemd` is defined for a container or pod, the role will: -1. Generate a systemd service file -2. Place it in the specified directory -3. Reload systemd daemon -4. (Optional) Enable and start the service - -**Note:** Container/pod-specific options take precedence over global options defined in `podman_systemd_options`. - -### Resource Pruning - -The role can automatically clean up unused Podman resources to free up disk space and maintain system hygiene. - -* `podman_prune_enabled`: Enable automatic pruning of unused resources (default: `true`) -* `podman_prune_options`: Configuration for what should be pruned - +#### Resource Cleanup ```yaml +# Auto-cleanup unused resources +podman_prune_enabled: true podman_prune_options: container: true # Remove stopped containers image: true # Remove unused images network: true # Remove unused networks - system: true # Prune all unused data - system_all: true # Prune all unused data including build cache volume: true # Remove unused volumes + system: true # Full system cleanup ``` -You can selectively disable certain types of pruning by setting their values to `false`: - +#### Storage Configuration ```yaml -podman_prune_options: - container: true # Still remove containers - image: false # Keep all images - network: true # Remove unused networks - system: false # Keep system data - system_all: false # Keep build cache - volume: false # Keep all volumes +podman_configure_storage: true +podman_storage_driver: overlay +podman_storage_graphroot: /var/lib/containers/storage +podman_storage_runroot: /run/containers/storage ``` -Dependencies ------------- - -* `containers.podman` collection for container management tasks - -Example Playbook ----------------- - +#### API & Socket Services ```yaml -- hosts: servers +podman_enable_socket: true # Enable Podman socket +podman_enable_api_service: true # Enable REST API +``` + +--- + +## 🏷️ Available Tags + +Run specific parts of the role: + +```bash +# Install only +ansible-playbook -t podman-install playbook.yml + +# Configure only +ansible-playbook -t podman-configure playbook.yml + +# Manage containers only +ansible-playbook -t podman-containers playbook.yml + +# Manage networks only +ansible-playbook -t podman-networks playbook.yml +``` + +**Available tags:** +- `podman` - Run everything +- `podman-install` - Package installation +- `podman-configure` - Configuration files +- `podman-services` - System services +- `podman-networks` - Network management +- `podman-volumes` - Volume management +- `podman-pods` - Pod management +- `podman-containers` - Container management +- `podman-systemd` - Systemd service generation +- `podman-prune` - Resource cleanup + +--- + +## 📚 Example Playbooks + +### Development Environment +```yaml +- hosts: dev-servers roles: - - role: keepit.podman + - role: podman vars: + # Permissive for development + podman_policy_default_type: "insecureAcceptAnything" podman_enable_socket: true + podman_containers: - - name: web-server + - name: dev-web image: nginx:latest state: started ports: - - "80:80" + - "8080:80" volumes: - - "/var/www/html:/usr/share/nginx/html:ro" - podman_networks: - - name: web-network - subnet: "172.20.0.0/16" + - "./web:/usr/share/nginx/html" ``` -Tags ----- +### Production Environment +```yaml +- hosts: prod-servers + roles: + - role: podman + vars: + # Strict security for production + podman_policy_default_type: "reject" + podman_policy_reject_unknown_registries: true + podman_policy_trusted_registries: + - registry: "registry.company.com" + type: "signedBy" + keyPath: "/etc/pki/containers/prod.gpg" + + podman_containers: + - name: prod-app + image: registry.company.com/app:v1.2.3 + state: started + restart_policy: always + memory: "2g" + cpu_shares: 2048 + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost/health"] + interval: 30s + timeout: 10s + retries: 3 +``` -* `podman` - Run all tasks -* `podman-install` - Install packages only -* `podman-configure` - Configure Podman only -* `podman-services` - Manage services only -* `podman-networks` - Manage networks only -* `podman-volumes` - Manage volumes only -* `podman-pods` - Manage pods only -* `podman-containers` - Manage containers only +### Multi-Service Application +```yaml +- hosts: app-servers + roles: + - role: podman + vars: + podman_networks: + - name: app-network + subnet: "172.20.0.0/16" + + podman_volumes: + - name: postgres-data + - name: redis-data + - name: app-uploads + + podman_containers: + # Database + - name: postgres + image: postgres:15 + state: started + networks: + - app-network + volumes: + - "postgres-data:/var/lib/postgresql/data" + env: + POSTGRES_DB: myapp + POSTGRES_PASSWORD: "{{ vault_db_password }}" + + # Cache + - name: redis + image: redis:7-alpine + state: started + networks: + - app-network + volumes: + - "redis-data:/data" + + # Application + - name: app + image: myapp:latest + state: started + networks: + - app-network + ports: + - "80:8080" + volumes: + - "app-uploads:/app/uploads" + env: + DATABASE_URL: "postgresql://postgres:{{ vault_db_password }}@postgres:5432/myapp" + REDIS_URL: "redis://redis:6379" + depends_on: + - postgres + - redis +``` -License -------- +--- + +## 📄 License MIT -Author Information ------------------- +## 👤 Author Daniel Akulenok From 1fb1c85b5d093c35b0c2d766eed73354731fd897 Mon Sep 17 00:00:00 2001 From: Daniel Akulenok Date: Fri, 12 Sep 2025 12:44:58 +0200 Subject: [PATCH 11/17] podman --- defaults/main.yml | 5 ----- molecule/default/molecule.yml | 2 +- molecule/default/prepare.yml | 2 +- tasks/containers.yml | 2 +- tasks/systemd.yml | 12 ++++++++---- 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/defaults/main.yml b/defaults/main.yml index 8c5af08..d1b24da 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -5,11 +5,6 @@ podman_packages: - podman - crun - - buildah - - skopeo - - fuse-overlayfs - - slirp4netns - - uidmap # Podman configuration podman_configure_registries: true diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml index b319cd8..9efaac2 100644 --- a/molecule/default/molecule.yml +++ b/molecule/default/molecule.yml @@ -14,7 +14,7 @@ platforms: - name: debian-bullseye image: docker.io/jrei/systemd-debian:12 <<: *platform_defaults - - name: debian-bullseye + - name: debian-trixie image: docker.io/jrei/systemd-debian:13 <<: *platform_defaults diff --git a/molecule/default/prepare.yml b/molecule/default/prepare.yml index febac6e..af9388d 100644 --- a/molecule/default/prepare.yml +++ b/molecule/default/prepare.yml @@ -5,7 +5,7 @@ tasks: - name: Ensure apt cache is up-to-date ansible.builtin.apt: - update_cache: yes + update_cache: true cache_valid_time: 3600 - name: Install prerequisites used by the role/tests diff --git a/tasks/containers.yml b/tasks/containers.yml index 60c90d5..fa220a3 100644 --- a/tasks/containers.yml +++ b/tasks/containers.yml @@ -224,4 +224,4 @@ loop: "{{ podman_containers }}" loop_control: label: "{{ item.name }}" - register: podman_container_results \ No newline at end of file + register: podman_container_results diff --git a/tasks/systemd.yml b/tasks/systemd.yml index dd08695..4288ea2 100644 --- a/tasks/systemd.yml +++ b/tasks/systemd.yml @@ -3,7 +3,9 @@ - name: Generate systemd service files for containers vars: - systemd_opts: "{{ item.systemd if item.systemd is defined else (item.generate_systemd if item.generate_systemd is defined else {}) }}" + systemd_opts: "{{ + item.systemd if item.systemd is defined + else (item.generate_systemd if item.generate_systemd is defined else {}) }}" containers.podman.podman_generate_systemd: name: "{{ item.name }}" dest: "{{ podman_systemd_dir }}" @@ -21,7 +23,7 @@ loop: "{{ podman_containers }}" loop_control: label: "{{ item.name }}" - when: + when: - podman_generate_systemd | bool - podman_containers is defined - podman_containers | length > 0 @@ -30,7 +32,9 @@ - name: Generate systemd service files for pods vars: - systemd_opts: "{{ item.systemd if item.systemd is defined else (item.generate_systemd if item.generate_systemd is defined else {}) }}" + systemd_opts: "{{ + item.systemd if item.systemd is defined + else (item.generate_systemd if item.generate_systemd is defined else {}) }}" containers.podman.podman_generate_systemd: name: "{{ item.name }}" dest: "{{ podman_systemd_dir }}" @@ -48,7 +52,7 @@ loop: "{{ podman_pods }}" loop_control: label: "{{ item.name }}" - when: + when: - podman_generate_systemd | bool - podman_pods is defined - podman_pods | length > 0 From 65dcedbe6cd88a6295b8f77271e34316e7e18cb8 Mon Sep 17 00:00:00 2001 From: Daniel Akulenok Date: Wed, 17 Sep 2025 14:45:49 +0200 Subject: [PATCH 12/17] add --- .ansible/.lock | 0 defaults/main.yml | 3 ++- tasks/containers.yml | 16 +--------------- tasks/systemd.yml | 4 ++-- 4 files changed, 5 insertions(+), 18 deletions(-) delete mode 100644 .ansible/.lock diff --git a/.ansible/.lock b/.ansible/.lock deleted file mode 100644 index e69de29..0000000 diff --git a/defaults/main.yml b/defaults/main.yml index 8c5af08..1123fc0 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -617,12 +617,13 @@ podman_pods: [] # requires: # - "postgresql.service" +podman_auto_remove: true # Systemd service generation configuration podman_generate_systemd: true podman_systemd_dir: /etc/systemd/system podman_systemd_options: new: true - force: true + force: false restart_policy: always stop_timeout: 120 no_header: false diff --git a/tasks/containers.yml b/tasks/containers.yml index 09d12f7..88e5f2f 100644 --- a/tasks/containers.yml +++ b/tasks/containers.yml @@ -18,7 +18,7 @@ detach: "{{ item.detach | bool | default(omit) }}" interactive: "{{ item.interactive | bool | default(omit) }}" tty: "{{ item.tty | bool | default(omit) }}" - rm: "{{ item.auto_remove | bool | default(omit) }}" + rm: "{{ item.auto_remove | bool | default(podman_auto_remove) }}" rmi: "{{ item.remove_image | bool | default(omit) }}" privileged: "{{ item.privileged | bool | default(omit) }}" read_only: "{{ item.read_only | bool | default(omit) }}" @@ -225,17 +225,3 @@ loop_control: label: "{{ item.name }}" register: podman_container_results - -- name: Enable and start container systemd services - ansible.builtin.systemd: - name: "container-{{ item.item.name }}" - enabled: true - state: started - daemon_reload: true - loop: "{{ podman_container_results.results }}" - loop_control: - label: "{{ item.item.name }}" - when: - - item.item.generate_systemd is defined - - item.item.generate_systemd - - item.item.state | default('present') in ['present', 'started'] diff --git a/tasks/systemd.yml b/tasks/systemd.yml index dd08695..7feae91 100644 --- a/tasks/systemd.yml +++ b/tasks/systemd.yml @@ -21,7 +21,7 @@ loop: "{{ podman_containers }}" loop_control: label: "{{ item.name }}" - when: + when: - podman_generate_systemd | bool - podman_containers is defined - podman_containers | length > 0 @@ -48,7 +48,7 @@ loop: "{{ podman_pods }}" loop_control: label: "{{ item.name }}" - when: + when: - podman_generate_systemd | bool - podman_pods is defined - podman_pods | length > 0 From 17fea0e02beb9941c465619b72c105788fa8b277 Mon Sep 17 00:00:00 2001 From: Daniel Akulenok Date: Sat, 10 Jan 2026 23:21:34 +0100 Subject: [PATCH 13/17] podman --- README.md | 178 +++++++++----------- defaults/main.yml | 46 ++++- handlers/main.yml | 58 +++++-- tasks/containers.yml | 334 +++++++++++++++++++------------------ tasks/host_directories.yml | 18 ++ tasks/main.yml | 7 + tasks/networks.yml | 43 +++-- tasks/pods.yml | 121 +++++++------- tasks/volumes.yml | 27 +-- 9 files changed, 452 insertions(+), 380 deletions(-) create mode 100644 tasks/host_directories.yml diff --git a/README.md b/README.md index e140dfa..97978bd 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ ``` ### 2. Run Your First Container + ```yaml - hosts: servers roles: @@ -20,7 +21,6 @@ podman_containers: - name: nginx image: nginx:latest - state: started ports: - "80:80" ``` @@ -28,11 +28,11 @@ ### 3. Common Patterns **Web application with database:** + ```yaml podman_containers: - name: webapp image: myapp:latest - state: started ports: - "8080:8080" env: @@ -40,7 +40,6 @@ podman_containers: - name: database image: postgres:15 - state: started volumes: - "db-data:/var/lib/postgresql/data" env: @@ -63,127 +62,99 @@ podman_volumes: --- -## 🔧 Complete Feature Reference +## 🔧 Configuration Guide -### Container Management +### Management Modes (Quadlet vs Systemd) + +This role automatically selects the best management engine based on your operating system version: + +- **Quadlet** (Default for Debian 13+): Uses Podman's native systemd generator via `.container` files. This is the modern, preferred method. +- **Systemd** (Default for Debian < 13): Uses legacy `podman generate systemd` to create service units. + +The determination is controlled by the logic in `defaults/main.yml`: + +```yaml +# Auto-detected. True for Debian 13+, False otherwise. +podman_use_quadlet: "{{ ... }}" + +# Sets default state to 'quadlet' or 'started'/'present' accordingly +podman_mode: ... +``` + +**Recommendation:** Do **not** set `state` explicitly in your variables (e.g., `podman_containers`) unless you have a specific reason. The role's defaults will ensure the correct state is applied for your OS version. + +Users can still manually control defaults if needed: + +```yaml +# Force Quadlet usage on older systems (if supported) +podman_use_quadlet: true + +# Or customize default options +podman_container_defaults: + quadlet_options: + - "AutoUpdate=registry" + - | + [Install] + WantedBy=default.target +``` + +If you prefer the standard imperative approach (similar to `docker run`) regardless of OS, you can override the defaults or set `state: started` on individual items. + +### Resource Definition + +The variables `podman_containers`, `podman_networks`, `podman_volumes`, and `podman_pods` accept standard parameters from the [containers.podman](https://docs.ansible.com/ansible/latest/collections/containers/podman/index.html) collection. + +#### Containers ```yaml podman_containers: - - name: my-app + - name: nginx image: nginx:latest - state: started # started|stopped|present|absent - ports: - - "8080:80" - - "443:443" + ports: ["80:80"] volumes: - - "/host/path:/container/path" - - "volume-name:/data" + - "html_vol:/usr/share/nginx/html" + - "./local_conf:/etc/nginx/conf.d:ro" env: - ENV_VAR: value - networks: - - app-network - restart_policy: always # no|always|on-failure|unless-stopped - user: "1000:1000" - labels: - app: web - environment: prod - memory: "1g" - cpu_shares: 1024 - device: - - "/dev/sda:/dev/xvda:rwm" - security_opt: - - "seccomp=unconfined" - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost"] - interval: 30s - timeout: 10s - retries: 3 - # Systemd service generation (optional) - generate_systemd: - path: "/etc/systemd/system" - restart_policy: always - after: ["network.target"] - wants: ["network-online.target"] + NGINX_HOST: example.com + # Quadlet-specific options can be added as a list + quadlet_options: + - "AutoUpdate=registry" ``` -### Network Management +#### Networks ```yaml podman_networks: - - name: app-network - state: present - driver: bridge # bridge|macvlan|ipvlan - subnet: "172.20.0.0/16" - gateway: "172.20.0.1" - internal: false # true for isolated networks - dns: - - "8.8.8.8" - - "1.1.1.1" - options: - mtu: 1500 - vlan: 100 - - # Advanced networking - - name: macvlan-net - driver: macvlan - macvlan: "eth0" # Parent interface - subnet: "192.168.1.0/24" - - - name: ipv6-net - driver: bridge - subnet: "fd00::/64" - ipv6: true + - name: app_net + subnet: "10.0.0.0/24" + gateway: "10.0.0.1" + dns: ["8.8.8.8"] ``` -### Volume Management +#### Volumes ```yaml +# Toggle automatic creation of host directories for bind mounts +podman_create_volumes: true + podman_volumes: - - name: app-data - state: present - driver: local # local|tmpfs - labels: - backup: daily - environment: prod - options: - - "device=/dev/sdb1" - - "type=ext4" - - "o=rw" - - - name: tmpfs-volume - driver: tmpfs - options: - - "tmpfs-size=100m" - - "tmpfs-mode=1777" + - name: db_data + # state defaults to 'quadlet' ``` -### Pod Management +#### Pods ```yaml podman_pods: - - name: webapp-pod - state: started - ports: - - "8080:80" - networks: - - frontend - hostname: webapp - dns: - - "8.8.8.8" - labels: - app: webapp - volumes: - - "webapp-data:/data" - memory: "2g" - cpu_shares: 1024 - share: "net,ipc" # Shared namespaces - infra: true # Use infra container - infra_image: "registry.k8s.io/pause:3.9" + - name: app_pod + ports: ["8080:80"] + share: "net,ipc" ``` ### Advanced Configuration #### Registry & Security Policy + ```yaml # Basic registry setup (development) podman_policy_default_type: "insecureAcceptAnything" @@ -206,6 +177,7 @@ podman_policy_trusted_registries: ``` #### Systemd Service Generation + ```yaml # Global systemd settings podman_generate_systemd: true @@ -219,6 +191,7 @@ podman_systemd_options: ``` #### Resource Cleanup + ```yaml # Auto-cleanup unused resources podman_prune_enabled: true @@ -231,6 +204,7 @@ podman_prune_options: ``` #### Storage Configuration + ```yaml podman_configure_storage: true podman_storage_driver: overlay @@ -239,6 +213,7 @@ podman_storage_runroot: /run/containers/storage ``` #### API & Socket Services + ```yaml podman_enable_socket: true # Enable Podman socket podman_enable_api_service: true # Enable REST API @@ -265,6 +240,7 @@ ansible-playbook -t podman-networks playbook.yml ``` **Available tags:** + - `podman` - Run everything - `podman-install` - Package installation - `podman-configure` - Configuration files @@ -281,6 +257,7 @@ ansible-playbook -t podman-networks playbook.yml ## 📚 Example Playbooks ### Development Environment + ```yaml - hosts: dev-servers roles: @@ -293,7 +270,6 @@ ansible-playbook -t podman-networks playbook.yml podman_containers: - name: dev-web image: nginx:latest - state: started ports: - "8080:80" volumes: @@ -301,6 +277,7 @@ ansible-playbook -t podman-networks playbook.yml ``` ### Production Environment + ```yaml - hosts: prod-servers roles: @@ -317,7 +294,6 @@ ansible-playbook -t podman-networks playbook.yml podman_containers: - name: prod-app image: registry.company.com/app:v1.2.3 - state: started restart_policy: always memory: "2g" cpu_shares: 2048 @@ -329,6 +305,7 @@ ansible-playbook -t podman-networks playbook.yml ``` ### Multi-Service Application + ```yaml - hosts: app-servers roles: @@ -347,7 +324,6 @@ ansible-playbook -t podman-networks playbook.yml # Database - name: postgres image: postgres:15 - state: started networks: - app-network volumes: @@ -359,7 +335,6 @@ ansible-playbook -t podman-networks playbook.yml # Cache - name: redis image: redis:7-alpine - state: started networks: - app-network volumes: @@ -368,7 +343,6 @@ ansible-playbook -t podman-networks playbook.yml # Application - name: app image: myapp:latest - state: started networks: - app-network ports: diff --git a/defaults/main.yml b/defaults/main.yml index 586a645..7bdd496 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -166,14 +166,27 @@ podman_policy_trusted_registries: podman_enable_socket: false podman_enable_api_service: false +# Determine if Quadlet should be used (Debian 13+ or other distros) +podman_use_quadlet: "{{ not (ansible_distribution | default('Debian') == 'Debian' and ansible_distribution_major_version | default('13') | int < 13) }}" +podman_mode: "{{ 'quadlet' if podman_use_quadlet else 'started' }}" + # Container management +podman_container_defaults: + state: "{{ podman_mode }}" + pull: newer + quadlet_options: + - "AutoUpdate=registry" + - "Pull=newer" + - | + [Install] + WantedBy=default.target + podman_containers: [] # Example container configurations: # podman_containers: # # Basic web server container # - name: nginx # image: docker.io/nginx:latest -# state: started # ports: # - "8080:80" # volumes: @@ -192,7 +205,6 @@ podman_containers: [] # # Advanced container with comprehensive configuration # - name: app-server # image: registry.example.com/myapp:v1.2.3 -# state: started # # Basic runtime options # command: ["/app/start.sh", "--config", "/etc/app/config.yaml"] # entrypoint: "/entrypoint.sh" @@ -473,6 +485,13 @@ podman_containers: [] # - "no-new-privileges=true" # Network management +podman_network_defaults: + state: "{{ 'quadlet' if podman_use_quadlet else 'present' }}" + quadlet_options: + - | + [Install] + WantedBy=default.target + podman_networks: [] # Example network configuration: # podman_networks: @@ -480,7 +499,6 @@ podman_networks: [] # driver: bridge # subnet: "10.89.0.0/24" # gateway: "10.89.0.1" -# state: present # - name: frontend-network # driver: bridge # subnet: "172.20.0.0/16" @@ -508,14 +526,20 @@ podman_networks: [] # - "10.10.0.0/16,192.168.1.1" # Volume management +podman_create_volumes: true +podman_volume_defaults: + state: "{{ 'quadlet' if podman_use_quadlet else 'present' }}" + quadlet_options: + - | + [Install] + WantedBy=default.target + podman_volumes: [] # Example volume configuration: # podman_volumes: # - name: app-data -# state: present # driver: local # - name: database-volume -# state: present # driver: local # labels: # environment: production @@ -525,7 +549,6 @@ podman_volumes: [] # - "type=ext4" # - "o=rw" # - name: tmpfs-volume -# state: present # driver: tmpfs # options: # - "tmpfs-size=100m" @@ -542,11 +565,17 @@ podman_volumes: [] # debug: false # Pod management +podman_pod_defaults: + state: "{{ podman_mode }}" + quadlet_options: + - | + [Install] + WantedBy=default.target + podman_pods: [] # Example pod configuration: # podman_pods: # - name: webapp-pod -# state: started # ports: # - "8080:80" # - "3306:3306" @@ -576,7 +605,6 @@ podman_pods: [] # share: "net,ipc" # userns: "auto" # - name: monitoring-pod -# state: started # ports: # - "9090:9090" # device: @@ -614,7 +642,7 @@ podman_pods: [] podman_auto_remove: true # Systemd service generation configuration -podman_generate_systemd: true +podman_generate_systemd: "{{ not podman_use_quadlet }}" podman_systemd_dir: /etc/systemd/system podman_systemd_options: new: true diff --git a/handlers/main.yml b/handlers/main.yml index e079a66..5e3dc2c 100644 --- a/handlers/main.yml +++ b/handlers/main.yml @@ -11,30 +11,52 @@ ansible.builtin.systemd: daemon_reload: true -- name: Start Podman pods +- name: Restart Podman pods ansible.builtin.systemd: - name: "{{ podman_systemd_options.pod_prefix }}{{ item.name }}" - enabled: true - state: started - daemon_reload: true + name: "{{ (podman_systemd_options.pod_prefix if podman_generate_systemd | bool else '') ~ item ~ ('-pod' if not podman_generate_systemd | bool else '') }}" + state: restarted listen: Reload systemd - loop: "{{ podman_pods }}" + loop: "{{ podman_pod_output.results | selectattr('changed', 'equalto', true) | map(attribute='item.name') | list }}" loop_control: - label: "{{ item.name }}" + label: "{{ item }}" when: - - podman_pods is defined - - podman_pods | length > 0 + - podman_pod_output is defined + - podman_pod_output.results | length > 0 -- name: Start Podman podless containers +- name: Restart Podman containers ansible.builtin.systemd: - name: "{{ podman_systemd_options.container_prefix }}{{ item.name }}" - enabled: true - state: started - daemon_reload: true + name: "{{ (podman_systemd_options.container_prefix if podman_generate_systemd | bool else '') ~ item }}" + state: restarted listen: Reload systemd - loop: "{{ podman_containers | rejectattr('pod', 'defined') | list }}" + loop: "{{ podman_container_output.results | selectattr('changed', 'equalto', true) | map(attribute='item.name') | list }}" loop_control: - label: "{{ item.name }}" + label: "{{ item }}" when: - - podman_containers is defined - - podman_containers | length > 0 + - podman_container_output is defined + - podman_container_output.results | length > 0 + +- name: Restart Podman networks + ansible.builtin.systemd: + name: "{{ item }}-network" + state: restarted + listen: Reload systemd + loop: "{{ podman_network_output.results | selectattr('changed', 'equalto', true) | map(attribute='item.name') | list }}" + loop_control: + label: "{{ item }}" + when: + - podman_network_output is defined + - podman_network_output.results | length > 0 + - not podman_generate_systemd | bool + +- name: Restart Podman volumes + ansible.builtin.systemd: + name: "{{ item }}-volume" + state: restarted + listen: Reload systemd + loop: "{{ podman_volume_output.results | selectattr('changed', 'equalto', true) | map(attribute='item.name') | list }}" + loop_control: + label: "{{ item }}" + when: + - podman_volume_output is defined + - podman_volume_output.results | length > 0 + - not podman_generate_systemd | bool diff --git a/tasks/containers.yml b/tasks/containers.yml index 88e5f2f..7fc0bd4 100644 --- a/tasks/containers.yml +++ b/tasks/containers.yml @@ -2,226 +2,230 @@ # Manage Podman containers - name: Manage Podman containers + vars: + container_item: "{{ podman_container_defaults | default({}) | combine(item) }}" containers.podman.podman_container: - name: "{{ item.name }}" - image: "{{ item.image }}" - state: "{{ item.state | default('present') }}" + name: "{{ container_item.name }}" + image: "{{ container_item.image }}" + state: "{{ container_item.state | default('present') }}" # Basic container configuration - command: "{{ item.command | default(omit) }}" - entrypoint: "{{ item.entrypoint | default(omit) }}" - user: "{{ item.user | default(omit) }}" - workdir: "{{ item.working_dir | default(omit) }}" - hostname: "{{ item.hostname | default(omit) }}" + command: "{{ container_item.command | default(omit) }}" + entrypoint: "{{ container_item.entrypoint | default(omit) }}" + user: "{{ container_item.user | default(omit) }}" + workdir: "{{ container_item.working_dir | default(omit) }}" + hostname: "{{ container_item.hostname | default(omit) }}" # Container runtime options - detach: "{{ item.detach | bool | default(omit) }}" - interactive: "{{ item.interactive | bool | default(omit) }}" - tty: "{{ item.tty | bool | default(omit) }}" - rm: "{{ item.auto_remove | bool | default(podman_auto_remove) }}" - rmi: "{{ item.remove_image | bool | default(omit) }}" - privileged: "{{ item.privileged | bool | default(omit) }}" - read_only: "{{ item.read_only | bool | default(omit) }}" - read_only_tmpfs: "{{ item.read_only_tmpfs | bool | default(omit) }}" - init: "{{ item.init | bool | default(omit) }}" - init_path: "{{ item.init_path | default(omit) }}" + detach: "{{ container_item.detach | bool | default(omit) }}" + interactive: "{{ container_item.interactive | bool | default(omit) }}" + tty: "{{ container_item.tty | bool | default(omit) }}" + rm: "{{ container_item.auto_remove | bool | default(podman_auto_remove) }}" + rmi: "{{ container_item.remove_image | bool | default(omit) }}" + privileged: "{{ container_item.privileged | bool | default(omit) }}" + read_only: "{{ container_item.read_only | bool | default(omit) }}" + read_only_tmpfs: "{{ container_item.read_only_tmpfs | bool | default(omit) }}" + init: "{{ container_item.init | bool | default(omit) }}" + init_path: "{{ container_item.init_path | default(omit) }}" # Networking - network: "{{ item.networks | default(omit) }}" - publish: "{{ item.ports | default(omit) }}" - publish_all: "{{ item.publish_all | bool | default(omit) }}" - expose: "{{ item.expose | default(omit) }}" - ip: "{{ item.ip | default(omit) }}" - ip6: "{{ item.ip6 | default(omit) }}" - mac_address: "{{ item.mac_address | default(omit) }}" - network_aliases: "{{ item.network_aliases | default(omit) }}" - dns: "{{ item.dns | default(omit) }}" - dns_option: "{{ item.dns_options | default(omit) }}" - dns_search: "{{ item.dns_search | default(omit) }}" - etc_hosts: "{{ item.add_hosts | default(omit) }}" - no_hosts: "{{ item.no_hosts | bool | default(omit) }}" + network: "{{ container_item.networks | default(omit) }}" + publish: "{{ container_item.ports | default(omit) }}" + publish_all: "{{ container_item.publish_all | bool | default(omit) }}" + expose: "{{ container_item.expose | default(omit) }}" + ip: "{{ container_item.ip | default(omit) }}" + ip6: "{{ container_item.ip6 | default(omit) }}" + mac_address: "{{ container_item.mac_address | default(omit) }}" + network_aliases: "{{ container_item.network_aliases | default(omit) }}" + dns: "{{ container_item.dns | default(omit) }}" + dns_option: "{{ container_item.dns_options | default(omit) }}" + dns_search: "{{ container_item.dns_search | default(omit) }}" + etc_hosts: "{{ container_item.add_hosts | default(omit) }}" + no_hosts: "{{ container_item.no_hosts | bool | default(omit) }}" # Storage and volumes - volume: "{{ item.volumes | default(omit) }}" - volumes_from: "{{ item.volumes_from | default(omit) }}" - mount: "{{ item.mounts | default(omit) }}" - tmpfs: "{{ item.tmpfs | default(omit) }}" - image_volume: "{{ item.image_volume | default(omit) }}" + volume: "{{ container_item.volumes | default(omit) }}" + volumes_from: "{{ container_item.volumes_from | default(omit) }}" + mount: "{{ container_item.mounts | default(omit) }}" + tmpfs: "{{ container_item.tmpfs | default(omit) }}" + image_volume: "{{ container_item.image_volume | default(omit) }}" # Environment variables - env: "{{ item.env | default(omit) }}" - env_file: "{{ item.env_files | default(omit) }}" - env_host: "{{ item.env_host | bool | default(omit) }}" - env_merge: "{{ item.env_merge | default(omit) }}" - unsetenv: "{{ item.unsetenv | default(omit) }}" - unsetenv_all: "{{ item.unsetenv_all | bool | default(omit) }}" + env: "{{ container_item.env | default(omit) }}" + env_file: "{{ container_item.env_files | default(omit) }}" + env_host: "{{ container_item.env_host | bool | default(omit) }}" + env_merge: "{{ container_item.env_merge | default(omit) }}" + unsetenv: "{{ container_item.unsetenv | default(omit) }}" + unsetenv_all: "{{ container_item.unsetenv_all | bool | default(omit) }}" # Resource constraints - memory: "{{ item.memory | default(omit) }}" - memory_reservation: "{{ item.memory_reservation | default(omit) }}" - memory_swap: "{{ item.memory_swap | default(omit) }}" - memory_swappiness: "{{ item.memory_swappiness | default(omit) }}" - kernel_memory: "{{ item.kernel_memory | default(omit) }}" - cpus: "{{ item.cpus | default(omit) }}" - cpu_shares: "{{ item.cpu_shares | default(omit) }}" - cpu_period: "{{ item.cpu_period | default(omit) }}" - cpu_quota: "{{ item.cpu_quota | default(omit) }}" - cpu_rt_period: "{{ item.cpu_rt_period | default(omit) }}" - cpu_rt_runtime: "{{ item.cpu_rt_runtime | default(omit) }}" - cpuset_cpus: "{{ item.cpuset_cpus | default(omit) }}" - cpuset_mems: "{{ item.cpuset_mems | default(omit) }}" - blkio_weight: "{{ item.blkio_weight | default(omit) }}" - blkio_weight_device: "{{ item.blkio_weight_device | default(omit) }}" - oom_kill_disable: "{{ item.oom_kill_disable | bool | default(omit) }}" - oom_score_adj: "{{ item.oom_score_adj | default(omit) }}" - pids_limit: "{{ item.pids_limit | default(omit) }}" + memory: "{{ container_item.memory | default(omit) }}" + memory_reservation: "{{ container_item.memory_reservation | default(omit) }}" + memory_swap: "{{ container_item.memory_swap | default(omit) }}" + memory_swappiness: "{{ container_item.memory_swappiness | default(omit) }}" + kernel_memory: "{{ container_item.kernel_memory | default(omit) }}" + cpus: "{{ container_item.cpus | default(omit) }}" + cpu_shares: "{{ container_item.cpu_shares | default(omit) }}" + cpu_period: "{{ container_item.cpu_period | default(omit) }}" + cpu_quota: "{{ container_item.cpu_quota | default(omit) }}" + cpu_rt_period: "{{ container_item.cpu_rt_period | default(omit) }}" + cpu_rt_runtime: "{{ container_item.cpu_rt_runtime | default(omit) }}" + cpuset_cpus: "{{ container_item.cpuset_cpus | default(omit) }}" + cpuset_mems: "{{ container_item.cpuset_mems | default(omit) }}" + blkio_weight: "{{ container_item.blkio_weight | default(omit) }}" + blkio_weight_device: "{{ container_item.blkio_weight_device | default(omit) }}" + oom_kill_disable: "{{ container_item.oom_kill_disable | bool | default(omit) }}" + oom_score_adj: "{{ container_item.oom_score_adj | default(omit) }}" + pids_limit: "{{ container_item.pids_limit | default(omit) }}" # Device access - device: "{{ item.devices | default(omit) }}" - device_cgroup_rule: "{{ item.device_cgroup_rule | default(omit) }}" - device_read_bps: "{{ item.device_read_bps | default(omit) }}" - device_read_iops: "{{ item.device_read_iops | default(omit) }}" - device_write_bps: "{{ item.device_write_bps | default(omit) }}" - device_write_iops: "{{ item.device_write_iops | default(omit) }}" - gpus: "{{ item.gpus | default(omit) }}" + device: "{{ container_item.devices | default(omit) }}" + device_cgroup_rule: "{{ container_item.device_cgroup_rule | default(omit) }}" + device_read_bps: "{{ container_item.device_read_bps | default(omit) }}" + device_read_iops: "{{ container_item.device_read_iops | default(omit) }}" + device_write_bps: "{{ container_item.device_write_bps | default(omit) }}" + device_write_iops: "{{ container_item.device_write_iops | default(omit) }}" + gpus: "{{ container_item.gpus | default(omit) }}" # Security options - security_opt: "{{ item.security_opt | default(omit) }}" - cap_add: "{{ item.cap_add | default(omit) }}" - cap_drop: "{{ item.cap_drop | default(omit) }}" - seccomp_policy: "{{ item.seccomp_policy | default(omit) }}" - userns: "{{ item.userns | default(omit) }}" - uidmap: "{{ item.uidmap | default(omit) }}" - gidmap: "{{ item.gidmap | default(omit) }}" - subuidname: "{{ item.subuidname | default(omit) }}" - subgidname: "{{ item.subgidname | default(omit) }}" - group_add: "{{ item.groups | default(omit) }}" - group_entry: "{{ item.group_entry | default(omit) }}" - passwd: "{{ item.passwd | default(omit) }}" - passwd_entry: "{{ item.passwd_entry | default(omit) }}" + security_opt: "{{ container_item.security_opt | default(omit) }}" + cap_add: "{{ container_item.cap_add | default(omit) }}" + cap_drop: "{{ container_item.cap_drop | default(omit) }}" + seccomp_policy: "{{ container_item.seccomp_policy | default(omit) }}" + userns: "{{ container_item.userns | default(omit) }}" + uidmap: "{{ container_item.uidmap | default(omit) }}" + gidmap: "{{ container_item.gidmap | default(omit) }}" + subuidname: "{{ container_item.subuidname | default(omit) }}" + subgidname: "{{ container_item.subgidname | default(omit) }}" + group_add: "{{ container_item.groups | default(omit) }}" + group_entry: "{{ container_item.group_entry | default(omit) }}" + passwd: "{{ container_item.passwd | default(omit) }}" + passwd_entry: "{{ container_item.passwd_entry | default(omit) }}" # Namespaces - ipc: "{{ item.ipc_mode | default(omit) }}" - pid: "{{ item.pid_mode | default(omit) }}" - uts: "{{ item.uts | default(omit) }}" - cgroupns: "{{ item.cgroupns | default(omit) }}" + ipc: "{{ container_item.ipc_mode | default(omit) }}" + pid: "{{ container_item.pid_mode | default(omit) }}" + uts: "{{ container_item.uts | default(omit) }}" + cgroupns: "{{ container_item.cgroupns | default(omit) }}" # Cgroups - cgroups: "{{ item.cgroups | default(omit) }}" - cgroup_parent: "{{ item.cgroup_parent | default(omit) }}" - cgroup_conf: "{{ item.cgroup_conf | default(omit) }}" + cgroups: "{{ container_item.cgroups | default(omit) }}" + cgroup_parent: "{{ container_item.cgroup_parent | default(omit) }}" + cgroup_conf: "{{ container_item.cgroup_conf | default(omit) }}" # System configuration - sysctl: "{{ item.sysctl | default(omit) }}" - systemd: "{{ item.systemd | default(omit) }}" - ulimit: "{{ item.ulimits | default(omit) }}" - umask: "{{ item.umask | default(omit) }}" + sysctl: "{{ container_item.sysctl | default(omit) }}" + systemd: "{{ container_item.systemd | default(omit) }}" + ulimit: "{{ container_item.ulimits | default(omit) }}" + umask: "{{ container_item.umask | default(omit) }}" # Shared memory and tmpfs - shm_size: "{{ item.shm_size | default(omit) }}" - shm_size_systemd: "{{ item.shm_size_systemd | default(omit) }}" + shm_size: "{{ container_item.shm_size | default(omit) }}" + shm_size_systemd: "{{ container_item.shm_size_systemd | default(omit) }}" # Pods - pod: "{{ item.pod | default(omit) }}" - pod_id_file: "{{ item.pod_id_file | default(omit) }}" + pod: "{{ container_item.pod | default(omit) }}" + pod_id_file: "{{ container_item.pod_id_file | default(omit) }}" # Logging - log_driver: "{{ item.log_driver | default(omit) }}" - log_opt: "{{ item.log_options | default(omit) }}" - log_level: "{{ item.log_level | default(omit) }}" + log_driver: "{{ container_item.log_driver | default(omit) }}" + log_opt: "{{ container_item.log_options | default(omit) }}" + log_level: "{{ container_item.log_level | default(omit) }}" # Health checks - healthcheck: "{{ item.healthcheck | default(omit) }}" - healthcheck_interval: "{{ item.healthcheck_interval | default(omit) }}" - healthcheck_timeout: "{{ item.healthcheck_timeout | default(omit) }}" - healthcheck_start_period: "{{ item.healthcheck_start_period | default(omit) }}" - healthcheck_retries: "{{ item.healthcheck_retries | default(omit) }}" - healthcheck_failure_action: "{{ item.healthcheck_failure_action | default(omit) }}" - no_healthcheck: "{{ item.no_healthcheck | bool | default(omit) }}" + healthcheck: "{{ container_item.healthcheck | default(omit) }}" + healthcheck_interval: "{{ container_item.healthcheck_interval | default(omit) }}" + healthcheck_timeout: "{{ container_item.healthcheck_timeout | default(omit) }}" + healthcheck_start_period: "{{ container_item.healthcheck_start_period | default(omit) }}" + healthcheck_retries: "{{ container_item.healthcheck_retries | default(omit) }}" + healthcheck_failure_action: "{{ container_item.healthcheck_failure_action | default(omit) }}" + no_healthcheck: "{{ container_item.no_healthcheck | bool | default(omit) }}" # Startup health checks - health_startup_cmd: "{{ item.health_startup_cmd | default(omit) }}" - health_startup_interval: "{{ item.health_startup_interval | default(omit) }}" - health_startup_timeout: "{{ item.health_startup_timeout | default(omit) }}" - health_startup_retries: "{{ item.health_startup_retries | default(omit) }}" - health_startup_success: "{{ item.health_startup_success | default(omit) }}" + health_startup_cmd: "{{ container_item.health_startup_cmd | default(omit) }}" + health_startup_interval: "{{ container_item.health_startup_interval | default(omit) }}" + health_startup_timeout: "{{ container_item.health_startup_timeout | default(omit) }}" + health_startup_retries: "{{ container_item.health_startup_retries | default(omit) }}" + health_startup_success: "{{ container_item.health_startup_success | default(omit) }}" # Metadata and labels - label: "{{ item.labels | default(omit) }}" - label_file: "{{ item.label_file | default(omit) }}" - annotation: "{{ item.annotations | default(omit) }}" + label: "{{ container_item.labels | default(omit) }}" + label_file: "{{ container_item.label_file | default(omit) }}" + annotation: "{{ container_item.annotations | default(omit) }}" # Container lifecycle - restart_policy: "{{ item.restart_policy | default(omit) }}" - restart_time: "{{ item.restart_time | default(omit) }}" - stop_signal: "{{ item.stop_signal | default(omit) }}" - stop_time: "{{ item.stop_time | default(omit) }}" - stop_timeout: "{{ item.stop_timeout | default(omit) }}" - timeout: "{{ item.timeout | default(omit) }}" + restart_policy: "{{ container_item.restart_policy | default(omit) }}" + restart_time: "{{ container_item.restart_time | default(omit) }}" + stop_signal: "{{ container_item.stop_signal | default(omit) }}" + stop_time: "{{ container_item.stop_time | default(omit) }}" + stop_timeout: "{{ container_item.stop_timeout | default(omit) }}" + timeout: "{{ container_item.timeout | default(omit) }}" # Pull and image options - pull: "{{ item.pull | default(omit) }}" - image_strict: "{{ item.image_strict | bool | default(omit) }}" - arch: "{{ item.arch | default(omit) }}" - os: "{{ item.os | default(omit) }}" - platform: "{{ item.platform | default(omit) }}" - variant: "{{ item.variant | default(omit) }}" + pull: "{{ container_item.pull | default(omit) }}" + image_strict: "{{ container_item.image_strict | bool | default(omit) }}" + arch: "{{ container_item.arch | default(omit) }}" + os: "{{ container_item.os | default(omit) }}" + platform: "{{ container_item.platform | default(omit) }}" + variant: "{{ container_item.variant | default(omit) }}" # Registry and authentication - authfile: "{{ item.authfile | default(omit) }}" - tls_verify: "{{ item.tls_verify | default(omit) }}" - decryption_key: "{{ item.decryption_key | default(omit) }}" + authfile: "{{ container_item.authfile | default(omit) }}" + tls_verify: "{{ container_item.tls_verify | default(omit) }}" + decryption_key: "{{ container_item.decryption_key | default(omit) }}" # File operations - cidfile: "{{ item.cidfile | default(omit) }}" - conmon_pidfile: "{{ item.conmon_pidfile | default(omit) }}" - pid_file: "{{ item.pid_file | default(omit) }}" + cidfile: "{{ container_item.cidfile | default(omit) }}" + conmon_pidfile: "{{ container_item.conmon_pidfile | default(omit) }}" + pid_file: "{{ container_item.pid_file | default(omit) }}" # Special options - attach: "{{ item.attach | default(omit) }}" - detach_keys: "{{ item.detach_keys | default(omit) }}" - sig_proxy: "{{ item.sig_proxy | bool | default(omit) }}" - http_proxy: "{{ item.http_proxy | bool | default(omit) }}" + attach: "{{ container_item.attach | default(omit) }}" + detach_keys: "{{ container_item.detach_keys | default(omit) }}" + sig_proxy: "{{ container_item.sig_proxy | bool | default(omit) }}" + http_proxy: "{{ container_item.http_proxy | bool | default(omit) }}" # Advanced options - chrootdirs: "{{ item.chrootdirs | default(omit) }}" - hooks_dir: "{{ item.hooks_dir | default(omit) }}" - hostuser: "{{ item.hostuser | default(omit) }}" - init_ctr: "{{ item.init_ctr | default(omit) }}" - personality: "{{ item.personality | default(omit) }}" - preserve_fd: "{{ item.preserve_fd | default(omit) }}" - preserve_fds: "{{ item.preserve_fds | default(omit) }}" - rdt_class: "{{ item.rdt_class | default(omit) }}" - requires: "{{ item.requires | default(omit) }}" - rootfs: "{{ item.rootfs | bool | default(omit) }}" - sdnotify: "{{ item.sdnotify | default(omit) }}" - secrets: "{{ item.secrets | default(omit) }}" - timezone: "{{ item.timezone | default(omit) }}" + chrootdirs: "{{ container_item.chrootdirs | default(omit) }}" + hooks_dir: "{{ container_item.hooks_dir | default(omit) }}" + hostuser: "{{ container_item.hostuser | default(omit) }}" + init_ctr: "{{ container_item.init_ctr | default(omit) }}" + personality: "{{ container_item.personality | default(omit) }}" + preserve_fd: "{{ container_item.preserve_fd | default(omit) }}" + preserve_fds: "{{ container_item.preserve_fds | default(omit) }}" + rdt_class: "{{ container_item.rdt_class | default(omit) }}" + requires: "{{ container_item.requires | default(omit) }}" + rootfs: "{{ container_item.rootfs | bool | default(omit) }}" + sdnotify: "{{ container_item.sdnotify | default(omit) }}" + secrets: "{{ container_item.secrets | default(omit) }}" + timezone: "{{ container_item.timezone | default(omit) }}" # Retry options - retry: "{{ item.retry | default(omit) }}" - retry_delay: "{{ item.retry_delay | default(omit) }}" + retry: "{{ container_item.retry | default(omit) }}" + retry_delay: "{{ container_item.retry_delay | default(omit) }}" # Systemd generation - generate_systemd: "{{ item.generate_systemd | default(omit) }}" + generate_systemd: "{{ container_item.generate_systemd | default(omit) }}" # Quadlet options - quadlet_dir: "{{ item.quadlet_dir | default(omit) }}" - quadlet_filename: "{{ item.quadlet_filename | default(omit) }}" - quadlet_file_mode: "{{ item.quadlet_file_mode | default(omit) }}" - quadlet_options: "{{ item.quadlet_options | default(omit) }}" + quadlet_dir: "{{ container_item.quadlet_dir | default(omit) }}" + quadlet_filename: "{{ container_item.quadlet_filename | default(omit) }}" + quadlet_file_mode: "{{ container_item.quadlet_file_mode | default(omit) }}" + quadlet_options: "{{ container_item.quadlet_options | default(omit) }}" # Control options - cmd_args: "{{ item.cmd_args | default(omit) }}" - executable: "{{ item.executable | default('podman') }}" - recreate: "{{ item.recreate | bool | default(omit) }}" - force_restart: "{{ item.force_restart | bool | default(omit) }}" - force_delete: "{{ item.force_delete | bool | default(omit) }}" - delete_depend: "{{ item.delete_depend | bool | default(omit) }}" - delete_time: "{{ item.delete_time | default(omit) }}" - delete_volumes: "{{ item.delete_volumes | bool | default(omit) }}" - debug: "{{ item.debug | bool | default(omit) }}" + cmd_args: "{{ container_item.cmd_args | default(omit) }}" + executable: "{{ container_item.executable | default('podman') }}" + recreate: "{{ container_item.recreate | bool | default(omit) }}" + force_restart: "{{ container_item.force_restart | bool | default(omit) }}" + force_delete: "{{ container_item.force_delete | bool | default(omit) }}" + delete_depend: "{{ container_item.delete_depend | bool | default(omit) }}" + delete_time: "{{ container_item.delete_time | default(omit) }}" + delete_volumes: "{{ container_item.delete_volumes | bool | default(omit) }}" + debug: "{{ container_item.debug | bool | default(omit) }}" loop: "{{ podman_containers }}" loop_control: label: "{{ item.name }}" - register: podman_container_results + register: podman_container_output + notify: + - Reload systemd diff --git a/tasks/host_directories.yml b/tasks/host_directories.yml new file mode 100644 index 0000000..a760d8d --- /dev/null +++ b/tasks/host_directories.yml @@ -0,0 +1,18 @@ +--- +# Ensure volumes are created + +- name: Create bind mount directories from containers + ansible.builtin.file: + path: "{{ item.1.split(':')[0] }}" + state: directory + mode: '0755' + owner: root + group: root + loop: "{{ podman_containers | subelements('volumes', skip_missing=True) }}" + when: + - podman_create_volumes | bool + - item.1 is string + - item.1.split(':')[0] | dirname | length > 0 + - item.1.split(':')[0].startswith('/') or item.1.split(':')[0].startswith('./') + loop_control: + label: "{{ item.0.name }} -> {{ item.1.split(':')[0] }}" diff --git a/tasks/main.yml b/tasks/main.yml index bd561a2..4bf6364 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -26,6 +26,13 @@ - podman - podman-networks +- name: Create volume directories + ansible.builtin.include_tasks: host_directories.yml + when: podman_create_volumes | bool + tags: + - podman + - podman-volumes + - name: Manage Podman volumes ansible.builtin.include_tasks: volumes.yml when: podman_volumes | length > 0 diff --git a/tasks/networks.yml b/tasks/networks.yml index ec42a5c..687fce9 100644 --- a/tasks/networks.yml +++ b/tasks/networks.yml @@ -2,24 +2,33 @@ # Manage Podman networks - name: Manage Podman networks + vars: + network_item: "{{ podman_network_defaults | default({}) | combine(item) }}" containers.podman.podman_network: - name: "{{ item.name }}" - state: "{{ item.state | default('present') }}" - driver: "{{ item.driver | default('bridge') }}" - subnet: "{{ item.subnet | default(omit) }}" - gateway: "{{ item.gateway | default(omit) }}" - ip_range: "{{ item.ip_range | default(omit) }}" - disable_dns: "{{ item.disable_dns | bool | default(omit) }}" - internal: "{{ item.internal | bool | default(omit) }}" - opt: "{{ item.options | default(omit) }}" - dns: "{{ item.dns | default(omit) }}" - interface_name: "{{ item.interface_name | default(omit) }}" - ipam_driver: "{{ item.ipam_driver | default(omit) }}" - ipv6: "{{ item.ipv6 | bool | default(omit) }}" - macvlan: "{{ item.macvlan | default(omit) }}" - net_config: "{{ item.net_config | default(omit) }}" - route: "{{ item.route | default(omit) }}" - recreate: "{{ item.recreate | bool | default(omit) }}" + name: "{{ network_item.name }}" + state: "{{ network_item.state | default('present') }}" + driver: "{{ network_item.driver | default('bridge') }}" + subnet: "{{ network_item.subnet | default(omit) }}" + gateway: "{{ network_item.gateway | default(omit) }}" + ip_range: "{{ network_item.ip_range | default(omit) }}" + disable_dns: "{{ network_item.disable_dns | bool | default(omit) }}" + internal: "{{ network_item.internal | bool | default(omit) }}" + opt: "{{ network_item.options | default(omit) }}" + dns: "{{ network_item.dns | default(omit) }}" + interface_name: "{{ network_item.interface_name | default(omit) }}" + ipam_driver: "{{ network_item.ipam_driver | default(omit) }}" + ipv6: "{{ network_item.ipv6 | bool | default(omit) }}" + macvlan: "{{ network_item.macvlan | default(omit) }}" + net_config: "{{ network_item.net_config | default(omit) }}" + route: "{{ network_item.route | default(omit) }}" + recreate: "{{ network_item.recreate | bool | default(omit) }}" + quadlet_dir: "{{ network_item.quadlet_dir | default(omit) }}" + quadlet_filename: "{{ network_item.quadlet_filename | default(omit) }}" + quadlet_file_mode: "{{ network_item.quadlet_file_mode | default(omit) }}" + quadlet_options: "{{ network_item.quadlet_options | default(omit) }}" loop: "{{ podman_networks }}" loop_control: label: "{{ item.name }}" + register: podman_network_output + notify: + - Reload systemd diff --git a/tasks/pods.yml b/tasks/pods.yml index 107f09d..e39756d 100644 --- a/tasks/pods.yml +++ b/tasks/pods.yml @@ -2,65 +2,70 @@ # Manage Podman pods - name: Manage Podman pods + vars: + pod_item: "{{ podman_pod_defaults | default({}) | combine(item) }}" containers.podman.podman_pod: - name: "{{ item.name }}" - state: "{{ item.state | default('created') }}" - publish: "{{ item.ports | default(omit) }}" - network: "{{ item.networks | default(omit) }}" - volume: "{{ item.volumes | default(omit) }}" - label: "{{ item.labels | default(omit) }}" - hostname: "{{ item.hostname | default(omit) }}" - infra: "{{ item.infra | default(omit) }}" - infra_image: "{{ item.infra_image | default(omit) }}" - infra_command: "{{ item.infra_command | default(omit) }}" - infra_name: "{{ item.infra_name | default(omit) }}" - add_host: "{{ item.add_host | default(omit) }}" - dns: "{{ item.dns | default(omit) }}" - dns_opt: "{{ item.dns_opt | default(omit) }}" - dns_search: "{{ item.dns_search | default(omit) }}" - ip: "{{ item.ip | default(omit) }}" - ip6: "{{ item.ip6 | default(omit) }}" - mac_address: "{{ item.mac_address | default(omit) }}" - no_hosts: "{{ item.no_hosts | default(omit) }}" - share: "{{ item.share | default(omit) }}" - share_parent: "{{ item.share_parent | default(omit) }}" - userns: "{{ item.userns | default(omit) }}" - uidmap: "{{ item.uidmap | default(omit) }}" - gidmap: "{{ item.gidmap | default(omit) }}" - subuidname: "{{ item.subuidname | default(omit) }}" - subgidname: "{{ item.subgidname | default(omit) }}" - security_opt: "{{ item.security_opt | default(omit) }}" - memory: "{{ item.memory | default(omit) }}" - memory_swap: "{{ item.memory_swap | default(omit) }}" - cpu_shares: "{{ item.cpu_shares | default(omit) }}" - cpus: "{{ item.cpus | default(omit) }}" - cpuset_cpus: "{{ item.cpuset_cpus | default(omit) }}" - cpuset_mems: "{{ item.cpuset_mems | default(omit) }}" - blkio_weight: "{{ item.blkio_weight | default(omit) }}" - blkio_weight_device: "{{ item.blkio_weight_device | default(omit) }}" - device: "{{ item.device | default(omit) }}" - device_read_bps: "{{ item.device_read_bps | default(omit) }}" - device_write_bps: "{{ item.device_write_bps | default(omit) }}" - shm_size: "{{ item.shm_size | default(omit) }}" - shm_size_systemd: "{{ item.shm_size_systemd | default(omit) }}" - sysctl: "{{ item.sysctl | default(omit) }}" - cgroup_parent: "{{ item.cgroup_parent | default(omit) }}" - pid: "{{ item.pid | default(omit) }}" - uts: "{{ item.uts | default(omit) }}" - network_alias: "{{ item.network_alias | default(omit) }}" - volumes_from: "{{ item.volumes_from | default(omit) }}" - exit_policy: "{{ item.exit_policy | default(omit) }}" - restart_policy: "{{ item.restart_policy | default(omit) }}" - pod_id_file: "{{ item.pod_id_file | default(omit) }}" - label_file: "{{ item.label_file | default(omit) }}" - gpus: "{{ item.gpus | default(omit) }}" - generate_systemd: "{{ item.generate_systemd | default(omit) }}" - quadlet_dir: "{{ item.quadlet_dir | default(omit) }}" - quadlet_filename: "{{ item.quadlet_filename | default(omit) }}" - quadlet_file_mode: "{{ item.quadlet_file_mode | default(omit) }}" - quadlet_options: "{{ item.quadlet_options | default(omit) }}" - recreate: "{{ item.recreate | default(omit) }}" - debug: "{{ item.debug | default(omit) }}" + name: "{{ pod_item.name }}" + state: "{{ pod_item.state | default('created') }}" + publish: "{{ pod_item.ports | default(omit) }}" + network: "{{ pod_item.networks | default(omit) }}" + volume: "{{ pod_item.volumes | default(omit) }}" + label: "{{ pod_item.labels | default(omit) }}" + hostname: "{{ pod_item.hostname | default(omit) }}" + infra: "{{ pod_item.infra | default(omit) }}" + infra_image: "{{ pod_item.infra_image | default(omit) }}" + infra_command: "{{ pod_item.infra_command | default(omit) }}" + infra_name: "{{ pod_item.infra_name | default(omit) }}" + add_host: "{{ pod_item.add_host | default(omit) }}" + dns: "{{ pod_item.dns | default(omit) }}" + dns_opt: "{{ pod_item.dns_opt | default(omit) }}" + dns_search: "{{ pod_item.dns_search | default(omit) }}" + ip: "{{ pod_item.ip | default(omit) }}" + ip6: "{{ pod_item.ip6 | default(omit) }}" + mac_address: "{{ pod_item.mac_address | default(omit) }}" + no_hosts: "{{ pod_item.no_hosts | default(omit) }}" + share: "{{ pod_item.share | default(omit) }}" + share_parent: "{{ pod_item.share_parent | default(omit) }}" + userns: "{{ pod_item.userns | default(omit) }}" + uidmap: "{{ pod_item.uidmap | default(omit) }}" + gidmap: "{{ pod_item.gidmap | default(omit) }}" + subuidname: "{{ pod_item.subuidname | default(omit) }}" + subgidname: "{{ pod_item.subgidname | default(omit) }}" + security_opt: "{{ pod_item.security_opt | default(omit) }}" + memory: "{{ pod_item.memory | default(omit) }}" + memory_swap: "{{ pod_item.memory_swap | default(omit) }}" + cpu_shares: "{{ pod_item.cpu_shares | default(omit) }}" + cpus: "{{ pod_item.cpus | default(omit) }}" + cpuset_cpus: "{{ pod_item.cpuset_cpus | default(omit) }}" + cpuset_mems: "{{ pod_item.cpuset_mems | default(omit) }}" + blkio_weight: "{{ pod_item.blkio_weight | default(omit) }}" + blkio_weight_device: "{{ pod_item.blkio_weight_device | default(omit) }}" + device: "{{ pod_item.device | default(omit) }}" + device_read_bps: "{{ pod_item.device_read_bps | default(omit) }}" + device_write_bps: "{{ pod_item.device_write_bps | default(omit) }}" + shm_size: "{{ pod_item.shm_size | default(omit) }}" + shm_size_systemd: "{{ pod_item.shm_size_systemd | default(omit) }}" + sysctl: "{{ pod_item.sysctl | default(omit) }}" + cgroup_parent: "{{ pod_item.cgroup_parent | default(omit) }}" + pid: "{{ pod_item.pid | default(omit) }}" + uts: "{{ pod_item.uts | default(omit) }}" + network_alias: "{{ pod_item.network_alias | default(omit) }}" + volumes_from: "{{ pod_item.volumes_from | default(omit) }}" + exit_policy: "{{ pod_item.exit_policy | default(omit) }}" + restart_policy: "{{ pod_item.restart_policy | default(omit) }}" + pod_id_file: "{{ pod_item.pod_id_file | default(omit) }}" + label_file: "{{ pod_item.label_file | default(omit) }}" + gpus: "{{ pod_item.gpus | default(omit) }}" + generate_systemd: "{{ pod_item.generate_systemd | default(omit) }}" + quadlet_dir: "{{ pod_item.quadlet_dir | default(omit) }}" + quadlet_filename: "{{ pod_item.quadlet_filename | default(omit) }}" + quadlet_file_mode: "{{ pod_item.quadlet_file_mode | default(omit) }}" + quadlet_options: "{{ pod_item.quadlet_options | default(omit) }}" + recreate: "{{ pod_item.recreate | default(omit) }}" + debug: "{{ pod_item.debug | default(omit) }}" loop: "{{ podman_pods }}" loop_control: label: "{{ item.name }}" + register: podman_pod_output + notify: + - Reload systemd diff --git a/tasks/volumes.yml b/tasks/volumes.yml index 656343c..b50745b 100644 --- a/tasks/volumes.yml +++ b/tasks/volumes.yml @@ -2,18 +2,23 @@ # Manage Podman volumes - name: Manage Podman volumes + vars: + volume_item: "{{ podman_volume_defaults | default({}) | combine(item) }}" containers.podman.podman_volume: - name: "{{ item.name }}" - state: "{{ item.state | default('present') }}" - driver: "{{ item.driver | default('local') }}" - options: "{{ item.options | default(omit) }}" - label: "{{ item.labels | default(omit) }}" - debug: "{{ item.debug | bool | default(omit) }}" - recreate: "{{ item.recreate | bool | default(omit) }}" - quadlet_dir: "{{ item.quadlet_dir | default(omit) }}" - quadlet_filename: "{{ item.quadlet_filename | default(omit) }}" - quadlet_file_mode: "{{ item.quadlet_file_mode | default(omit) }}" - quadlet_options: "{{ item.quadlet_options | default(omit) }}" + name: "{{ volume_item.name }}" + state: "{{ volume_item.state | default('present') }}" + driver: "{{ volume_item.driver | default('local') }}" + options: "{{ volume_item.options | default(omit) }}" + label: "{{ volume_item.labels | default(omit) }}" + debug: "{{ volume_item.debug | bool | default(omit) }}" + recreate: "{{ volume_item.recreate | bool | default(omit) }}" + quadlet_dir: "{{ volume_item.quadlet_dir | default(omit) }}" + quadlet_filename: "{{ volume_item.quadlet_filename | default(omit) }}" + quadlet_file_mode: "{{ volume_item.quadlet_file_mode | default(omit) }}" + quadlet_options: "{{ volume_item.quadlet_options | default(omit) }}" loop: "{{ podman_volumes }}" loop_control: label: "{{ item.name }}" + register: podman_volume_output + notify: + - Reload systemd From 6177112df835645c95de38ca6eb15c2a6d690f81 Mon Sep 17 00:00:00 2001 From: Daniel Akulenok Date: Wed, 21 Jan 2026 10:11:18 +0100 Subject: [PATCH 14/17] Update with latest out of band changes --- README.md | 28 ++++++++++++++++------ defaults/main.yml | 3 +-- handlers/main.yml | 48 +++++++++++++++++++------------------- tasks/host_directories.yml | 18 -------------- tasks/main.yml | 7 ------ tasks/services.yml | 15 ++++++++++++ 6 files changed, 61 insertions(+), 58 deletions(-) delete mode 100644 tasks/host_directories.yml diff --git a/README.md b/README.md index 97978bd..d817d1e 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ ## 🚀 Quick Start ### 1. Basic Setup + ```yaml - hosts: servers roles: @@ -38,7 +39,7 @@ podman_containers: env: DATABASE_URL: "postgresql://postgres@db:5432/app" - - name: database + - name: postgres image: postgres:15 volumes: - "db-data:/var/lib/postgresql/data" @@ -134,9 +135,6 @@ podman_networks: #### Volumes ```yaml -# Toggle automatic creation of host directories for bind mounts -podman_create_volumes: true - podman_volumes: - name: db_data # state defaults to 'quadlet' @@ -174,6 +172,15 @@ podman_policy_trusted_registries: insecure: false mirror: - location: "backup.company.com" + +# Additional registries (for special mirror/proxy configurations) +podman_registries_additional: + - location: "internal-mirror.company.com" + insecure: false + blocked: false + mirror: + - location: "docker.io" + insecure: false ``` #### Systemd Service Generation @@ -190,6 +197,13 @@ podman_systemd_options: pod_prefix: "pod-" ``` +#### Container Defaults + +```yaml +# Auto-remove containers when they exit (applies to all containers unless overridden) +podman_auto_remove: true +``` + #### Resource Cleanup ```yaml @@ -217,8 +231,11 @@ podman_storage_runroot: /run/containers/storage ```yaml podman_enable_socket: true # Enable Podman socket podman_enable_api_service: true # Enable REST API +podman_enable_auto_update: true # Enable automatic container updates ``` +> **Note:** When using `podman_enable_auto_update`, containers must use **fully qualified image names** including the registry (e.g., `docker.io/postgres:15` instead of `postgres:15`) and should have `AutoUpdate=registry` in their `quadlet_options` or be configured with Quadlet state. + --- ## 🏷️ Available Tags @@ -352,9 +369,6 @@ ansible-playbook -t podman-networks playbook.yml env: DATABASE_URL: "postgresql://postgres:{{ vault_db_password }}@postgres:5432/myapp" REDIS_URL: "redis://redis:6379" - depends_on: - - postgres - - redis ``` --- diff --git a/defaults/main.yml b/defaults/main.yml index 7bdd496..35c78cd 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -165,6 +165,7 @@ podman_policy_trusted_registries: # Service management podman_enable_socket: false podman_enable_api_service: false +podman_enable_auto_update: false # Determine if Quadlet should be used (Debian 13+ or other distros) podman_use_quadlet: "{{ not (ansible_distribution | default('Debian') == 'Debian' and ansible_distribution_major_version | default('13') | int < 13) }}" @@ -526,7 +527,6 @@ podman_networks: [] # - "10.10.0.0/16,192.168.1.1" # Volume management -podman_create_volumes: true podman_volume_defaults: state: "{{ 'quadlet' if podman_use_quadlet else 'present' }}" quadlet_options: @@ -656,7 +656,6 @@ podman_systemd_options: requires: [] container_prefix: "container-" pod_prefix: "pod-" - restart_sec: 30 ### Prune Configuration podman_prune_enabled: true diff --git a/handlers/main.yml b/handlers/main.yml index 5e3dc2c..7595888 100644 --- a/handlers/main.yml +++ b/handlers/main.yml @@ -11,30 +11,6 @@ ansible.builtin.systemd: daemon_reload: true -- name: Restart Podman pods - ansible.builtin.systemd: - name: "{{ (podman_systemd_options.pod_prefix if podman_generate_systemd | bool else '') ~ item ~ ('-pod' if not podman_generate_systemd | bool else '') }}" - state: restarted - listen: Reload systemd - loop: "{{ podman_pod_output.results | selectattr('changed', 'equalto', true) | map(attribute='item.name') | list }}" - loop_control: - label: "{{ item }}" - when: - - podman_pod_output is defined - - podman_pod_output.results | length > 0 - -- name: Restart Podman containers - ansible.builtin.systemd: - name: "{{ (podman_systemd_options.container_prefix if podman_generate_systemd | bool else '') ~ item }}" - state: restarted - listen: Reload systemd - loop: "{{ podman_container_output.results | selectattr('changed', 'equalto', true) | map(attribute='item.name') | list }}" - loop_control: - label: "{{ item }}" - when: - - podman_container_output is defined - - podman_container_output.results | length > 0 - - name: Restart Podman networks ansible.builtin.systemd: name: "{{ item }}-network" @@ -60,3 +36,27 @@ - podman_volume_output is defined - podman_volume_output.results | length > 0 - not podman_generate_systemd | bool + +- name: Restart Podman pods + ansible.builtin.systemd: + name: "{{ (podman_systemd_options.pod_prefix if podman_generate_systemd | bool else '') ~ item ~ ('-pod' if not podman_generate_systemd | bool else '') }}" + state: restarted + listen: Reload systemd + loop: "{{ podman_pod_output.results | selectattr('changed', 'equalto', true) | map(attribute='item.name') | list }}" + loop_control: + label: "{{ item }}" + when: + - podman_pod_output is defined + - podman_pod_output.results | length > 0 + +- name: Restart Podman containers + ansible.builtin.systemd: + name: "{{ (podman_systemd_options.container_prefix if podman_generate_systemd | bool else '') ~ item }}" + state: restarted + listen: Reload systemd + loop: "{{ podman_container_output.results | selectattr('changed', 'equalto', true) | map(attribute='item.name') | list }}" + loop_control: + label: "{{ item }}" + when: + - podman_container_output is defined + - podman_container_output.results | length > 0 diff --git a/tasks/host_directories.yml b/tasks/host_directories.yml deleted file mode 100644 index a760d8d..0000000 --- a/tasks/host_directories.yml +++ /dev/null @@ -1,18 +0,0 @@ ---- -# Ensure volumes are created - -- name: Create bind mount directories from containers - ansible.builtin.file: - path: "{{ item.1.split(':')[0] }}" - state: directory - mode: '0755' - owner: root - group: root - loop: "{{ podman_containers | subelements('volumes', skip_missing=True) }}" - when: - - podman_create_volumes | bool - - item.1 is string - - item.1.split(':')[0] | dirname | length > 0 - - item.1.split(':')[0].startswith('/') or item.1.split(':')[0].startswith('./') - loop_control: - label: "{{ item.0.name }} -> {{ item.1.split(':')[0] }}" diff --git a/tasks/main.yml b/tasks/main.yml index 4bf6364..bd561a2 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -26,13 +26,6 @@ - podman - podman-networks -- name: Create volume directories - ansible.builtin.include_tasks: host_directories.yml - when: podman_create_volumes | bool - tags: - - podman - - podman-volumes - - name: Manage Podman volumes ansible.builtin.include_tasks: volumes.yml when: podman_volumes | length > 0 diff --git a/tasks/services.yml b/tasks/services.yml index 13ff076..3f9caee 100644 --- a/tasks/services.yml +++ b/tasks/services.yml @@ -30,3 +30,18 @@ enabled: false state: stopped when: not podman_enable_api_service + +- name: Enable and start Podman auto-update service + ansible.builtin.systemd: + name: podman-auto-update.service + enabled: true + state: started + daemon_reload: true + when: podman_enable_auto_update + +- name: Disable Podman auto-update service + ansible.builtin.systemd: + name: podman-auto-update.service + enabled: false + state: stopped + when: not podman_enable_auto_update From a30de11a4af589fac3ba8cf2d901d42d7ab2833e Mon Sep 17 00:00:00 2001 From: Daniel Akulenok Date: Wed, 21 Jan 2026 12:26:35 +0100 Subject: [PATCH 15/17] Molecule is broken --- molecule/default/converge.yml | 13 ------- molecule/default/molecule.yml | 30 ---------------- molecule/default/prepare.yml | 28 --------------- molecule/default/requirements.yml | 3 -- .../__pycache__/test_default.cpython-313.pyc | Bin 2463 -> 0 bytes molecule/default/tests/test_default.py | 34 ------------------ 6 files changed, 108 deletions(-) delete mode 100644 molecule/default/converge.yml delete mode 100644 molecule/default/molecule.yml delete mode 100644 molecule/default/prepare.yml delete mode 100644 molecule/default/requirements.yml delete mode 100644 molecule/default/tests/__pycache__/test_default.cpython-313.pyc delete mode 100644 molecule/default/tests/test_default.py diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml deleted file mode 100644 index cd892c6..0000000 --- a/molecule/default/converge.yml +++ /dev/null @@ -1,13 +0,0 @@ ---- -- name: Converge - hosts: all - vars: - podman_packages: - - podman - podman_configure_registries: true - podman_configure_storage: true - podman_configure_policy: true - podman_enable_socket: false - podman_enable_api_service: false - podman_policy_default_type: "insecureAcceptAnything" - podman_policy_trusted_registries: [] diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml deleted file mode 100644 index 9efaac2..0000000 --- a/molecule/default/molecule.yml +++ /dev/null @@ -1,30 +0,0 @@ ---- -dependency: - name: galaxy -driver: - name: podman -platform_defaults: &platform_defaults - tmpfs: - - /tmp - - /run/lock - volumes: - - /sys/fs/cgroup:/sys/fs/cgroup:ro - command: /lib/systemd/systemd -platforms: - - name: debian-bullseye - image: docker.io/jrei/systemd-debian:12 - <<: *platform_defaults - - name: debian-trixie - image: docker.io/jrei/systemd-debian:13 - <<: *platform_defaults - -provisioner: - name: ansible - lint: - name: ansible-lint -verifier: - name: ansible -lint: | - set -e - ansible-lint . - yamllint . diff --git a/molecule/default/prepare.yml b/molecule/default/prepare.yml deleted file mode 100644 index af9388d..0000000 --- a/molecule/default/prepare.yml +++ /dev/null @@ -1,28 +0,0 @@ ---- -- name: Prepare instance for podman role testing - hosts: all - become: true - tasks: - - name: Ensure apt cache is up-to-date - ansible.builtin.apt: - update_cache: true - cache_valid_time: 3600 - - - name: Install prerequisites used by the role/tests - ansible.builtin.apt: - name: - - apt-transport-https - - ca-certificates - - gnupg - - lsb-release - - curl - state: present - update_cache: false - - - name: Install python3-venv and python3-pip for testinfra - ansible.builtin.apt: - name: - - python3-pip - - python3-venv - state: present - update_cache: false diff --git a/molecule/default/requirements.yml b/molecule/default/requirements.yml deleted file mode 100644 index 25a97a4..0000000 --- a/molecule/default/requirements.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -collections: - - name: containers.podman diff --git a/molecule/default/tests/__pycache__/test_default.cpython-313.pyc b/molecule/default/tests/__pycache__/test_default.cpython-313.pyc deleted file mode 100644 index ae22792d8e160f336dabb8c58732ec26e7d56f40..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2463 zcmc&$%}*pn6tDhZm=C^o5J8Z&#UM<=&?M-FzzX7sk&hj+GqTZSlTI^T3=K2gld2v9 zbAr=K_Q0CBQR6|c-1OvbV&XsGp-CSgy+;qmlSFQweAOS!3?Z28K|Ap3)vH%uzgO?~ z=Ayklj^O+A@$*enMCdox3BQ&=x%d^7rznr|+%h75%Mtz|pNoX9^)?r+>!FrQgj&6Q zh;nW9UZ~|dL#^KC`FAAxO_N%#P}we0b_hvw@5`HJ8OxfwEoV#(%c?>3^%72R zV_eZKnV2O^Wq(t-44p!?1m1W7)K7C@+tCo}c~{ zA#KFNT!6^1b+3HyahI`ylS>G^u6f-ZXtFtGVz$;5#44TM!G!9jK^W$>p1^FFmPD$C zp#t2~Qc83M=(#*8xDje;m=Lcut5%99F_`EIg|Zfejj+UV2O5E1*A12IDR>t`1a|TY zc(A0QcR~0tj~^D`{<@_8L*tlEWGzFw2nkoiCz8eFRvVGdXu zc=zg8uN(%y#$Q*z0{-whTWsR&mMj^B1^tv?uIyQxrZFj{3(|;Qu9(D<7;s~BkIpb< zykx4{s9C|r=+F((8A_!_YEnZi!*Bur@2DkRla}(U*;EVZWn1@wa7C)uFkwVM01T)l z9+!}QX6Ob%Su#tKskb#Jqi=*9+7$E%iJqT@kfQW#}o z;>Iq=-4@Tm?YQF6KJS;ZoIjxz8#_8N4Ob{)%Z-6hSTUq-=jBSi!7ET6eR}y$*j>F# zAHy#NrhD8Qy<0XlsD-m64K`Nu={ksWf#bLrDE0ykpGQ#l>tD%V$fwDPljOu{a`q%S zdnQI6EgdX9${u9x?lDJ9UkH3R|2v;JNPWKG@X7NQ#C81@=eZ9!CYL?=p(o$xF5)8R G)A2uvVje31 diff --git a/molecule/default/tests/test_default.py b/molecule/default/tests/test_default.py deleted file mode 100644 index f6e1f3f..0000000 --- a/molecule/default/tests/test_default.py +++ /dev/null @@ -1,34 +0,0 @@ -import os -import testinfra.utils.ansible_runner - -# Get hosts from Testinfra inventory created by Molecule -testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( - os.environ.get('MOLECULE_INVENTORY_FILE') -).get_hosts('all') - - -def test_podman_package_installed(host): - pkg = host.package("podman") - assert pkg.is_installed, "podman package should be installed" - - -def test_podman_binary_executes(host): - cmd = host.run("podman --version") - assert cmd.rc == 0, f"podman not runnable: {cmd.stderr or cmd.stdout}" - - -def test_containers_conf_exists(host): - f = host.file("/etc/containers/registries.conf") - assert f.exists, "/etc/containers/registries.conf should exist" - - -def test_policy_json_exists(host): - f = host.file("/etc/containers/policy.json") - assert f.exists, "/etc/containers/policy.json should exist" - - -def test_podman_config_file_valid_json(host): - f = host.file("/etc/containers/policy.json") - assert f.exists and f.size > 0 - cmd = host.run("python3 -c 'import json,sys;json.load(open(\"/etc/containers/policy.json\"))'") - assert cmd.rc == 0, "policy.json should be valid JSON" From 1845afdc9be12c0ae32b516dc8d5b6ecebb02569 Mon Sep 17 00:00:00 2001 From: Daniel Akulenok Date: Sat, 24 Jan 2026 21:53:58 +0100 Subject: [PATCH 16/17] Remove unneccesary requirement --- defaults/main.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/defaults/main.yml b/defaults/main.yml index 35c78cd..8d38179 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -178,9 +178,6 @@ podman_container_defaults: quadlet_options: - "AutoUpdate=registry" - "Pull=newer" - - | - [Install] - WantedBy=default.target podman_containers: [] # Example container configurations: From 8e334ad76f23c53f3efa89febf965e3fc97cabaa Mon Sep 17 00:00:00 2001 From: Daniel Akulenok Date: Fri, 6 Feb 2026 13:38:49 +0100 Subject: [PATCH 17/17] Refactor YAML formatting for consistency in podman configuration and systemd tasks --- defaults/main.yml | 22 +++++++++++----------- tasks/systemd.yml | 8 ++------ 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/defaults/main.yml b/defaults/main.yml index 8d38179..714a3c0 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -50,7 +50,7 @@ podman_policy_trusted_registries: # Registry configuration insecure: false blocked: false - unqualified_search: false # Don't search library namespace for unqualified images + unqualified_search: false # Don't search library namespace for unqualified images # Docker Hub (all namespaces) - use with caution in production - registry: "docker.io" @@ -58,7 +58,7 @@ podman_policy_trusted_registries: # Registry configuration insecure: false blocked: false - unqualified_search: true # Primary search registry + unqualified_search: true # Primary search registry # Red Hat's official registry - registry: "registry.redhat.io" @@ -101,7 +101,7 @@ podman_policy_trusted_registries: # Registry configuration insecure: false blocked: false - unqualified_search: false # Not commonly used for unqualified searches + unqualified_search: false # Not commonly used for unqualified searches # GitHub Container Registry - registry: "ghcr.io" @@ -109,7 +109,7 @@ podman_policy_trusted_registries: # Registry configuration insecure: false blocked: false - unqualified_search: false # Not commonly used for unqualified searches + unqualified_search: false # Not commonly used for unqualified searches # GitLab Container Registry - registry: "registry.gitlab.com" @@ -117,7 +117,7 @@ podman_policy_trusted_registries: # Registry configuration insecure: false blocked: false - unqualified_search: false # Not commonly used for unqualified searches + unqualified_search: false # Not commonly used for unqualified searches # Example production configuration with signature verification: # podman_policy_default_type: "reject" @@ -657,9 +657,9 @@ podman_systemd_options: ### Prune Configuration podman_prune_enabled: true podman_prune_options: - container: true # Remove stopped containers - image: true # Remove unused images - network: true # Remove unused networks - system: true # Prune all unused data - system_all: true # Prune all unused data including build cache - volume: true # Remove unused volumes + container: true # Remove stopped containers + image: true # Remove unused images + network: true # Remove unused networks + system: true # Prune all unused data + system_all: true # Prune all unused data including build cache + volume: true # Remove unused volumes diff --git a/tasks/systemd.yml b/tasks/systemd.yml index 4288ea2..7feae91 100644 --- a/tasks/systemd.yml +++ b/tasks/systemd.yml @@ -3,9 +3,7 @@ - name: Generate systemd service files for containers vars: - systemd_opts: "{{ - item.systemd if item.systemd is defined - else (item.generate_systemd if item.generate_systemd is defined else {}) }}" + systemd_opts: "{{ item.systemd if item.systemd is defined else (item.generate_systemd if item.generate_systemd is defined else {}) }}" containers.podman.podman_generate_systemd: name: "{{ item.name }}" dest: "{{ podman_systemd_dir }}" @@ -32,9 +30,7 @@ - name: Generate systemd service files for pods vars: - systemd_opts: "{{ - item.systemd if item.systemd is defined - else (item.generate_systemd if item.generate_systemd is defined else {}) }}" + systemd_opts: "{{ item.systemd if item.systemd is defined else (item.generate_systemd if item.generate_systemd is defined else {}) }}" containers.podman.podman_generate_systemd: name: "{{ item.name }}" dest: "{{ podman_systemd_dir }}"