Skip to main content
Plugins / Podman

Podman

by head1328

Build and push OCI images with Podman.


The Podman plugin runs podman build and podman push (or podman manifest push for multi-arch builds) inside a Woodpecker pipeline. The setting surface mirrors the podman build and podman push flags 1:1, so anything documented for those subcommands can be expressed as a plugin setting.

Runtime requirements

The plugin runs podman build. The pipeline container must be able to create a new user namespace (CLONE_NEWUSER); the plugin probes this at startup via unshare -U and aborts with a clear error if blocked.

Runner Notes
Codeberg public CI Default seccomp profile lets CLONE_NEWUSER through; works out of the box.
Self-hosted Woodpecker, image on the server's allowlist Server escalates the step automatically.
Self-hosted Woodpecker, repo marked trusted, step uses privileged: true Same as above.

Cross-arch builds (platforms covering more than the host architecture) need qemu binfmt handlers registered on the build host with the F (fix-binary) flag. The plugin itself does no in-container binfmt registration; it relies on the host providing handlers that the kernel exposes to every nested rootless user namespace via F.

Since Linux 6.7 binfmt_misc is per-user-namespace and rootless podman creates a fresh userns per build. Without F, the kernel opens the interpreter lazily inside that fresh userns and fails to find it. With F, the kernel pins the interpreter file descriptor at registration time on the host and re-uses that open fd on every exec, independent of the calling namespace.

On Fedora CoreOS, layer the package via rpm-ostree:

sudo rpm-ostree install qemu-user-static
sudo systemctl reboot

Fedora's qemu-user-static ships /usr/lib/binfmt.d/qemu-*-static.conf files with F already set; systemd-binfmt registers them on the next boot. On other distros, install the equivalent statically-linked qemu package and verify flags: F (or similar) is present in /proc/sys/fs/binfmt_misc/qemu-<arch>.

The chilly-willy-agent reference setup ships this wiring in its Ignition config.

Settings

Output and authentication

Setting Default Description
repo none Target image repository (e.g. head1328/myapp). With registry and tags this forms the destination ref.
tags latest One or more image tags. List or comma-separated.
registry docker.io Registry hostname or hostname/path.
username none Registry username for podman login.
password none Registry password or access token (use a Woodpecker secret).
auth_config none Full registry auth JSON, written verbatim to $REGISTRY_AUTH_FILE. Use for multi-registry pushes.
dry_run false Build only, skip push. Automatically enabled when no repo is set.

Source

Setting Default podman build flag Description
containerfile Containerfile --file Path to the Containerfile, relative to context.
context $CI_WORKSPACE (positional) Build context directory.
target none --target Multi-stage build target.
build_args none --build-arg KEY=VALUE entries. List or comma-separated.
build_args_from_env none --build-arg Names of env vars to forward as build args.
env_file none (sourced) File whose KEY=VALUE lines are sourced into the plugin environment before the build.

Multi-architecture

Setting podman build flag Description
platforms --platform + --manifest Comma-separated list. Triggers manifest-list mode: the plugin builds for every platform and pushes the resulting manifest list via podman manifest push --all.
platform --platform Single-target build.
arch --arch CPU architecture for the build.
os --os Operating system for the build.
variant --variant CPU variant (e.g. v7).
manifest --manifest Override the internal manifest list name (default: auto-generated).

Caching

Setting podman build flag Description
cache --layers=true Enable layer caching.
cache_from --cache-from Registry path(s) to pull cache layers from. List.
cache_to --cache-to Registry path(s) to push cache layers to. List.
no_cache --no-cache=true Do not use any cached layers.

Image metadata

Setting podman build flag Description
labels --label OCI labels (KEY=VALUE). List.
annotations --annotation OCI annotations (KEY=VALUE). List.
format --format Image format: oci or docker.
squash --squash=true Squash newly built layers into one.
squash_all --squash-all=true Squash every layer, including the base.
omit_history --omit-history=true Do not record build history in the image.
identity_label --identity-label=true Add a default identity label to the image.

Build environment

Setting podman build flag Description
secrets --secret Build-time secrets, e.g. id=mytoken,src=/run/secrets/foo or id=mytoken,env=MY_ENV. List.
ssh --ssh SSH agents to forward into the build (default or id=path). List.
userns --userns User namespace mode.
network --network Network mode during build (host, none, private, name).
add_host --add-host Extra host-to-IP mappings (host:ip). List.
http_proxy --http-proxy true or false. Whether the host's proxy env vars are passed in.
unsetenv --unsetenv Environment variables to unset before building. List.
pull --pull Pull policy: always, missing, never, newer.

Resource limits

Setting podman build flag Description
memory --memory Memory limit (e.g. 512m, 2g).
memory_swap --memory-swap Swap limit.
cpu_shares --cpu-shares Relative CPU shares.
cpu_period --cpu-period CFS period in microseconds.
cpu_quota --cpu-quota CFS quota in microseconds.
cpuset_cpus --cpuset-cpus CPUs to allow (e.g. 0-3).
shm_size --shm-size /dev/shm size.
ulimit --ulimit type=soft:hard entries. List.
cgroup_parent --cgroup-parent Optional parent cgroup.

Security, devices and mounts

Setting podman build flag Description
security_opt --security-opt Security options (e.g. label=disable). List.
cap_add --cap-add Linux capabilities to add. List.
cap_drop --cap-drop Linux capabilities to drop. List.
device --device Devices to expose to the build (/dev/sda). List.
volume --volume Bind-mounts during build (src:dst[:opt]). List.
decryption_key --decryption-key Key file(s) to decrypt encrypted base images. List.

Push

Setting podman push flag Description
compress --compression-format Layer compression on push (e.g. gzip, zstd, zstd:chunked).
retry (plugin-side loop) Number of times to retry on push failure. Implemented as a shell loop because podman manifest push does not support --retry.
retry_delay (plugin-side loop) Delay in seconds between retries (integer, no s suffix).
sign_by --sign-by GPG key fingerprint for signing the pushed image.

TLS, logging, escape hatch

Setting Flag(s) Description
tls_verify --tls-verify Default true. Set to false to disable TLS verification for login, build and push.
skip_tls_verify --tls-verify=false Convenience alias for tls_verify: false.
log_level --log-level Podman log level. Default info.
extra_opts (passthrough) Free-form extra flags appended verbatim to the build command. Whitespace-split.

Examples

Minimal build and push

steps:
  - name: publish
    image: head1328/woodpecker-ci-plugin-podman
    settings:
      repo: head1328/myapp
      tags: latest
      username:
        from_secret: docker_hub_user
      password:
        from_secret: docker_hub_token

Multi-arch with labels and a cache repo

steps:
  - name: publish
    image: head1328/woodpecker-ci-plugin-podman
    settings:
      repo: head1328/myapp
      tags: [latest, "1.2.3"]
      platforms: linux/amd64,linux/arm64
      cache: true
      cache_from: [head1328/myapp:cache]
      cache_to: [head1328/myapp:cache]
      labels:
        - org.opencontainers.image.source=https://codeberg.org/head1328/myapp
        - org.opencontainers.image.revision=${CI_COMMIT_SHA}
      username:
        from_secret: docker_hub_user
      password:
        from_secret: docker_hub_token

Build-time secret and SSH forwarding

steps:
  - name: build
    image: head1328/woodpecker-ci-plugin-podman
    settings:
      repo: head1328/myapp
      secrets:
        - id=npmrc,env=NPM_TOKEN
      ssh:
        - default
      dry_run: true
    environment:
      NPM_TOKEN:
        from_secret: npm_token

Push to multiple registries

steps:
  - name: docker-hub
    image: head1328/woodpecker-ci-plugin-podman
    settings:
      repo: head1328/myapp
      registry: docker.io
      tags: latest
      username:
        from_secret: docker_hub_user
      password:
        from_secret: docker_hub_token

  - name: codeberg
    image: head1328/woodpecker-ci-plugin-podman
    settings:
      repo: head1328/myapp
      registry: codeberg.org
      tags: latest
      username:
        from_secret: codeberg_username
      password:
        from_secret: codeberg_token