This commit is contained in:
Daniel Akulenok
2025-09-05 22:49:16 +02:00
commit 9cfd12e745
23 changed files with 1646 additions and 0 deletions

273
README.md Normal file
View File

@@ -0,0 +1,273 @@
Podman
======
This Ansible role installs and configures Podman container runtime, and provides comprehensive container, pod, network, and volume management capabilities.
Requirements
------------
- Ansible 2.11 or higher
- Target systems: Ubuntu 20.04+, Debian 11+
- containers.podman collection (for container management tasks)
Role Variables
--------------
### Installation Variables
* `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
### 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
```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
```
### Service Variables
* `podman_enable_socket`: Enable Podman socket service (default: `false`)
* `podman_enable_api_service`: Enable Podman API service (default: `false`)
### Container Management Variables
```yaml
podman_containers:
- name: nginx
image: docker.io/nginx:latest
state: started
ports:
- "8080:80"
volumes:
- "/etc/nginx/conf.d:/etc/nginx/conf.d:ro"
env:
NGINX_HOST: example.com
restart_policy: always
user: nginx
networks:
- podman
labels:
app: webserver
version: "1.0"
```
### Network Management Variables
```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
dns:
- "8.8.8.8"
options:
mtu: 1500
vlan: 100
ipam_driver: "host-local"
interface_name: "podman1"
route:
- "10.10.0.0/16,192.168.1.1"
- name: macvlan-net
driver: macvlan
macvlan: "eth0"
subnet: "192.168.1.0/24"
- name: ipv6-net
driver: bridge
subnet: "fd00::/64"
ipv6: true
recreate: false
```
### Volume Management Variables
```yaml
podman_volumes:
- name: app-data
state: present
driver: local
labels:
environment: production
backup: daily
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
```yaml
podman_pods:
- name: webapp-pod
state: started
ports:
- "8080:80"
- "3306:3306"
networks:
- frontend
hostname: webapp
dns:
- "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"
```
Dependencies
------------
* `containers.podman` collection for container management tasks
Example Playbook
----------------
```yaml
- hosts: servers
roles:
- role: keepit.podman
vars:
podman_enable_socket: true
podman_containers:
- name: web-server
image: nginx:latest
state: started
ports:
- "80:80"
volumes:
- "/var/www/html:/usr/share/nginx/html:ro"
podman_networks:
- name: web-network
subnet: "172.20.0.0/16"
```
Tags
----
* `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
License
-------
MIT
Author Information
------------------
Daniel Akulenok <dak@keepit.com>
Keepit A/S

603
defaults/main.yml Normal file
View File

@@ -0,0 +1,603 @@
---
# defaults file for podman
# Package installation
podman_install_from_repo: true
podman_packages:
- podman
- crun
# Podman configuration
podman_configure_registries: true
podman_registries_conf_path: /etc/containers/registries.conf
# Additional registries not included in policy configuration
# Use this for registries that need special mirror or proxy configuration
podman_registries_additional: []
# Example:
# podman_registries_additional:
# - location: "internal-mirror.company.com"
# insecure: false
# blocked: false
# mirror:
# - location: "docker.io"
# insecure: false
# Storage configuration
podman_configure_storage: true
podman_storage_conf_path: /etc/containers/storage.conf
podman_storage_driver: overlay
podman_storage_runroot: /run/containers/storage
podman_storage_graphroot: /var/lib/containers/storage
# Policy configuration
podman_configure_policy: true
podman_policy_path: /etc/containers/policy.json
# Image signature policy settings
# Default policy type: "insecureAcceptAnything" for development, "reject" for production
podman_policy_default_type: "insecureAcceptAnything"
# Whether to reject images from unknown/unlisted registries
podman_policy_reject_unknown_registries: false
# Trusted registries configuration
# Each registry can have different trust levels and signature requirements
# Plus registries.conf configuration options
podman_policy_trusted_registries:
# Docker Hub official images (library namespace)
- registry: "docker.io/library"
type: "insecureAcceptAnything"
# Registry configuration
insecure: false
blocked: false
unqualified_search: false # Don't search library namespace for unqualified images
# Docker Hub (all namespaces) - use with caution in production
- registry: "docker.io"
type: "insecureAcceptAnything"
# Registry configuration
insecure: false
blocked: false
unqualified_search: true # Primary search registry
# Red Hat's official registry
- registry: "registry.redhat.io"
type: "insecureAcceptAnything"
# For production with signature verification:
# type: "signedBy"
# keyPath: "/etc/pki/containers/redhat.gpg"
# Registry configuration
insecure: false
blocked: false
unqualified_search: true
# Quay.io - Red Hat's container registry
- registry: "quay.io"
type: "insecureAcceptAnything"
# Registry configuration
insecure: false
blocked: false
unqualified_search: true
# Google Container Registry
- registry: "gcr.io"
type: "insecureAcceptAnything"
# Registry configuration
insecure: false
blocked: false
unqualified_search: true
# Microsoft Container Registry
- registry: "mcr.microsoft.com"
type: "insecureAcceptAnything"
# Registry configuration
insecure: false
blocked: false
unqualified_search: true
# Amazon ECR Public
- registry: "public.ecr.aws"
type: "insecureAcceptAnything"
# Registry configuration
insecure: false
blocked: false
unqualified_search: false # Not commonly used for unqualified searches
# GitHub Container Registry
- registry: "ghcr.io"
type: "insecureAcceptAnything"
# Registry configuration
insecure: false
blocked: false
unqualified_search: false # Not commonly used for unqualified searches
# GitLab Container Registry
- registry: "registry.gitlab.com"
type: "insecureAcceptAnything"
# Registry configuration
insecure: false
blocked: false
unqualified_search: false # Not commonly used for unqualified searches
# Example production configuration with signature verification:
# podman_policy_default_type: "reject"
# podman_policy_reject_unknown_registries: true
# podman_policy_trusted_registries:
# # Internal company registry with signature verification
# - registry: "internal-registry.company.com"
# type: "signedBy"
# keyPath: "/etc/pki/containers/company.gpg"
# # Registry configuration
# insecure: false
# blocked: false
# unqualified_search: true
# # Optional: mirror configuration
# mirror:
# - location: "backup-registry.company.com"
# insecure: false
#
# # Red Hat registry with signature verification
# - registry: "registry.redhat.io"
# type: "signedBy"
# keyPath: "/etc/pki/containers/redhat.gpg"
# # Registry configuration
# insecure: false
# blocked: false
# unqualified_search: true
#
# # Docker Hub official images only (more restrictive)
# - registry: "docker.io/library"
# type: "insecureAcceptAnything"
# # Registry configuration
# insecure: false
# blocked: false
# unqualified_search: false
#
# # Specific trusted namespaces
# - registry: "quay.io/company"
# type: "signedBy"
# keyPath: "/etc/pki/containers/company.gpg"
# # Registry configuration
# insecure: false
# blocked: false
# unqualified_search: false
# Service management
podman_enable_socket: false
podman_enable_api_service: false
# Container management
podman_containers: []
# Example container configurations:
# podman_containers:
# # Basic web server container
# - name: nginx
# image: docker.io/nginx:latest
# state: started
# ports:
# - "8080:80"
# volumes:
# - "/etc/nginx/conf.d:/etc/nginx/conf.d:ro"
# env:
# NGINX_HOST: example.com
# restart_policy: always
# user: nginx
# networks:
# - podman
# labels:
# app: webserver
# version: "1.0"
# working_dir: /app
#
# # 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"
# user: "1000:1000"
# working_dir: /app
# hostname: app-server
# detach: true
# interactive: false
# tty: false
# auto_remove: false
# privileged: false
# read_only: false
# read_only_tmpfs: true
# init: true
#
# # Networking configuration
# networks:
# - frontend
# - backend
# ports:
# - "8080:8080"
# - "127.0.0.1:8081:8081/tcp"
# publish_all: false
# expose:
# - "9000"
# ip: "10.88.0.100"
# mac_address: "02:42:ac:11:00:02"
# network_aliases:
# - app
# - api
# dns:
# - "8.8.8.8"
# - "1.1.1.1"
# dns_options: "ndots:2"
# dns_search:
# - "example.com"
# - "local"
# add_hosts:
# database: "192.168.1.100"
# cache: "192.168.1.101"
# no_hosts: false
#
# # Storage and volumes
# volumes:
# - "/data/app:/app/data:rw"
# - "app-config:/etc/app:ro"
# volumes_from:
# - data-container
# mounts:
# - "type=bind,source=/host/path,destination=/container/path,ro"
# tmpfs:
# /tmp: "rw,size=100m,mode=1777"
# /var/cache: "rw,size=50m"
#
# # Environment variables
# env:
# DATABASE_URL: "postgresql://user:pass@db:5432/myapp"
# REDIS_URL: "redis://cache:6379/0"
# LOG_LEVEL: "info"
# FEATURE_FLAGS: "new_ui,api_v2"
# env_files:
# - "/etc/app/.env"
# env_host: false
# env_merge:
# PATH: "/app/bin:$PATH"
# unsetenv:
# - "TEMP_VAR"
#
# # Resource constraints
# memory: "2g"
# memory_reservation: "1g"
# memory_swap: "4g"
# memory_swappiness: 60
# cpus: "1.5"
# cpu_shares: 1024
# cpu_period: 100000
# cpu_quota: 150000
# cpuset_cpus: "0-1"
# cpuset_mems: "0"
# blkio_weight: 500
# blkio_weight_device:
# "/dev/sda": 600
# oom_kill_disable: false
# oom_score_adj: 500
# pids_limit: "1000"
#
# # Device access
# devices:
# - "/dev/nvidia0:/dev/nvidia0:rwm"
# - "/dev/sda:/dev/xvda:ro"
# device_read_bps:
# - "/dev/sda:1mb"
# device_write_bps:
# - "/dev/sda:1mb"
# device_read_iops:
# - "/dev/sda:1000"
# device_write_iops:
# - "/dev/sda:1000"
# gpus: "all"
#
# # Security configuration
# security_opt:
# - "seccomp=unconfined"
# - "label=type:container_runtime_t"
# cap_add:
# - "NET_ADMIN"
# - "SYS_TIME"
# cap_drop:
# - "MKNOD"
# - "AUDIT_WRITE"
# userns: "host"
# uidmap:
# - "0:1000:1000"
# gidmap:
# - "0:1000:1000"
# subuidname: "myuser"
# subgidname: "myuser"
# groups:
# - "docker"
# - "audio"
#
# # Namespaces
# ipc_mode: "container:other-container"
# pid_mode: "host"
# uts: "host"
# cgroupns: "host"
#
# # Cgroups
# cgroups: "enabled"
# cgroup_parent: "/system.slice"
# cgroup_conf:
# "memory.swappiness": "10"
#
# # System configuration
# sysctl:
# net.core.somaxconn: "1024"
# kernel.shm_rmid_forced: "1"
# systemd: "true"
# ulimits:
# - "nofile=65536:65536"
# - "nproc=4096:4096"
# umask: "0027"
#
# # Shared memory
# shm_size: "128m"
# shm_size_systemd: "64m"
#
# # Pod integration
# pod: "app-pod"
#
# # Logging
# log_driver: "journald"
# log_options:
# max_size: "10mb"
# max_files: "3"
# tag: "app-server"
# log_level: "info"
#
# # Health checks
# healthcheck: "curl -f http://localhost:8080/health || exit 1"
# healthcheck_interval: "30s"
# healthcheck_timeout: "10s"
# healthcheck_start_period: "60s"
# healthcheck_retries: 3
# healthcheck_failure_action: "restart"
# no_healthcheck: false
#
# # Startup health checks
# health_startup_cmd: "curl -f http://localhost:8080/ready || exit 1"
# health_startup_interval: "5s"
# health_startup_timeout: "3s"
# health_startup_retries: 20
# health_startup_success: 1
#
# # Metadata
# labels:
# app: "myapp"
# version: "1.2.3"
# environment: "production"
# maintainer: "team@example.com"
# annotations:
# "org.opencontainers.image.source": "https://github.com/example/myapp"
#
# # Container lifecycle
# restart_policy: "on-failure:3"
# restart_time: "10s"
# stop_signal: 15
# stop_time: "30s"
# stop_timeout: 30
# timeout: 0
#
# # Image options
# pull: "missing"
# image_strict: false
# arch: "amd64"
# platform: "linux/amd64"
#
# # Advanced options
# timezone: "UTC"
# requires:
# - "database-container"
#
# # Systemd integration
# generate_systemd:
# path: "/etc/systemd/system"
# restart_policy: "always"
# stop_timeout: 120
# names: true
# container_prefix: "container"
# new: false
# no_header: false
# wants:
# - "network-online.target"
# after:
# - "network-online.target"
# requires:
# - "postgresql.service"
#
# # Control options
# recreate: false
# force_restart: false
# debug: false
#
# # Database container with quadlet
# - name: postgres
# image: docker.io/postgres:15
# state: quadlet
# env:
# POSTGRES_DB: myapp
# POSTGRES_USER: appuser
# POSTGRES_PASSWORD: secretpass
# volumes:
# - "postgres-data:/var/lib/postgresql/data"
# ports:
# - "5432:5432"
# networks:
# - backend
# memory: "4g"
# cpu_shares: 2048
# healthcheck: "pg_isready -U appuser -d myapp"
# healthcheck_interval: "10s"
# healthcheck_timeout: "5s"
# healthcheck_retries: 5
# quadlet_dir: "/etc/containers/systemd"
# quadlet_filename: "postgres-db"
# quadlet_file_mode: "0640"
# quadlet_options:
# - "AutoUpdate=registry"
# - "Pull=newer"
# - |
# [Install]
# WantedBy=default.target
#
# # Privileged system container
# - name: monitoring-agent
# image: quay.io/prometheus/node-exporter:latest
# state: started
# privileged: true
# read_only: true
# pid_mode: "host"
# networks:
# - host
# volumes:
# - "/proc:/host/proc:ro"
# - "/sys:/host/sys:ro"
# - "/:/rootfs:ro"
# command:
# - "--path.procfs=/host/proc"
# - "--path.sysfs=/host/sys"
# - "--collector.filesystem.ignored-mount-points"
# ports:
# - "9100:9100"
# restart_policy: "always"
# user: "nobody"
# cap_drop:
# - "ALL"
# security_opt:
# - "no-new-privileges=true"
# Network management
podman_networks: []
# Example network configuration:
# podman_networks:
# - name: app-network
# 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"
# gateway: "172.20.0.1"
# internal: true
# disable_dns: false
# dns:
# - "8.8.8.8"
# - "8.8.4.4"
# options:
# mtu: 1500
# vlan: 100
# - name: macvlan-network
# driver: macvlan
# macvlan: "eth0"
# subnet: "192.168.1.0/24"
# gateway: "192.168.1.1"
# ipam_driver: "host-local"
# - name: ipv6-network
# driver: bridge
# subnet: "fd00::/64"
# gateway: "fd00::1"
# ipv6: true
# route:
# - "10.10.0.0/16,192.168.1.1"
# Volume management
podman_volumes: []
# Example volume configuration:
# podman_volumes:
# - name: app-data
# state: present
# driver: local
# - name: database-volume
# state: present
# driver: local
# labels:
# environment: production
# backup: daily
# 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
podman_pods: []
# Example pod configuration:
# podman_pods:
# - name: webapp-pod
# state: started
# ports:
# - "8080:80"
# - "3306:3306"
# networks:
# - frontend
# hostname: webapp
# dns:
# - "8.8.8.8"
# - "8.8.4.4"
# labels:
# app: webapp
# tier: frontend
# volumes:
# - "webapp-data:/data"
# infra: true
# infra_image: "k8s.gcr.io/pause:3.1"
# - name: database-pod
# state: created
# networks:
# - backend
# security_opt:
# - "seccomp=unconfined"
# memory: "2g"
# cpu_shares: "1024"
# add_host:
# - "database.local:127.0.0.1"
# share: "net,ipc"
# userns: "auto"
# - name: monitoring-pod
# state: started
# ports:
# - "9090:9090"
# device:
# - "/dev/sda:/dev/xvda:rwm"
# sysctl:
# net.core.somaxconn: "1024"
# shm_size: "128m"
# exit_policy: "stop"
# - name: quadlet-pod
# state: quadlet
# ports:
# - "4444:5555"
# volumes:
# - "/var/run/docker.sock:/var/run/docker.sock"
# quadlet_dir: "/etc/containers/systemd"
# quadlet_filename: "custom-pod"
# quadlet_file_mode: "0640"
# quadlet_options:
# - "AutoUpdate=registry"
# - "Pull=newer"
# generate_systemd:
# path: "/etc/systemd/system"
# restart_policy: "always"

8
handlers/main.yml Normal file
View File

@@ -0,0 +1,8 @@
---
# handlers file for podman
- name: Restart podman
ansible.builtin.systemd:
name: podman
state: restarted
listen: restart podman

24
meta/main.yml Normal file
View File

@@ -0,0 +1,24 @@
---
galaxy_info:
role_name: podman
author: Daniel Akulenok
namespace: keepit
description: Install and configure Podman container runtime and manage containers
company: Keepit A/S
license: MIT
min_ansible_version: "2.11"
platforms:
- name: Ubuntu
versions:
- focal
- jammy
- name: Debian
versions:
- bullseye
- bookworm
galaxy_tags:
- podman
- containers
- docker
- oci
dependencies: []

5
meta/requirements.yml Normal file
View File

@@ -0,0 +1,5 @@
---
# Collection requirements for the podman role
collections:
- name: containers.podman
version: ">=1.10.0"

View File

@@ -0,0 +1,17 @@
---
- name: Converge - apply podman role
hosts: all
become: true
vars:
podman_install_from_repo: true
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: []
roles:
- name: podman

View File

@@ -0,0 +1,30 @@
---
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-bullseye
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 .

View File

@@ -0,0 +1,28 @@
---
- 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: yes
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

View File

@@ -0,0 +1,3 @@
---
collections:
- name: containers.podman

View File

@@ -0,0 +1,34 @@
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"

58
tasks/configure.yml Normal file
View File

@@ -0,0 +1,58 @@
---
# Configure Podman
- name: Ensure containers configuration directory exists
ansible.builtin.file:
path: /etc/containers
state: directory
owner: root
group: root
mode: "0755"
- name: Configure container registries
ansible.builtin.template:
src: registries.conf.j2
dest: "{{ podman_registries_conf_path }}"
owner: root
group: root
mode: "0644"
backup: true
when: podman_configure_registries
notify:
- restart podman
- name: Configure container storage
ansible.builtin.template:
src: storage.conf.j2
dest: "{{ podman_storage_conf_path }}"
owner: root
group: root
mode: "0644"
backup: true
when: podman_configure_storage
notify:
- restart podman
- name: Configure container policy
ansible.builtin.template:
src: policy.json.j2
dest: "{{ podman_policy_path }}"
owner: root
group: root
mode: "0644"
backup: true
when: podman_configure_policy
notify:
- restart podman
- name: Ensure storage directories exist
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: root
group: root
mode: "0755"
loop:
- "{{ podman_storage_runroot }}"
- "{{ podman_storage_graphroot }}"
when: podman_configure_storage

241
tasks/containers.yml Normal file
View File

@@ -0,0 +1,241 @@
---
# Manage Podman containers
- name: Manage Podman containers
containers.podman.podman_container:
name: "{{ item.name }}"
image: "{{ item.image }}"
state: "{{ 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) }}"
# 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) }}"
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) }}"
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 | default(false) }}"
# 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) }}"
# Environment variables
env: "{{ item.env | default(omit) }}"
env_file: "{{ item.env_files | default(omit) }}"
env_host: "{{ item.env_host | default(false) }}"
env_merge: "{{ item.env_merge | default(omit) }}"
unsetenv: "{{ item.unsetenv | default(omit) }}"
unsetenv_all: "{{ item.unsetenv_all | default(false) }}"
# 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 | default(false) }}"
oom_score_adj: "{{ item.oom_score_adj | default(omit) }}"
pids_limit: "{{ 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) }}"
# 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) }}"
# Namespaces
ipc: "{{ item.ipc_mode | default(omit) }}"
pid: "{{ item.pid_mode | default(omit) }}"
uts: "{{ item.uts | default(omit) }}"
cgroupns: "{{ item.cgroupns | default(omit) }}"
# Cgroups
cgroups: "{{ item.cgroups | default(omit) }}"
cgroup_parent: "{{ item.cgroup_parent | default(omit) }}"
cgroup_conf: "{{ 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) }}"
# Shared memory and tmpfs
shm_size: "{{ item.shm_size | default(omit) }}"
shm_size_systemd: "{{ item.shm_size_systemd | default(omit) }}"
# Pods
pod: "{{ item.pod | default(omit) }}"
pod_id_file: "{{ 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) }}"
# 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 | default(false) }}"
# 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) }}"
# Metadata and labels
label: "{{ item.labels | default(omit) }}"
label_file: "{{ item.label_file | default(omit) }}"
annotation: "{{ item.annotations | default(omit) }}"
# Container lifecycle
restart_policy: "{{ item.restart_policy | default('no') }}"
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) }}"
# Pull and image options
pull: "{{ item.pull | default('missing') }}"
image_strict: "{{ item.image_strict | default(false) }}"
arch: "{{ item.arch | default(omit) }}"
os: "{{ item.os | default(omit) }}"
platform: "{{ item.platform | default(omit) }}"
variant: "{{ 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) }}"
# File operations
cidfile: "{{ item.cidfile | default(omit) }}"
conmon_pidfile: "{{ item.conmon_pidfile | default(omit) }}"
pid_file: "{{ item.pid_file | default(omit) }}"
# 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) }}"
# 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 | default(false) }}"
sdnotify: "{{ item.sdnotify | default(omit) }}"
secrets: "{{ item.secrets | default(omit) }}"
timezone: "{{ item.timezone | default(omit) }}"
# Retry options
retry: "{{ item.retry | default(omit) }}"
retry_delay: "{{ item.retry_delay | default(omit) }}"
# Systemd generation
generate_systemd: "{{ 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) }}"
# 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) }}"
delete_time: "{{ item.delete_time | default(omit) }}"
delete_volumes: "{{ item.delete_volumes | default(false) }}"
debug: "{{ item.debug | default(false) }}"
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']

14
tasks/install.yml Normal file
View File

@@ -0,0 +1,14 @@
---
# Install Podman packages
- 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:
name: "{{ podman_packages }}"
state: present
notify:
- restart podman

48
tasks/main.yml Normal file
View File

@@ -0,0 +1,48 @@
---
# tasks file for podman
- name: Install Podman packages
ansible.builtin.include_tasks: install.yml
tags:
- podman
- podman-install
- name: Configure Podman
ansible.builtin.include_tasks: configure.yml
tags:
- podman
- podman-configure
- name: Manage Podman services
ansible.builtin.include_tasks: services.yml
tags:
- podman
- podman-services
- name: Manage Podman networks
ansible.builtin.include_tasks: networks.yml
when: podman_networks | length > 0
tags:
- podman
- podman-networks
- name: Manage Podman volumes
ansible.builtin.include_tasks: volumes.yml
when: podman_volumes | length > 0
tags:
- podman
- podman-volumes
- name: Manage Podman pods
ansible.builtin.include_tasks: pods.yml
when: podman_pods | length > 0
tags:
- podman
- podman-pods
- name: Manage Podman containers
ansible.builtin.include_tasks: containers.yml
when: podman_containers | length > 0
tags:
- podman
- podman-containers

25
tasks/networks.yml Normal file
View File

@@ -0,0 +1,25 @@
---
# Manage Podman networks
- name: Manage Podman networks
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 | default(false) }}"
internal: "{{ item.internal | default(false) }}"
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) }}"
macvlan: "{{ item.macvlan | default(omit) }}"
net_config: "{{ item.net_config | default(omit) }}"
route: "{{ item.route | default(omit) }}"
recreate: "{{ item.recreate | default(false) }}"
loop: "{{ podman_networks }}"
loop_control:
label: "{{ item.name }}"

66
tasks/pods.yml Normal file
View File

@@ -0,0 +1,66 @@
---
# Manage Podman pods
- name: Manage Podman pods
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(true) }}"
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(false) }}"
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(false) }}"
debug: "{{ item.debug | default(false) }}"
loop: "{{ podman_pods }}"
loop_control:
label: "{{ item.name }}"

32
tasks/services.yml Normal file
View File

@@ -0,0 +1,32 @@
---
# Manage Podman services
- name: Enable and start Podman socket
ansible.builtin.systemd:
name: podman.socket
enabled: true
state: started
daemon_reload: true
when: podman_enable_socket
- name: Disable Podman socket
ansible.builtin.systemd:
name: podman.socket
enabled: false
state: stopped
when: not podman_enable_socket
- name: Enable and start Podman API service
ansible.builtin.systemd:
name: podman.service
enabled: true
state: started
daemon_reload: true
when: podman_enable_api_service
- name: Disable Podman API service
ansible.builtin.systemd:
name: podman.service
enabled: false
state: stopped
when: not podman_enable_api_service

19
tasks/volumes.yml Normal file
View File

@@ -0,0 +1,19 @@
---
# Manage Podman volumes
- name: Manage Podman volumes
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 | default(false) }}"
recreate: "{{ item.recreate | default(false) }}"
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) }}"
loop: "{{ podman_volumes }}"
loop_control:
label: "{{ item.name }}"

37
templates/policy.json.j2 Normal file
View File

@@ -0,0 +1,37 @@
{
"default": [
{
"type": "{{ podman_policy_default_type }}"
}
],
"transports": {
"docker-daemon": {
"": [
{
"type": "insecureAcceptAnything"
}
]
},
"docker": {
{% for registry in podman_policy_trusted_registries %}
"{{ registry.registry }}": [
{
"type": "{{ registry.type }}"{% if registry.keyPath is defined %},
"keyType": "{{ registry.keyType | default('GPGKeys') }}",
"keyPath": "{{ registry.keyPath }}"{% endif %}{% if registry.keyData is defined %},
"keyType": "{{ registry.keyType | default('GPGKeys') }}",
"keyData": "{{ registry.keyData }}"{% endif %}{% if registry.signedIdentity is defined %},
"signedIdentity": {{ registry.signedIdentity | to_json }}{% endif %}
}
]{% if not loop.last %},{% endif %}
{% endfor %}
{% if podman_policy_reject_unknown_registries %}
"": [
{
"type": "reject"
}
]
{% endif %}
}
}
}

View File

@@ -0,0 +1,54 @@
# Configuration file for container registries
# {{ ansible_managed }}
# Unqualified image search registries
# These registries will be searched when pulling images without a registry prefix
unqualified-search-registries = [
{% set registries = podman_policy_trusted_registries | selectattr('unqualified_search', 'defined') | selectattr('unqualified_search') | map(attribute='registry') | list %}
{% if registries | length == 0 %}
{% set registries = podman_policy_trusted_registries | map(attribute='registry') | list %}
{% endif %}
{% set base_registries = registries | map('regex_replace', '^([^/]+).*', '\\1') | list %}
{% for registry in base_registries | unique %}
"{{ registry }}"{% if not loop.last %},{% endif %}
{% endfor %}
]
{% for registry_config in podman_policy_trusted_registries %}
# Registry: {{ registry_config.registry }}
[[registry]]
location = "{{ registry_config.registry }}"
insecure = {{ registry_config.insecure | default(false) | lower }}
blocked = {{ registry_config.blocked | default(false) | lower }}
{% if registry_config.mirror is defined %}
# Mirror configuration for {{ registry_config.registry }}
{% for mirror in registry_config.mirror %}
[[registry.mirror]]
location = "{{ mirror.location }}"
insecure = {{ mirror.insecure | default(false) | lower }}
{% endfor %}
{% endif %}
{% if registry_config.prefix is defined %}
prefix = "{{ registry_config.prefix }}"
{% endif %}
{% endfor %}
# Additional registry configurations
{% if podman_registries_additional is defined %}
{% for registry in podman_registries_additional %}
[[registry]]
location = "{{ registry.location }}"
insecure = {{ registry.insecure | default(false) | lower }}
blocked = {{ registry.blocked | default(false) | lower }}
{% if registry.mirror is defined %}
{% for mirror in registry.mirror %}
[[registry.mirror]]
location = "{{ mirror.location }}"
insecure = {{ mirror.insecure | default(false) | lower }}
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}

13
templates/storage.conf.j2 Normal file
View File

@@ -0,0 +1,13 @@
# Storage configuration for containers
# {{ ansible_managed }}
[storage]
driver = "{{ podman_storage_driver }}"
runroot = "{{ podman_storage_runroot }}"
graphroot = "{{ podman_storage_graphroot }}"
[storage.options]
additionalimagestores = []
[storage.options.overlay]
mountopt = "nodev,metacopy=on"

14
vars/Debian.yml Normal file
View File

@@ -0,0 +1,14 @@
---
# 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