podman-systemd.unit(5)

πŸ’– podman-systemd.unit(5) β€” File Formats Manual πŸ’–

okay so you're reading podman-systemd.unit(5), from the File Formats Manual, and it's literally the glow-up guide for Podman Quadlet systemd units. 🫢

🌸 NAME 🌸

podman-systemd.unit β€” systemd units using Podman Quadlet.

✨ SYNOPSIS ✨

Quadlet unit filenames are giving this exact shape, bestie:

name.artifact, name.build, name.container, name.image, name.kube,
name.network, name.pod, name.volume

and the file types are:

πŸ—‚οΈ Podman rootful unit search path

Quadlet files for the root user can be placed in these directories, ordered in precedence. Meaning duplicate named quadlets found under /run take precedence over ones in /etc, as well as those in /usr:

Temporary quadlets, usually used for testing:

  • /run/containers/systemd/

System administrator's defined quadlets:

  • /etc/containers/systemd/

Distribution defined quadlets:

  • /usr/share/containers/systemd/

🐚 Podman rootless unit search path

Quadlet files for non-root users can be placed in the following directories:

  • $XDG_RUNTIME_DIR/containers/systemd/
  • $XDG_CONFIG_HOME/containers/systemd/ or ~/.config/containers/systemd/
  • /etc/containers/systemd/users/${UID}
  • /etc/containers/systemd/users/
  • /usr/share/containers/systemd/users/${UID}
  • /usr/share/containers/systemd/users/

Quadlet supports using symbolic links for the base of the search paths and inside them. slay. πŸ’…

πŸ’… DESCRIPTION πŸ’…

Podman supports building and starting containers (and creating volumes) via systemd by using a systemd generator. These files are read during boot (and when systemctl daemon-reload is run) and generate corresponding regular systemd service unit files. Both system and user systemd units are supported. All options and tables available in standard systemd unit files are supported. For example, options defined in the [Service] table and [Install] tables pass directly to systemd and are handled by it. See systemd.unit(5) man page for more information.

The Podman generator reads the search paths above and reads files with the extensions .container .volume, .network, .build, .pod, .kube, and .artifact, and for each file generates a similarly named .service file. Be aware that existing vendor services (i.e., in /usr/) are replaced if they have the same name. The generated unit files can be started and managed with systemctl like any other systemd service. systemctl {--user} list-unit-files lists existing unit files on the system. To list unit files of a user who has /sbin/nologin as a login shell, run sudo systemctl --machine username@ --user list-unit-files.

The Podman files use the same format as regular systemd unit files. Each file type has a custom section (for example, [Container]) that is handled by Podman, and all other sections are passed on untouched, allowing the use of any normal systemd configuration options like dependencies or cgroup limits.

The source files also support drop-ins in the same way systemd does. For a given source file (foo.container), the corresponding .d directory (foo.container.d) will be scanned for files with a .conf extension, which are then merged into the base file in alphabetical order. Top-level type drop-ins (container.d) will also be included. If the unit contains dashes (-) in the name (foo-bar-baz.container), then the drop-in directories generated by truncating the name after the dash are searched as well (foo-.container.d and foo-bar-.container.d). Drop-in files with the same name further down the hierarchy override those further up (foo-bar-baz.container.d/10-override.conf overrides foo-bar-.container.d/10-override.conf, which overrides foo-.service.d/10-override.conf, which overrides container.d/10-override.conf). The format of these drop-in files is the same as the base file. This is useful to alter or add configuration settings for a unit, without having to modify unit files.

For rootless containers, when administrators place Quadlet files in the /etc/containers/systemd/users or /usr/share/containers/users directories, all users' sessions execute the Quadlet when the login session begins. If the administrator places a Quadlet file in the /etc/containers/systemd/users/${UID}/ or /usr/share/containers/users/${UID} directory, then only the user with the matching UID executes the Quadlet when the login session gets started. For unit files placed in subdirectories within /etc/containers/systemd/users/${UID}/, /usr/share/containers/users/${UID} and the other user unit search paths, Quadlet will recursively search and run the unit files present in these subdirectories.

Note that Quadlet units do not support running as a non-root user by defining the User, Group, or DynamicUser systemd options. If you want to run a rootless Quadlet, you will need to create the user and add the unit file to one of the above rootless unit search paths.

When a Quadlet unit starts, Podman may need to pull or build container images, which can take significantly longer than systemd's default 90-second service startup limit. If this causes the unit to fail, you can either pre-pull the required images or increase the service's startup timeout using the TimeoutStartSec option. Keep in mind, however, that for units that specify Type=oneshot, the startup timeout is disabled by default (set to infinity), so TimeoutStartSec must be explicitly configured if a timeout is desired. For further details on TimeoutStartSec, see systemd.service(5).

Adding the following snippet to a Quadlet file extends the startup timeout to 15 minutes.

[Service]
TimeoutStartSec=900

Quadlet requires the use of cgroup v2, use podman info --format {{.Host.CgroupsVersion}} to check on the system.

βš™οΈ Service Type

By default, the Type field of the Service section of the Quadlet file does not need to be set. Quadlet will set it to notify for .container and .kube files, forking for .pod files, and oneshot for .volume, .network, .build, .image, and .artifact files.

However, Type may be explicitly set to oneshot for .container and .kube files when no containers are expected to run once podman exits.

When setting Type=oneshot, it is recommended to also set RemainAfterExit=yes to prevent the service state from becoming inactive (dead). However, when activating a service via a timer unit, having RemainAfterExit=yes leaves the job in a "started" state which prevents subsequent activations by the timer. For more information, see the systemd.service(5) man page.

Examples for such cases:

  • .container file with an image that exits after their entrypoint has finished
  • .kube file pointing to a Kubernetes Yaml file that does not define any containers. E.g. PVCs only

πŸš€ Enabling unit files

The services created by Podman are considered transient by systemd, which means they don't have the same persistence rules as regular units. In particular, it is not possible to systemctl enable them in order for them to become automatically enabled on the next boot.

To compensate for this, the generator manually applies the [Install] section of the container definition unit files during generation, in the same way systemctl enable does when run later.

For example, to start a container on boot, add something like this to the file:

[Install]
WantedBy=default.target

Currently, only the Alias, WantedBy, RequiredBy, and UpheldBy keys are supported.

The Install section can be part of the main file, or it can be in a separate drop-in file as described above. The latter allows you to install a non-enabled unit and then later enabling it by installing the drop-in.

🧬 Template files

Systemd supports a concept of template files. They are units with names of the form basename@instancename.service when they are running, but that can be instantiated multiple times from a single basename@.service file. The individual instances can also be different by using drop-in files with the full instance name.

Quadlets support these in two ways. First of all, a quadlet unit with a template form will generate a systemd service with a template form, and the template systemd service can be used as a regular template. For example, foo@.container will generate foo@.service and you can then systemctl start foo@bar.service.

Secondly, if you make a symlink like foo@instance.container, that will generate an instantiated template file. When generating this file quadlet will read drop-in files both from the instanced directory (foo@instance.container.d) and the template directory (foo@.container.d). This allows customization of individual instances.

Instanced template files (like foo@bar.container) can be enabled just like non-templated ones. However, templated ones (foo@.container) are different, because they need to be instantiated. If the [Install] section contains a DefaultInstance= key, then that instance will be enabled, but if not, nothing will happen and the options will only be used as the default for units that are instantiated using symlinks.

An example template file sleep@.container might look like this:

[Unit]
Description=A templated sleepy container

[Container]
Image=quay.io/fedora/fedora
Exec=sleep %i

[Service]
# Restart service when sleep finishes
Restart=always

[Install]
WantedBy=multi-user.target
DefaultInstance=100

If this is installed, then on boot there will be a sleep@100.service running that sleeps for 100 seconds. You can then do something like systemctl start sleep@50.service to start another instance that sleeps 50 seconds, or alternatively another service can start it via a dependency like Wants=sleep@50.service.

In addition, if you do ln -s sleep@.container sleep@10.container you will also have a 10 second sleep running at boot. And, if you want that particular instance to be running with another image, you can create a drop-in file like sleep@10.container.d/10-image.conf:

[Container]
Image=quay.io/centos/centos

πŸ” Referencing other template Quadlets

It often occurs that you may want to reference another template Quadlet from your Quadlet. For example, you may want each instance of foo@.container to mount a distinct volume. In this case you can create a template Quadlet foo-data@.volume (the name is arbitrary) and reference it in foo@.container:

foo-data@.volume:

okay so here’s the example in action, bestie:

[Volume]
# Optional: without an explicit VolumeName, Quadlet auto-generates one with a
# "systemd-" prefix (e.g. systemd-foo-data-%i for foo-data@.volume).
# This does not affect how you reference this Quadlet from other Quadlets.
VolumeName=my-name-%i

foo@.container:

[Container]
Volume=foo-data@.volume:/mnt/path

Note that we reference the template name foo-data@.volume instead of the instance name foo-data@%i.volume. Quadlet will read the template file and determine the correct volume name (with the systemd specifier) for the podman run command in the generated .service file. In this example, the ExecStart generated by Quadlet would look something like this:

ExecStart=/usr/bin/podman run ... -v my-name-%i:/mnt/path ...

πŸ›€οΈ Relative paths πŸ›€οΈ

In order to support systemd specifiers, Quadlet does not resolve relative paths that start with %. To resolve such a path, prepend it with ./.

For example, instead of EnvironmentFile=%n/env use EnvironmentFile=./%n/env.

🐞 Debugging unit files 🐞

After placing the unit file in one of the unit search paths (mentioned above), you can start it with systemctl start {--user}. If it fails with "Failed to start example.service: Unit example.service not found.", then it is possible that you used incorrect syntax or you used an option from a newer version of Podman Quadlet and the generator failed to create a service file.

View the generated files and/or error messages with:

/usr/lib/systemd/system-generators/podman-system-generator {--user} --dryrun

Alternatively, show only the errors with:

systemd-analyze {--user} --generators=true verify example.service

That command also performs additional checks on the generated service unit. For details, see systemd-analyze(1) man page.

πŸ” Debugging a limited set of unit files πŸ”

If you would like to debug a limited set of unit files, you can copy them to a separate directory and set the QUADLET_UNIT_DIRS environment variable to this directory when running the command below:

QUADLET_UNIT_DIRS=<Directory> /usr/lib/systemd/system-generators/podman-system-generator {--user} --dryrun

This will instruct Quadlet to look for units in this directory instead of the common ones and by that limit the output to only the units you are debugging.

🌐 Implicit network dependencies 🌐

Quadlet will add dependencies on network-online.target (as root) or podman-user-wait-network-online.service (as user) by adding After= and Wants= properties to the unit. This is to ensure that the network is reachable if an image needs to be pulled and by the time the container is started.

The special case podman-user-wait-network-online.service unit is needed as user because user units are unable to wait for system (root) units so network-online.target doesn't do anything there and is instead ignored. As this caused a significant amount of issues we decided to work around this with our own special purpose unit that simply checks if the network-online.target unit is active with systemctl is-active network-online.target.

This behavior can be disabled by adding DefaultDependencies=false in the Quadlet section.

⚠️ Note: the systemd [Unit] section has an option with the same name but a different meaning.

πŸ”— Dependency between Quadlet units πŸ”—

Quadlet will automatically translate dependencies, specified in the keys Wants, Requires, Requisite, BindsTo, PartOf, Upholds, Conflicts, Before and After of the [Unit] section, between different Quadlet units.

For example the fedora.container unit below specifies a dependency on the basic.container unit.

[Unit]
After=basic.container
Requires=basic.container

[Container]
Image=registry.fedoraproject.org/fedora:41

🏷️ Setting resource names 🏷️

Quadlet units allow setting the names of the created resources (e.g. VolumeName for .volume units or PodName for .pod units).

Note that using systemd specifiers that reference the generated service unit (e.g. $N) breaks Quadlet's ability to link between resources as they are translated differently in each service.

🐳 Container units [Container] 🐳

Container units are named with a .container extension and contain a [Container] section describing the container that is run as a service. The resulting service file contains a line like ExecStart=podman run … image-name, and most of the keys in this section control the command-line options passed to Podman. However, some options also affect the details of how systemd is set up to run and interact with the container.

By default, the Podman container has the same name as the unit, but with a systemd- prefix, i.e. a $name.container file creates a $name.service unit and a systemd-$name Podman container. The ContainerName option allows for overriding this default name with a user-provided one.

There is only one required key, Image, which defines the container image the service runs.

Valid options for [Container] are listed below:

Okay bestie, picking right back up with the cheat sheet β€” left column is what you write in the unit file, right column is the podman run flag it turns into. No cap, they line up almost one-to-one 🫢

  • AddCapability=CAP β€” this is --cap-add CAP, easy peasy
  • AddDevice=/dev/foo β€” becomes --device /dev/foo, so your container can actually touch the thing
  • AddHost=example.com:192.168.10.11 β€” turns into --add-host example.com:192.168.10.11
  • Annotation="XYZ" β€” same as --annotation "XYZ" (quotes and all, don't lose 'em πŸ’…)
  • AppArmor="alternate-profile" β€” maps to --security-opt apparmor=alternate-profile
  • AutoUpdate=registry β€” lowkey just a label wearing a costume: --label "io.containers.autoupdate=registry"
  • CgroupsMode=no-conmon β€” that's --cgroups=no-conmon
  • ContainerName=name β€” the classic, --name name
  • ContainersConfModule=/etc/nvd.conf β€” lines up with --module=/etc/nvd.conf
  • DNS=192.168.55.1 β€” same as --dns=192.168.55.1
  • DNSOption=ndots:1 β€” same as --dns-option=ndots:1
  • DNSSearch=example.com β€” same as --dns-search example.com
  • DropCapability=CAP β€” the flip side, --cap-drop=CAP
  • Entrypoint=/foo.sh β€” same as --entrypoint=/foo.sh
  • Environment=foo=bar β€” this one's --env foo=bar
  • EnvironmentFile=/tmp/env β€” this one's --env-file /tmp/env
  • EnvironmentHost=true β€” this one's --env-host
  • Exec=/usr/bin/command β€” special case alert 🚨 this isn't a flag at all, it's the command that goes after the image name, like /usr/bin/command
  • ExposeHostPort=50-59 β€” same as --expose 50-59
  • GIDMap=0:10000:10 β€” same as --gidmap=0:10000:10
  • GlobalArgs=--log-level=debug β€” same as --log-level=debug
  • Group=1234 β€” quirky one: this is --user UID:1234 (yes, literally the string UID in there β€” don't @ me, that's what it says πŸ’€)
  • GroupAdd=keep-groups β€” same as --group-add=keep-groups
  • HealthCmd=/usr/bin/command β€” same as --health-cmd=/usr/bin/command
  • HealthInterval=2m β€” same as --health-interval=2m
  • HealthLogDestination=/foo/log β€” same as --health-log-destination=/foo/log
  • HealthMaxLogCount=5 β€” same as --health-max-log-count=5
  • HealthMaxLogSize=500 β€” same as --health-max-log-size=500
  • HealthOnFailure=kill β€” same as --health-on-failure=kill
  • HealthRetries=5 β€” same as --health-retries=5
  • HealthStartPeriod=1m β€” same as --health-start-period=1m
  • HealthStartupCmd=command β€” same as --health-startup-cmd=command
  • HealthStartupInterval=1m β€” same as --health-startup-interval=1m
  • HealthStartupRetries=8 β€” same as --health-startup-retries=8

…and the list keeps going, don't you worry 🧚

🌈 key β†’ flag, the saga continues 🌈

okay so the chart keeps going, bestie β€” left side is what you type in the unit file, right side is the podman run flag it lowkey turns into. no cap, it's giving "translator app" energy πŸ’…

  • HealthStartupSuccess=2 β†’ --health-startup-success=2 πŸ«€
  • HealthStartupTimeout=1m33s β†’ --health-startup-timeout=1m33s ⏱️
  • HealthTimeout=20s β†’ --health-timeout=20s βŒ›
  • HostName=example.com β†’ --hostname example.com β€” yes she gets a name, main character energy ✨
  • HttpProxy=true β†’ --http-proxy=true 🌐
  • Image=ubi8 β†’ the image specification itself, so that's image ubi8 πŸ–ΌοΈ
  • ImageVolume=tmpfs β†’ --image-volume tmpfs πŸ“¦
  • IP=192.5.0.1 β†’ --ip 192.5.0.1
  • IP6=2001:db8::1 β†’ --ip6 2001:db8::1 β€” v6 girlies rise 🌸
  • Label="XYZ" β†’ --label "XYZ" 🏷️
  • LogDriver=journald β†’ --log-driver journald
  • LogOpt=path=/var/log/mykube.json β†’ --log-opt path=/var/log/mykube.json πŸ“
  • Mask=/proc/sys/foo:/proc/sys/bar β†’ --security-opt mask=/proc/sys/foo:/proc/sys/bar β€” those paths simply do not exist to the container, periodt πŸ™ˆ
  • Memory=20g β†’ --memory 20g πŸ’Ύ
  • Mount=type=... β†’ --mount type=...
  • Network=host β†’ --network host πŸ•ΈοΈ
  • NetworkAlias=name β†’ --network-alias name
  • NoNewPrivileges=true β†’ --security-opt no-new-privileges πŸ”’
  • Notify=true β†’ --sdnotify container β€” that's the sdnotify vibe, iykyk πŸ””
  • PidsLimit=10000 β†’ --pids-limit 10000
  • Pod=pod-name β†’ --pod=pod-name 🫘
  • PodmanArgs=--publish 8080:80 β†’ --publish 8080:80 β€” the sneaky backdoor for extra flags πŸšͺ
  • PublishPort=8080:80 β†’ --publish 8080:80 πŸš€
  • Pull=never β†’ --pull never
  • ReadOnly=true β†’ --read-only β€” she's frozen, do not touch 🧊
  • ReadOnlyTmpfs=true β†’ --read-only-tmpfs
  • ReloadCmd=/usr/bin/command β†’ adds ExecReload and runs exec with the value πŸ”
  • ReloadSignal=SIGHUP β†’ adds ExecReload and runs kill with the signal πŸ“Ά
  • Retry=5 β†’ --retry=5 πŸ”„
  • RetryDelay=5s β†’ --retry-delay=5s ⏳
  • Rootfs=/var/lib/rootfs β†’ --rootfs /var/lib/rootfs 🌳
  • RunInit=true β†’ --init 🐣
  • SeccompProfile=/tmp/s.json β†’ --security-opt seccomp=/tmp/s.json πŸ›‘οΈ
  • Secret=secret β†’ --secret=secret[,opt=opt ...] 🀫 (yes, the [,opt=opt ...] part is literally there, copy it exactly bestie)
  • SecurityLabelDisable=true β†’ --security-opt label=disable 🏷️
  • SecurityLabelDisable=true...

wait, hold on β€” the last one is where our part ends πŸŽ€ more coming, don't go anywhere πŸ’–

  • SecurityLabelFileType=usr_t β€” maps to --security-opt label=filetype:usr_t
  • SecurityLabelLevel=s0:c1,c2 β€” maps to --security-opt label=level:s0:c1,c2
  • SecurityLabelNested=true β€” maps to --security-opt label=nested
  • SecurityLabelType=spc_t β€” maps to --security-opt label=type:spc_t
  • ServiceName=name β€” names the systemd unit name.service
  • ShmSize=100m β€” maps to --shm-size=100m
  • StartWithPod=true β€” if Pod= is defined, the container gets started by the pod
  • StopSignal=SIGINT β€” maps to --stop-signal=SIGINT
  • StopTimeout=20 β€” maps to --stop-timeout=20
  • SubGIDMap=gtest β€” maps to --subgidname=gtest
  • SubUIDMap=utest β€” maps to --subuidname=utest
  • Sysctl=name=value β€” maps to --sysctl=name=value
  • Timezone=local β€” maps to --tz local
  • Tmpfs=/work β€” maps to --tmpfs /work
  • UIDMap=0:10000:10 β€” maps to --uidmap=0:10000:10
  • Ulimit=nofile=1000:10000 β€” maps to --ulimit nofile=1000:10000
  • Unmask=ALL β€” maps to --security-opt unmask=ALL
  • User=bin β€” maps to --user bin
  • UserNS=keep-id:uid=200,gid=210 β€” maps to --userns keep-id:uid=200,gid=210
  • Volume=/source:/dest β€” maps to --volume /source:/dest
  • WorkingDir=$HOME β€” maps to --workdir $HOME

πŸŽ€ Description of [Container] section πŸŽ€

okay bestie, this is the main character section, so pay attention πŸ’… here's every single key you can put in there:

AddCapability=

Adds these capabilities on top of the default Podman capability set that the container already gets. 🫢

It's a space-separated list of capabilities, and you can list this key as many times as you want.

Like, for example:

AddCapability=CAP_DAC_OVERRIDE CAP_IPC_OWNER

AddDevice=

Pulls a device node from the host into the container. The format is HOST-DEVICE[:CONTAINER-DEVICE][:PERMISSIONS], where HOST-DEVICE is the path of the device node on the host, CONTAINER-DEVICE is the path of the device node in the container, and PERMISSIONS is a list of permissions combining r for read, w for write, and m for mknod(2).

And a - prefix? That tells Quadlet to add the device only if it actually exists on the host ✨

This key can be listed multiple times.

AddHost=

Drops a host-to-IP mapping into /etc/hosts. Format is hostname:ip.

Same energy as the Podman --add-host option. This key can be listed multiple times, no cap.

Annotation=

Sets one or more OCI annotations on the container. The format is a list of key=value items β€” think of it like Environment's cousin.

This key can be listed multiple times.

AppArmor=

Sets the AppArmor confinement profile for the container. And if you set it to unconfined, it literally turns AppArmor confinement off. 🫒

AutoUpdate=

Tells you whether the container gets auto-updated (that's podman-auto-update(1), iykyk). These are the values it understands:

  • registry β€” needs a fully-qualified image reference (like, e.g., quay.io/podman/stable:latest) to create the container. This enforcement is genuinely necessary so Podman knows which image to actually check and pull. If you used an image ID instead? Podman has no idea which image to check/pull anymore, so like... yeah. πŸ’€
  • local β€” tells Podman to compare the image a container is using to the image with its raw name in local storage. If an image got updated locally, Podman just restarts the systemd unit that runs the container. easy.

CgroupsMode=

The cgroups mode of the Podman container. Same thing as Podman's --cgroups option.

Important tho: by default, the cgroups mode of a container made by Quadlet is split, which is different from the default (enabled) that the Podman CLI uses. πŸ™ˆ

And if the container joins a pod (as in, Pod= is specified), you might wanna change this to no-conmon or enabled so that pod-level cgroup resource limits can actually take effect.

ContainerName=

The (optional) name of the Podman container. If you don't say anything, the default value systemd-%N is used β€” that's the same as the service name but with a systemd- prefix, so it doesn't beef with user-managed containers. 🫢

ContainersConfModule=

Loads the containers.conf(5) module you point at. Equivalent to the Podman --module option.

This key can be listed multiple times.

DNS=

Sets a network-scoped DNS resolver/nameserver for containers in this network.

This key can be listed multiple times.

DNSOption=

Sets custom DNS options.

This key can be listed multiple times.

DNSSearch=

Sets custom DNS search domains. And if you want to remove the search domain? Use DNSSearch=. β€” iconic little trick. ✨

This key can be listed multiple times.

DropCapability=

Drops these capabilities from the default podman capability set β€” or all to drop literally every single capability.

Space-separated list of capabilities, and the key can be listed multiple times.

Like for example:

DropCapability=CAP_DAC_OVERRIDE CAP_IPC_OWNER

Entrypoint=

Overrides the default ENTRYPOINT from the image. Equivalent to the Podman --entrypoint option. If you've got multi-option commands, write them as a JSON string. πŸŽ€

Environment=

Sets an environment variable in the container. Uses the exact same format as services in systemd, and you can list it multiple times.

EnvironmentFile=

Uses a line-delimited file to set environment variables in the container. The path can be absolute or relative to wherever the unit file lives. This key can be used multiple times, and the order persists when it gets passed to podman run. πŸ‘‘

EnvironmentHost=

Uses the host environment inside of the container.

Exec=

Extra arguments for the container β€” this has exactly the same effect as passing more arguments after a podman run <image> <arguments> invocation.

The format is the same as for systemd command lines. But heads up: unlike the usage scenario for the similarly-named systemd ExecStart= verb, which operates on the ambient root filesystem, container images very commonly have their own ENTRYPOINT or CMD metadata, and this is what Exec= interacts with. πŸ¦‹

So like, most images ship with an ENTRYPOINT that already points at a default binary, and this field just sprinkles extra args on top of that. πŸ’«

Honestly? It's the same energy as the args field in a Kubernetes pod. If you know, you know. ✨

  • ExposeHostPort= β€” exposes a port β€” or a whole range of ports, like 50-59 β€” from the host into the container. Same thing as Podman's --expose. 🫢 You can list this key more than once, bestie.
  • GIDMap= β€” runs the container in a brand new user namespace using the GID mapping you hand it. Equivalent to Podman's --gidmap. This key can be listed multiple times, no cap.
  • GlobalArgs= β€” this one holds a list of arguments passed directly between podman and run in the generated file. It's your escape hatch for Podman features the generator doesn't otherwise support. ⚠️ But listen: since the generator has no idea what cursed interactions these args might cause, it is not recommended to use this option. Consider yourself warned, babe.

The format is a space-separated list of arguments, which you can optionally escape individually so you can sneak whitespace and other control characters in there. πŸŽ€

This key can be listed multiple times.

  • Group= β€” the (numeric) GID to run as inside the container. It doesn't have to match the GID on the host β€” that's what UserNS is for β€” but if you don't specify UserNS, then this GID gets used on the host too.
πŸ“Œ Note: when you specify both User= and Group=, they get smooshed into one single --user USER:GROUP argument handed to Podman. And using Group= without User=? That's an error, periodt. πŸ’€
  • GroupAdd= β€” assigns extra groups to the primary user running inside the container process. It also supports the special keep-groups flag. Equivalent to Podman's --group-add.
  • HealthCmd= β€” sets or alters the healthcheck command for a container. A value of none disables any existing healthchecks. Equivalent to Podman's --health-cmd. 🩺
  • HealthInterval= β€” sets the interval for the healthchecks. An interval of disable means no automatic timer gets set up at all. Equivalent to Podman's --health-interval.
  • HealthLogDestination= β€” sets the destination of the HealthCheck log. It takes a directory path, local, or events_logger (local uses the container state file). Default is local. Equivalent to Podman's --health-log-destination. πŸ“
    • local: (default) HealthCheck logs are stored in overlay containers. (For example: $runroot/healthcheck.log)
    • directory: creates a log file named <container-ID>-healthcheck.log with HealthCheck logs in the directory you specify.
    • events_logger: the log gets written with the logging mechanism set by events_logger. It also saves the log to a default directory β€” which is a whole vibe on performance if you've got a system with a huge number of logs.
  • HealthMaxLogCount= β€” sets the maximum number of attempts in the HealthCheck log file. (a '0' value means an infinite number of attempts in the log file) (Default: 5 attempts) Equivalent to Podman's --Health-max-log-count.
  • HealthMaxLogSize= β€” sets the maximum length in characters of the stored HealthCheck log. (a "0" value means an infinite log length) (Default: 500 characters) Equivalent to Podman's --Health-max-log-size.
  • HealthOnFailure= β€” the action to take once the container transitions to an unhealthy state. The "kill" action in combination integrates best with systemd: once the container turns unhealthy it gets killed, and systemd restarts the service. Iconic teamwork honestly. πŸ”₯ Equivalent to Podman's --health-on-failure.
  • HealthRetries= β€” the number of retries allowed before a healthcheck is considered unhealthy. Equivalent to Podman's --health-retries.
  • HealthStartPeriod= β€” the initialization time a container needs to bootstrap itself. Equivalent to Podman's --health-start-period.
  • HealthStartupCmd= β€” sets a startup healthcheck command for a container. Equivalent to Podman's --health-startup-cmd.
  • HealthStartupInterval= β€” sets an interval for the startup healthcheck. An interval of disable results in no automatic timer setup. Equivalent to Podman's --health-startup-interval.
  • HealthStartupRetries= β€” the number of attempts allowed before the startup healthcheck restarts the container. Equivalent to Podman's --health-startup-retries.
  • HealthStartupSuccess= β€” the number of successful runs required before the startup healthcheck succeeds and the regular healthcheck kicks in. Equivalent to Podman's --health-startup-success.
  • HealthStartupTimeout= β€” the maximum time a startup healthcheck command has to complete before it's marked as failed. Equivalent to Podman's --health-startup-timeout.
  • HealthTimeout= β€” the maximum time allowed to complete the healthcheck before an interval is considered failed. Equivalent to Podman's --health-timeout.
  • HostName= β€” sets the host name that's available inside the container. Equivalent to Podman's --hostname. 🏷️
  • HttpProxy= β€” controls whether proxy environment variables (http_proxy, https_proxy, ftp_proxy, no_proxy) get passed from the Podman process into the container during image pulls and builds.

Set it to true to enable proxy inheritance (that's the default Podman behavior) or false to disable it. This one is especially useful on systems that need proxy config to reach the internet but don't want those proxy settings leaking into the container runtime. Equivalent to Podman's --http-proxy. 🌐

  • Image= β€” the image to run in the container. Highly recommend using a fully qualified image name instead of a short name β€” both for performance and for robustness reasons. Trust. πŸ’…

The format of the name is the same as when you pass it to podman pull, so it supports :tag or digests to guarantee that specific image version. πŸ‘‘

Special cases, bestie:

  • If the name of the image ends with .image, Quadlet uses the image pulled by the corresponding .image file, and the generated systemd service contains a dependency on the $name-image.service (or whatever service name is set in the .image file). Note that the corresponding .image file must exist. 🫑
  • If the name of the image ends with .build, Quadlet uses the image built by the corresponding .build file, and the generated systemd service contains a dependency on the $name-build.service. Note: the corresponding .build file must exist.
  • ImageVolume= β€” tells Podman how to handle the builtin image volumes. Default is bind. Equivalent to Podman's --image-volume.
  • IP= β€” specify a static IPv4 address for the container, for example 10.88.64.128. Equivalent to Podman's --ip.
  • IP6= β€” specify a static IPv6 address for the container, for example fd46:db93:aa76:ac37::10. Equivalent to Podman's --ip6. ✨
  • Label= β€” sets one or more OCI labels on the container. The format is a list of key=value items, similar to Environment. 🏷️

This key can be listed multiple times.

  • LogDriver= β€” sets the log-driver Podman uses when running the container. Equivalent to Podman's --log-driver.
  • LogOpt= β€” sets the log-opt (logging options) Podman uses when running the container. Equivalent to Podman's --log-opt. This key can be listed multiple times.
  • Mask= β€” specify the paths to mask, separated by a colon: Mask=/path/1:/path/2. A masked path cannot be accessed inside the container. πŸ™ˆ
  • Memory= β€” specify the amount of memory for the container.
  • Mount= β€” attaches a filesystem mount to the container. This is equivalent to Podman's --mount option, and generally has the form type=TYPE,TYPE-SPECIFIC-OPTION[,...].

Special cases:

  • For type=volume, if source ends with .volume, the Podman named volume generated by the corresponding .volume file is used.
  • For type=image, if source ends with .image, the image generated by the corresponding .image file is used.

In both those cases, the generated systemd service will contain a dependency on the service generated for the corresponding unit. Note: the corresponding .volume or .image file must exist. 🫢

This key can be listed multiple times.

  • Network= β€” specify a custom network for the container. Same format as the --network option to podman run. For example, use host to use the host network in the container, or none to not set up networking in the container at all. 🌈

Special cases:

  • If the name of the network ends with .network, a Podman network called systemd-$name is used, and the generated systemd service contains a dependency on the $name-network.service. A network like that can be automatically created by using a $name.network Quadlet file. Note: the corresponding .network file must exist.
  • If the name ends with .container, the container will reuse the network stack of another container created by $name.container. The generated systemd service contains a dependency on $name.service. Note: the corresponding .container file must exist.

This key can be listed multiple times.

  • NetworkAlias= β€” adds a network-scoped alias for the container. Same format as the --network-alias option to podman run. Aliases can group containers together in DNS resolution: for example, setting NetworkAlias=web on multiple containers makes a DNS query for web resolve to all the containers rockin' that alias. πŸ•ΈοΈ

This key can be listed multiple times.

  • NoNewPrivileges= (defaults to false) β€” if enabled, this disables container processes from gaining additional privileges via things like setuid and file capabilities. πŸ”’
  • Notify= (defaults to false) β€” by default, Podman is run in such a way that the systemd startup notify command is handled by the container runtime. In other words, the service is deemed started when the container runtime starts the child in the container. But! If the container application supports sd_notify, then setting Notify to true passes the notification details to the container, letting it notify of startup all on its own. main character energy, honestly. πŸ’…

Also, setting Notify to healthy will postpone startup notifications until such time as the container is marked healthy, as determined by Podman healthchecks. Note that this requires setting up a container healthcheck β€” see the HealthCmd option for more. 🩺

  • PidsLimit= β€” tunes the container's pids limit. This is equivalent to Podman's --pids-limit.
  • Pod= β€” specify a Quadlet .pod unit to link the container to. The value must take the form of <name>.pod and the .pod unit must exist. πŸ«›

Quadlet will add all the necessary parameters to link between the container and the pod, and between their corresponding services.

  • PodmanArgs= β€” a list of arguments that get passed straight to the end of the podman run command in the generated file (right before the image name in the command line). It's your little backdoor into Podman features the generator doesn't support yet. But bestie, the generator has no idea what unexpected interactions your args might cause, so this one is not recommended 🫣. Format: a space separated list of arguments, and you can optionally escape each one individually so you can sneak in whitespace and other control characters. Can be listed multiple times.
  • PublishPort= β€” exposes a port, or a range of ports (e.g. 50-59), from the container to the host. Equivalent to the Podman --publish option. Format is similar to the Podman options, which goes like ip:hostPort:containerPort, ip::containerPort, hostPort:containerPort or containerPort, and the number of host and container ports must match (in the case of a range). You can slap the protocol on the end, e.g. hostPort:containerPort/tcp. Valid protocols are tcp and udp; the sctp protocol is supported only for rootful containers. If the IP is set to 0.0.0.0 or not set at all, the port is bound on all IPv4 addresses on the host β€” use [::] for IPv6 πŸ’….
Heads up: not listing a host port means Podman automatically selects one, and it may be different for each invocation of the service. That makes it a less useful option, ngl. The allocated port can be found with the podman port command.

This key can be listed multiple times.

  • Pull= β€” sets the image pull policy. Equivalent to the Podman --pull option ✨.
  • ReadOnly= (defaults to false) β€” if enabled, makes the image read-only.
  • ReadOnlyTmpfs= (defaults to true) β€” if ReadOnly is set to true, mounts a read-write tmpfs on /dev, /dev/shm, /run, /tmp, and /var/tmp.
  • ReloadCmd= β€” adds an ExecReload line to the Service that runs podman exec with this command in this container. To actually execute the reload, run systemctl reload <Service>. Mutually exclusive with ReloadSignal 🚫.
  • ReloadSignal= β€” adds an ExecReload line to the Service that runs podman kill with this signal, which sends the signal to the main container process. To actually execute the reload, run systemctl reload <Service>. Mutually exclusive with ReloadCmd 🚫.
  • Retry= β€” number of times to retry the image pull when a HTTP error occurs. Equivalent to the Podman --retry option.
  • RetryDelay= β€” delay between retries. Equivalent to the Podman --retry-delay option.
  • Rootfs= β€” the rootfs to use for the container. Rootfs points to a directory on the system that contains the content to be run within the container. This option conflicts with the Image option 😬.

The format of the rootfs is the same as when passed to podman run --rootfs, so it supports overlay mounts as well.

Note: on SELinux systems, the rootfs needs the correct label, which is by default unconfined_u:object_r:container_file_t:s0.
  • RunInit= (defaults to false) β€” if enabled, the container has a minimal init process inside the container that forwards signals and reaps processes. Cleanup crew energy 🧹.
  • SeccompProfile= β€” set the seccomp profile to use in the container. If unset, the default podman profile is used. Set it to either the pathname of a JSON file, or unconfined to disable the seccomp filters.
  • Secret= β€” use a Podman secret in the container either as a file or an environment variable. Equivalent to the Podman --secret option and generally has the form secret[,opt=opt ...].
  • SecurityLabelDisable= β€” turn off label separation for the container.
  • SecurityLabelFileType= β€” set the label file type for the container files.
  • SecurityLabelLevel= β€” set the label process level for the container processes.
  • SecurityLabelNested= β€” allow SecurityLabels to function within the container. This allows separation of containers created within the container πŸͺ†.
  • SecurityLabelType= β€” set the label process type for the container processes.
  • ServiceName= β€” by default, Quadlet names the systemd service unit using the name of the Quadlet. Setting this key overrides that behavior by instructing Quadlet to use the provided name instead.
Note: the name should not include the .service file extension.
  • ShmSize= β€” size of /dev/shm. Equivalent to the Podman --shm-size option and generally has the form number[unit].
  • StartWithPod= β€” start the container after the associated pod is created. Defaults to true.

If true, the container will be started/stopped/restarted alongside the pod.

If false, the container will not be started when the pod starts. The container will be stopped with the pod. Restarting the pod will also restart the container as long as the container was also running before.

Note: the container can still be started manually or through a target by configuring the [Install] section. The pod will be started as needed in any case.
  • StopSignal= β€” signal to stop a container. Default is SIGTERM. Equivalent to the Podman --stop-signal option.
  • StopTimeout= β€” seconds to wait before forcibly stopping the container.
Note: this value should be lower than the actual systemd unit timeout to make sure the podman rm command is not killed by systemd πŸ‘€.

Equivalent to the Podman --stop-timeout option.

  • SubGIDMap= β€” run the container in a new user namespace using the map with name in the /etc/subgid file. Equivalent to the Podman --subgidname option.
  • SubUIDMap= β€” run the container in a new user namespace using the map with name in the /etc/subuid file. Equivalent to the Podman --subuidname option.
  • Sysctl= β€” configures namespaced kernel parameters for the container. The format is Sysctl=name=value. This is a space separated list of kernel parameters. This key can be listed multiple times. For example:
Sysctl=net.ipv6.conf.all.disable_ipv6=1 net.ipv6.conf.all.use_tempaddr=1
  • Timezone= (if unset uses system-configured default) β€” the timezone to run the container in πŸ•°οΈ.
  • Tmpfs= β€” mount a tmpfs in the container. Equivalent to the Podman --tmpfs option, and generally has the form CONTAINER-DIR[:OPTIONS]. This key can be listed multiple times.
  • UIDMap= β€” run the container in a new user namespace using the supplied UID mapping. Equivalent to the Podman --uidmap option. This key can be listed multiple times.
  • Ulimit= β€” Ulimit options. Sets the ulimits values inside the container. This key can be listed multiple times.
  • Unmask= β€” specify the paths to unmask, separated by a colon: unmask=ALL or /path/1:/path/2, or shell expanded paths (/proc/*). If set to ALL, Podman will unmask all the paths that are masked or made read-only by default.

The default masked paths are /proc/acpi, /proc/kcore, /proc/keys, /proc/latency_stats, /proc/sched_debug, /proc/scsi, /proc/timer_list, /proc/timer_stats, /sys/firmware, and /sys/fs/selinux.

The default paths that are read-only are /proc/asound, /proc/bus, /proc/fs, /proc/irq, /proc/sys, /proc/sysrq-trigger, /sys/fs/cgroup.

  • User= β€” the (numeric) UID to run as inside the container. This does not need to match the UID on the host, which can be modified with UserNS, but if that is not specified, this UID is also used on the host.
Note: when both User= and Group= are specified, they are combined into a single --user USER:GROUP argument passed to Podman.
  • UserNS= β€” set the user namespace mode for the container. Equivalent to the Podman --userns option and generally has the form MODE[:OPTIONS,...].
  • Volume= β€” mount a volume in the container. Equivalent to the Podman --volume option, and generally has the form [[SOURCE-VOLUME|HOST-DIR:]CONTAINER-DIR[:OPTIONS]].

If SOURCE-VOLUME starts with ., Quadlet resolves the path relative to the location of the unit file 🎯.

Special case:

  • If SOURCE-VOLUME ends with .volume, a Podman named volume called systemd-$name is used as the source, and the generated systemd service contains a dependency on the $name-volume.service. Note that the corresponding .volume file must exist.

This key can be listed multiple times.

  • WorkingDir= β€” working directory inside the container. The default working directory for running binaries within a container is the root directory (/). The image developer can set a different default with the WORKDIR instruction. This option overrides the working directory by using the -w option.

πŸ«› Pod units [Pod] πŸ«›

Pod units are named with a .pod extension and contain a [Pod] section describing the pod that is created and run as a service. The resulting service file contains a line like ExecStartPre=podman pod create …, and most of the keys in this section control the command-line options passed to Podman.

By default, the Podman pod has the same name as the unit, but with a systemd- prefix, i.e. a $name.pod file creates a $name-pod.service unit and a systemd-$name Podman pod. The PodName option allows for overriding this default name with a user-provided one ✨.

The generated service defaults to Restart=on-failure. Note that with ExitPolicy=stop (the default for Quadlet pods), the pod exits cleanly (exit code 0) when all its containers stop, so on-failure will not trigger a restart in that case. To have the pod automatically restart when containers exit, set Restart=always in the [Service] section of the .pod file πŸ’….

Valid options for [Pod] are listed below:

Ok so here's the cheat sheet for how every [Pod] key lines up with the real podman pod create flag πŸ’… (left side = what you write in the unit, right side = what Quadlet turns it into, iykyk):

  • AddHost=example.com:192.168.10.11 β†’ --add-host example.com:192.168.10.11
  • ContainersConfModule=/etc/nvd.conf β†’ --module=/etc/nvd.conf
  • DNS=192.168.55.1 β†’ --dns=192.168.55.1
  • DNSOption=ndots:1 β†’ --dns-option=ndots:1
  • DNSSearch=example.com β†’ --dns-search example.com
  • ExitPolicy=stop β†’ --exit-policy stop
  • GIDMap=0:10000:10 β†’ --gidmap=0:10000:10
  • GlobalArgs=--log-level=debug β†’ --log-level=debug
  • HostName=name β†’ --hostname=name
  • IP=192.5.0.1 β†’ --ip 192.5.0.1
  • IP6=2001:db8::1 β†’ --ip6 2001:db8::1
  • Label="XYZ" β†’ --label "XYZ"
  • Network=host β†’ --network host
  • NetworkAlias=name β†’ --network-alias name
  • PodmanArgs=--cpus=2 β†’ --cpus=2
  • PodName=name β†’ --name=name
  • PublishPort=8080:80 β†’ --publish 8080:80
  • ServiceName=name β†’ Name the systemd unit name.service
  • ShmSize=100m β†’ --shm-size=100m
  • StopTimeout=20 β†’ --time=20
  • SubGIDMap=gtest β†’ --subgidname=gtest
  • SubUIDMap=utest β†’ --subuidname=utest
  • UIDMap=0:10000:10 β†’ --uidmap=0:10000:10
  • UserNS=keep-id:uid=200,gid=210 β†’ --userns keep-id:uid=200,gid=210
  • Volume=/source:/dest β†’ --volume /source:/dest

🫢 Supported keys in the [Pod] section

Ok bestie, here's every single key, what it does, and whether you can stack it. Let's go πŸ’–

AddHost=

Adds a host-to-IP mapping into /etc/hosts. Format is hostname:ip. No cap, this is literally the same as the Podman --add-host option.

You can list this key multiple times πŸŽ€

ContainersConfModule=

Loads the containers.conf(5) module you point at. Same energy as the Podman --module option.

Also stackable β€” this key can be listed multiple times ✨

DNS=

Sets a network-scoped DNS resolver/nameserver for the containers inside this pod.

You guessed it: this key can be listed multiple times πŸ¦‹

DNSOption=

Sets custom DNS options.

Multiple times, bestie πŸ’…

DNSSearch=

Sets custom DNS search domains. Wanna nuke the search domain? Set DNSSearch=. and it's gone, iconic.

Can be listed multiple times 🌸

ExitPolicy=

Sets the exit policy of the pod for when the last container dips out. Default for quadlets is stop.

Want the pod to stay alive instead? Set ExitPolicy=continue. That's the whole vibe 🌈

GIDMap=

Creates the pod inside a shiny new user namespace using the GID mapping you supply. Equivalent to the Podman --gidmap option.

Listable multiple times, fr fr ⭐

GlobalArgs=

This one holds a list of arguments that get passed directly between podman and pod in the generated file. It's the escape hatch for reaching Podman features the generator otherwise doesn't support.

⚠️ Okay so real talk: since the generator has zero clue what kind of unhinged interactions these arguments might cause, using this option is not recommended. You've been warned πŸ’€

The format is a space-separated list of arguments, and each one can optionally be individually escaped so you can sneak whitespace and other control characters in there.

This key can be listed multiple times 🧚

HostName=

Sets the pod's hostname inside all the containers. Cute, right?

That hostname also gets added to /etc/hosts using the container's primary IP address (peep the --add-host option too πŸ‘€).

Equivalent to the Podman --hostname option.

IP=

Pins a static IPv4 address for the pod, like 10.88.64.128. Equivalent to the Podman --ip option.

IP6=

Pins a static IPv6 address for the pod, like fd46:db93:aa76:ac37::10. Equivalent to the Podman --ip6 option.

Label=

Slaps one or more OCI labels on the pod. Format is a list of key=value items, same style as Environment.

Multiple times, always πŸ’œ

Network=

Points the pod at a custom network. Same format as the --network option to podman pod create. So like, use host to give the pod the host network, or none to not set up networking in the pod at all.

Special case, pay attention bestie:

  • If the network name ends with .network, Quadlet goes looking for the matching .network Quadlet unit. If it finds one, Quadlet uses the name of the Network set in the Unit β€” and if there isn't one, it falls back to systemd-$name.

The generated systemd service gets a dependency on the service unit generated for that .network unit.

πŸŽ€ Note: the corresponding .network file must exist.

This key can be listed multiple times πŸ¦‹

  • NetworkAlias= β€” gives your pod a network-scoped alias, bestie. Same format as the --network-alias option to podman pod create. The whole point of aliases is grouping stuff together in DNS resolution β€” so like, if you set NetworkAlias=web on multiple containers, a DNS query for web resolves to all the containers with that alias. iconic group-chat behavior tbh πŸ’…

This key can be listed multiple times.

  • PodmanArgs= β€” this one is just a big list of arguments shoved directly onto the end of the podman pod create command in the generated file. It's your backstage pass to Podman features the generator otherwise doesn't support. BUT β€” and I cannot stress this enough β€” the generator has zero clue what chaotic unexpected interactions these arguments might cause, so using this option is not recommended. 🫣

The format is a space separated list of arguments, and you can optionally escape them individually so you can sneak in whitespace and other control characters.

This key can be listed multiple times.

  • PodName= β€” the (optional) name of the Podman pod. If you don't specify it, the default value is the same name as the unit, but with a systemd- prefix β€” so a $name.pod file creates a systemd-$name Podman pod, specifically to avoid conflicts with user-managed pods. ✨

Heads up though: pods and containers cannot have the same name. So if PodName is set, it must not conflict with any container. no cap.

  • PublishPort= β€” exposes a port, or a range of ports (e.g. 50-59), from the pod to the host. Equivalent to the Podman --publish option. The format is similar to the Podman options, which is of the form ip:hostPort:containerPort, ip::containerPort, hostPort:containerPort or containerPort β€” and the number of host and container ports must be the same (in the case of a range). You can slap the protocol at the end, e.g., hostPort:containerPort/tcp. Valid protocols are tcp and udp; the sctp protocol is supported only for rootful containers. 🧚

If the IP is set to 0.0.0.0 or not set at all, the port is bound on all IPv4 addresses on the host; use [::] for IPv6.

Note that not listing a host port means that Podman automatically selects one, and it may be different for each invocation of service. That makes it a less useful option, ngl. The allocated port can be found with the podman port command.

When using host networking via Network=host, the PublishPort= option cannot be used.

This key can be listed multiple times.

  • ServiceName= β€” by default, Quadlet will name the systemd service unit by appending -pod to the name of the Quadlet. Setting this key overrides that behavior by instructing Quadlet to use the provided name instead. πŸŽ€

Note, the name should not include the .service file extension.

  • ShmSize= β€” size of /dev/shm. Equivalent to the Podman --shm-size option and generally has the form number[unit].
  • StopTimeout= β€” sets the time in seconds to wait for the pod to gracefully stop. This value is equivalent to the --time argument in the podman pod stop command when the service is stopped. After this period expires, any running containers in the pod are forcibly killed. 😭 dramatic but necessary.
  • SubGIDMap= β€” creates the pod in a new user namespace using the map with name in the /etc/subgid file. Equivalent to the Podman --subgidname option.
  • SubUIDMap= β€” creates the pod in a new user namespace using the map with name in the /etc/subuid file. Equivalent to the Podman --subuidname option.
  • UIDMap= β€” creates the pod in a new user namespace using the supplied UID mapping. Equivalent to the Podman --uidmap option.

This key can be listed multiple times.

  • UserNS= β€” sets the user namespace mode for the pod. Equivalent to the Podman --userns option and generally has the form MODE[:OPTIONS,...].
  • Volume= β€” mounts a volume in the pod. Equivalent to the Podman --volume option, and generally has the form [[SOURCE-VOLUME|HOST-DIR:]CONTAINER-DIR[:OPTIONS]]. πŸ“

If SOURCE-VOLUME starts with ., Quadlet resolves the path relative to the location of the unit file.

Special case:

  • If SOURCE-VOLUME ends with .volume, Quadlet will look for the corresponding .volume Quadlet unit. If found, Quadlet will use the name of the Volume set in the Unit, otherwise, systemd-$name is used. Note: the corresponding .volume file must exist.

The generated systemd service contains a dependency on the service unit generated for that .volume unit, or on $name-volume.service if the .volume unit is not found.

This key can be listed multiple times.


🐝 Kube units [Kube]

Kube units are named with a .kube extension and contain a [Kube] section describing how podman kube play runs as a service. The resulting service file contains a line like ExecStart=podman kube play … file.yml, and most of the keys in this section control the command-line options passed to Podman. However, some options also affect the details of how systemd is set up to run and interact with the container. it's giving very hands-on, love that for us. πŸ’–

There is only one required key, Yaml, which defines the path to the Kubernetes YAML file. everything else is optional bestie, breathe.

Valid options for [Kube] are listed below:

Okay bestie, so before we get into the keys one by one β€” here's the cheat sheet of how every [Kube] key lines up with its podman kube play equivalent, because seeing them side by side is so much easier to remember πŸ’…

  • AutoUpdate=registry β†’ --annotation "io.containers.autoupdate=registry"
  • ConfigMap=/tmp/config.map β†’ --config-map /tmp/config.map
  • ContainersConfModule=/etc/nvd.conf β†’ --module=/etc/nvd.conf
  • ExitCodePropagation=how β†’ How to propagate container error status
  • GlobalArgs=--log-level=debug β†’ --log-level=debug
  • KubeDownForce=true β†’ --force (for podman kube down)
  • LogDriver=journald β†’ --log-driver journald
  • Network=host β†’ --network host
  • PodmanArgs=--annotation=key=value β†’ --annotation=key=value
  • PublishPort=8080:80 β†’ --publish 8080:80
  • ServiceName=name β†’ Name the systemd unit name.service
  • SetWorkingDirectory=yaml β†’ Set WorkingDirectory of unit file to location of the YAML file
  • UserNS=keep-id:uid=200,gid=210 β†’ --userns keep-id:uid=200,gid=210
  • Yaml=/tmp/kube.yaml β†’ podman kube play /tmp/kube.yaml

Now the actual tea β˜• β€” the supported keys in the [Kube] section are:

🌈 AutoUpdate=

Tells you whether the containers get auto-updated (that's podman-auto-update(1) doing its thing ✨). And yup, you can drop AutoUpdate in there multiple times, she's flexible. Accepted values, bestie:

  • registry β€” needs a fully-qualified image reference (like quay.io/podman/stable:latest) to create the container. This is literally required so Podman knows which images to actually check and pull. If you used an image ID instead, Podman has no idea which image to check/pull anymore. Sorry, them's the rules πŸ™ˆ
  • local β€” tells Podman to compare the image a container is using against the image with its raw name in local storage. If an image got updated locally, Podman just restarts the systemd unit running the Kubernetes Quadlet. Slay πŸŽ€
  • name/(local|registry) β€” tells Podman to do the local or registry autoupdate on the container name you specified.

πŸ¦‹ ConfigMap=

Passes the Kubernetes ConfigMap YAML path over to podman kube play via the --configmap argument. Small plot twist though: unlike the configmap argument, this value can only hold one path β€” but that path can be absolute or relative to the location of the unit file. No cap πŸ’–

This key may be used multiple times.

🧚 ContainersConfModule=

Loads the containers.conf(5) module you point at. Equivalent to the Podman --module option. Iconic load order behaviour, honestly.

This key can be listed multiple times.

πŸ‘‘ ExitCodePropagation=

Controls how the main PID of the systemd service should exit. Values that get the green light:

  • all β€” exits non-zero if all containers have failed (i.e., exited non-zero)
  • any β€” exits non-zero if any container has failed
  • none β€” exits zero and ignores failed containers

The current default value is none. That's the vibe rn πŸ’…

🫢 GlobalArgs=

This key holds a list of arguments that get passed directly between podman and kube in the generated file. It's your back door to Podman features the generator otherwise doesn't support. But real talk: since the generator has zero clue what unhinged interactions these arguments might cause, it is not recommended to use this option. You've been warned, bestie 😭

The format is a space separated list of arguments, which can optionally be individually escaped so you can sneak in whitespace and other control characters.

This key can be listed multiple times.

πŸ’€ KubeDownForce=

Removes all resources, including volumes, when you call podman kube down. Equivalent to the Podman --force option. Like, it's giving "burn it all down" energy β€” make sure that's what you actually want πŸ”₯

⭐ LogDriver=

Sets the log-driver Podman uses when running the container. Equivalent to the Podman --log-driver option. Simple, clean, does what it says πŸŽ€

🌸 Network=

Specifies a custom network for the container. Same format as the --network option to podman kube play. For example, use host to use the host network in the container, or none to not set up networking in the container at all. Hermit mode 🐚

Special case, pay attention:

  • If the name of the network ends with .network, a Podman network called systemd-$name gets used, and the generated systemd service contains a dependency on $name-network.service. You can get such a network auto-created by using a $name.network Quadlet file. Note: the corresponding .network file must exist. No file, no network, no mercy πŸ’…

This key can be listed multiple times.

πŸ“ PodmanArgs=

This key holds a list of arguments passed directly to the end of the podman kube play command in the generated file (right before the path to the yaml file in the command line). It's how you reach Podman features the generator doesn't support on its own. But again β€” since the generator is unaware of what unexpected interactions these arguments can cause, it is not recommended to use this option. Same warning, same energy πŸ™ˆ

The format is a space separated list of arguments, which can optionally be individually escaped to allow inclusion of whitespace and other control characters.

This key can be listed multiple times.

πŸ’œ PublishPort=

Exposes a port β€” or a whole range of ports (e.g. 50-59) β€” from the container to the host. Equivalent to podman kube play's --publish option. The format is similar to the Podman options, which is of the form ip:hostPort:containerPort, ip::containerPort, hostPort:containerPort or containerPort, where the number of host and container ports must be the same (in the case of a range). You can tack the protocol on at the end, e.g. hostPort:containerPort/tcp. Valid protocols are tcp and udp; the sctp protocol is supported only for rootful containers. Rootful girlies only for that one 🫢

If the IP is set to 0.0.0.0 or not set at all, the port is bound on all IPv4 addresses on the host; use [::] for IPv6.

The list of published ports specified in the unit file is merged with the list of ports specified in the Kubernetes YAML file. If the same container port and protocol shows up in both, the entry from the unit file takes precedence. Unit file stays winning πŸ‘‘

This key can be listed multiple times.

  • ServiceName= β€” lowkey one of the handiest keys ever πŸ’… by default Quadlet names the systemd service unit after the Quadlet itself, but set this and you get to choose the name instead. Main character behavior, honestly.
Heads up bestie: the name should not include the .service file extension, ok?
  • SetWorkingDirectory= β€” this sets the WorkingDirectory field of the Service group in the systemd service unit file. Why do you care? Because it lets podman kube play actually resolve relative paths correctly πŸ™Œ Supported values are yaml and unit, which set the working directory to that of the YAML file or the Quadlet unit file respectively.

Alternatively, you can just explicitly set the WorkingDirectory field of the Service group in the .kube file yourself, no cap. But like β€” please note that if the WorkingDirectory field of the Service group is set, Quadlet will not set it, even if SetWorkingDirectory is set. It respects your choices, periodt.

Special case: if multiple YAML paths are provided, only unit is supported. 😭

  • UserNS= β€” sets the user namespace mode for the container. This is equivalent to the Podman --userns option and generally has the form MODE[:OPTIONS,...].
  • Yaml= β€” the path to the Kubernetes YAML file to use. Can be absolute, or relative to the location of the unit file.

This key can be listed multiple times. ✨


πŸ•ΈοΈ Network units [Network] πŸ•ΈοΈ

Network files are named with a .network extension and contain a section [Network] describing the named Podman network. The generated service is a one-time command that makes sure the network exists on the host, creating it if needed. It's giving "set it and forget it" energy, fr fr.

By default, the Podman network has the same name as the unit, but with a systemd- prefix β€” so for a network file named $NAME.network, the generated Podman network is called systemd-$NAME, and the generated service file is $NAME-network.service. The NetworkName option lets you override this default name with one you provide yourself πŸ’–

So here's the thing about updating network parameters: you will first need to manually remove the podman network and then restart the service. Set NetworkDeleteOnStop=true to override that behavior and delete the network when the device is stopped. Obsessed with a clean exit tbh.

Using network units lets containers depend on networks being automatically pre-created. This is particularly interesting when you're using special options to control network creation, because Podman otherwise creates networks with the default options. iykyk.

Valid options for [Network] are listed below β€” here's the whole glow-up chart of "[Network] option ➑️ podman network create equivalent":

  • ContainersConfModule=/etc/nvd.conf ➑️ --module=/etc/nvd.conf
  • DisableDNS=true ➑️ --disable-dns
  • DNS=192.168.55.1 ➑️ --dns=192.168.55.1
  • Driver=bridge ➑️ --driver bridge
  • Gateway=192.168.55.3 ➑️ --gateway 192.168.55.3
  • GlobalArgs=--log-level=debug ➑️ --log-level=debug
  • InterfaceName=enp1 ➑️ --interface-name enp1
  • Internal=true ➑️ --internal
  • IPAMDriver=dhcp ➑️ --ipam-driver dhcp
  • IPRange=192.168.55.128/25 ➑️ --ip-range 192.168.55.128/25
  • IPv6=true ➑️ --ipv6
  • Label="XYZ" ➑️ --label "XYZ"
  • NetworkDeleteOnStop=true ➑️ Add ExecStopPost to delete the network when the unit is stopped
  • NetworkName=foo ➑️ podman network create foo
  • Options=isolate=true ➑️ --opt isolate=true
  • PodmanArgs=--dns=192.168.55.1 ➑️ --dns=192.168.55.1
  • ServiceName=name ➑️ Name the systemd unit name.service
  • Subnet=192.5.0.0/16 ➑️ --subnet 192.5.0.0/16

Supported keys in the [Network] section are:

πŸ’« ContainersConfModule=

Load the specified containers.conf(5) module. Equivalent to the Podman --module option.

This key can be listed multiple times. πŸŽ€

πŸ’« DisableDNS= (defaults to false)

If enabled, disables the DNS plugin for this network.

This is equivalent to the Podman --disable-dns option.

πŸ’« DNS=

Set network-scoped DNS resolver/nameserver for containers in this network.

This key can be listed multiple times.

πŸ’« Driver= (defaults to bridge)

Driver to manage the network. Currently bridge, macvlan and ipvlan are supported.

This is equivalent to the Podman --driver option.

πŸ’« Gateway=

Define a gateway for the subnet. If you want to provide a gateway address, you must also provide a subnet option β€” they're a package deal, bestie 🫢

This is equivalent to the Podman --gateway option.

This key can be listed multiple times.

πŸ’« GlobalArgs=

This key contains a list of arguments passed directly between podman and network in the generated file. It can be used to access Podman features otherwise unsupported by the generator. But β€” since the generator is completely unaware of what unexpected interactions these arguments can cause, it is not recommended to use this option. πŸ™ˆ It's giving "do it if you must, but don't come crying to me."

The format of this is a space separated list of arguments, which can optionally be individually escaped to allow inclusion of whitespace and other control characters.

This key can be listed multiple times.

πŸ’« InterfaceName=

This option maps the network_interface option in the network config, see podman network inspect. Depending on the driver, this can have different effects: for bridge, it uses the bridge interface name. For macvlan and ipvlan, it is the parent device on the host. It is the same as --opt parent=....

This is equivalent to the Podman --interface-name option.

πŸ’« Internal= (defaults to false)

Restrict external access of this network. ✨

This is equivalent to the Podman --internal option.

πŸ’« IPAMDriver=

Set the ipam driver (IP Address Management Driver) for the network. Currently host-local, dhcp and none are supported.

This is equivalent to the Podman --ipam-driver option.

πŸ’« IPRange=

Allocate container IP from a range. The range must be either a complete subnet in CIDR notation, or be in the <startIP>-<endIP> syntax, which allows for a more flexible range compared to the CIDR subnet. The ip-range option must be used with a subnet option.

This is equivalent to the Podman --ip-range option.

This key can be listed multiple times.

πŸ’« IPv6=

Enable IPv6 (Dual Stack) networking.

This is equivalent to the Podman --ipv6 option.

🏷️ Label=

Sets one or more OCI labels on the network, bestie. The format is a list of key=value items β€” similar to Environment, iykyk. πŸ’…

This key can be listed multiple times. ✨

πŸ’€ NetworkDeleteOnStop= (defaults to false)

Set it to true and the network gets deleted when the service is stopped. That's literally it. Poof. Gone. No cap. 😭

πŸŽ€ NetworkName=

The (optional) name of the Podman network. If you don't specify it, the default is the same name as the unit β€” but with a systemd- prefix 🫢. So like, a $name.network file creates a systemd-$name Podman network, and the whole reason is to avoid conflicts with user-managed network. Iconic behaviour, honestly. πŸ‘‘

🌸 Options=

Set driver specific options.

This is equivalent to the Podman --opt option.

πŸ™ˆ PodmanArgs=

This key contains a list of arguments passed directly to the end of the podman network create command in the generated file (right before the name of the network in the command line). It can be used to access Podman features otherwise unsupported by the generator.

ok so listen β€” since the generator is unaware of what unexpected interactions can be caused by these arguments, it is not recommended to use this option. fr fr, be careful. 🚨

The format of this is a space separated list of arguments, which can optionally be individually escaped to allow inclusion of whitespace and other control characters.

This key can be listed multiple times. ⭐

πŸ¦‹ ServiceName=

By default, Quadlet will name the systemd service unit by appending -network to the name of the Quadlet. Setting this key overrides this behavior by instructing Quadlet to use the provided name you gave it. πŸ’–

Note, the name should not include the .service file extension.

🌈 Subnet=

The subnet in CIDR notation. That's the vibe check. ✨

This is equivalent to the Podman --subnet option.

This key can be listed multiple times. πŸ”₯

πŸ’Ύ Volume units [Volume] πŸ’Ύ

Volume files are named with the .volume extension and contain a section [Volume] that describes the named Podman volume. The generated service is a one-time command that makes sure the volume exists on the host, creating it if needed. πŸ’…

By default, the Podman volume has the same name as the unit, but with a systemd- prefix. So for a volume file named $NAME.volume, the generated Podman volume is called systemd-$NAME, and the generated service file is $NAME-volume.service. The VolumeName option lets you override this default name with a user-provided one. ✨

Using volume units lets containers depend on volumes being automatically pre-created. This is especially interesting when you’re using special options to control volume creation, because Podman otherwise creates volumes with the default options. 🫢

Valid options for [Volume] are listed below. Mapping time, bestie β€” here’s the [Volume] option to podman volume create equivalent glow-up: πŸ‘‘

  • ContainersConfModule=/etc/nvd.conf β€” --module=/etc/nvd.conf
  • Copy=true β€” --opt copy
  • Device=tmpfs β€” --opt device=tmpfs
  • Driver=image β€” --driver=image
  • GID=5678 β€” --gid 5678
  • GlobalArgs=--log-level=debug β€” --log-level=debug
  • Group=192 β€” --opt "o=group=192"
  • Image=quay.io/centos/centos:latest β€” --opt image=quay.io/centos/centos:latest
  • Label="foo=bar" β€” --label "foo=bar"
  • Options=XYZ β€” --opt "o=XYZ"
  • PodmanArgs=--driver=image β€” --driver=image
  • ServiceName=name β€” Name the systemd unit name.service
  • Type=type β€” Filesystem type of Device
  • UID=1234 β€” --uid 1234
  • User=123 β€” --opt "o=uid=123"
  • VolumeName=foo β€” podman volume create foo

Supported keys in the [Volume] section are: 🌸

ContainersConfModule= 🧚

Load the specified containers.conf(5) module. Equivalent to the Podman --module option.

This key can be listed multiple times.

Copy= (default to true) πŸŽ€

If enabled, the content of the image located at the mountpoint of the volume is copied into the volume on the first run.

Device= πŸ’–

The path of a device which is mounted for the volume.

Driver= πŸ”₯

Specify the volume driver name. When set to image, the Image key must also be set.

This is equivalent to the Podman --driver option.

GID= πŸ‘‘

The GID that the volume will be created as. Differently than Group=, the specified value is not passed to the mount operation. The specified GID will own the volume's mount point directory and affects the volume chown operation.

GlobalArgs= 🌈

This key contains a list of arguments passed directly between podman and volume in the generated file. It can be used to access Podman features otherwise unsupported by the generator. Since the generator is unaware of what unexpected interactions can be caused by these arguments, it is not recommended to use this option.

The format of this is a space separated list of arguments, which can optionally be individually escaped to allow inclusion of whitespace and other control characters.

This key can be listed multiple times.

Group= 🌸

The host (numeric) GID, or group name to use as the group for the volume. Differently than GID, the specified value is passed to the mount operation.

Image= πŸ¦‹

Specifies the image the volume is based on when Driver is set to the image. It is recommended to use a fully qualified image name rather than a short name, both for performance and robustness reasons.

The format of the name is the same as when passed to podman pull. So, it supports using :tag or digests to guarantee the specific image version.

Special case:

  • If the name of the image ends with .image, Quadlet will use the image pulled by the corresponding .image file, and the generated systemd service contains a dependency on the $name-image.service (or the service name set in the .image file). Note: the corresponding .image file must exist.

Label= 🏷️

Set one or more OCI labels on the volume. The format is a list of key=value items, similar to Environment.

This key can be listed multiple times.

Options= βš™οΈ

The mount options to use for a filesystem as used by the mount(8) command -o option.

PodmanArgs= πŸ’…

This key contains a list of arguments passed directly to the end of the podman volume create command in the generated file (right before the name of the volume in the command line). It can be used to access Podman features otherwise unsupported by the generator. Since the generator is unaware of what unexpected interactions can be caused by these arguments, it is not recommended to use this option.

The format of this is a space separated list of arguments, which can optionally be individually escaped to allow inclusion of whitespace and other control characters.

This key can be listed multiple times.

ServiceName= ✨

By default, Quadlet will name the systemd service unit by appending -volume to the name of the Quadlet. Setting this key overrides this behavior by instructing Quadlet to use the provided name.

Note, the name should not include the .service file extension.

Type= 🧚

The filesystem type of Device as used by the mount(8) commands -t option.

UID= πŸ”‘

The UID that the volume will be created as. Differently than User, the specified value is not passed to the mount operation. The specified UID will own the volume's mount point directory and affects the volume chown operation.

User= 🌟

The host (numeric) UID, or user name to use as the owner for the volume. Differently than UID, the specified value is passed to the mount operation.

VolumeName= πŸŽ€

The (optional) name of the Podman volume. If this is not specified, the default value is the same name as the unit, but with a systemd- prefix, i.e. a $name.volume file creates a systemd-$name Podman volume to avoid conflicts with user-managed volumes.


πŸ› οΈ Build units [Build] πŸ› οΈ

Build files are named with a .build extension and contain a section [Build] describing the image build command. The generated service is a one-time command that ensures that the image is built on the host from a supplied Containerfile and context directory. Subsequent (re-)starts of the generated built service will usually finish quickly, as image layer caching will skip unchanged build steps. πŸ”₯

A minimal .build unit needs at least the ImageTag= key, and either of File= or SetWorkingDirectory= keys. πŸ’–

These build units are how you make containers and volumes depend on images that get built locally β€” which is so useful when you want to create container images that aren't up on any container registry, or when you're just doing local testing and development. Main character behavior, honestly. πŸ’…

Valid options for [Build] are listed below β€” every single one, mapped to its podman build equivalent, no cap:

  • Annotation=annotation=value β€” becomes --annotation=annotation=value
  • Arch=aarch64 β€” becomes --arch=aarch64
  • AuthFile=/etc/registry/auth.json β€” becomes --authfile=/etc/registry/auth.json
  • BuildArg=foo=bar β€” becomes --build-arg foo=bar
  • ContainersConfModule=/etc/nvd.conf β€” becomes --module=/etc/nvd.conf
  • DNS=192.168.55.1 β€” becomes --dns=192.168.55.1
  • DNSOption=ndots:1 β€” becomes --dns-option=ndots:1
  • DNSSearch=example.com β€” becomes --dns-search example.com
  • Environment=foo=bar β€” becomes --env foo=bar
  • File=/path/to/Containerfile β€” becomes --file=/path/to/Containerfile
  • ForceRM=false β€” becomes --force-rm=false
  • GlobalArgs=--log-level=debug β€” becomes --log-level=debug
  • GroupAdd=keep-groups β€” becomes --group-add=keep-groups
  • IgnoreFile=/path/to/.customignore β€” becomes --ignorefile=/path/to/.customignore
  • ImageTag=localhost/imagename β€” becomes --tag=localhost/imagename
  • Label=label β€” becomes --label=label
  • Network=host β€” becomes --network=host
  • PodmanArgs=--pull never β€” becomes --pull never
  • Pull=never β€” becomes --pull never
  • Retry=5 β€” becomes --retry=5
  • RetryDelay=10s β€” becomes --retry-delay=10s
  • Secret=secret β€” becomes --secret=id=mysecret,src=path
  • ServiceName=name β€” names the systemd unit name.service
  • SetWorkingDirectory=unit β€” sets the WorkingDirectory of the systemd unit file
  • Target=my-app β€” becomes --target=my-app
  • TLSVerify=false β€” becomes --tls-verify=false
  • Variant=arm/v7 β€” becomes --variant=arm/v7
  • Volume=/source:/dest β€” becomes --volume /source:/dest

Annotation=

Adds an image annotation (like annotation=value) to the image metadata. You can use it as many times as you want, bestie β€” stack 'em up. ✨

This is equivalent to the --annotation option of podman build.

Arch=

Overrides the architecture of the image you're building. Defaults to the host's architecture, iykyk.

This is equivalent to the --arch option of podman build.

AuthFile=

Path of the authentication file. That's it, that's the vibe. πŸ”‘

This is equivalent to the --authfile option of podman build.

BuildArg=

Specifies a build argument and its value in the same way environment variables are written (e.g., env=value), but heads up β€” it is not added to the environment variable list in the resulting image's configuration. Can be listed multiple times, so go off. πŸŽ€

This is equivalent to the --build-arg option of podman build.

ContainersConfModule=

Loads the specified containers.conf(5) module. Equivalent to the Podman --module option.

This key can be listed multiple times.

DNS=

Sets a network-scoped DNS resolver/nameserver for the build container. Literally telling your build where to look stuff up. 🌈

This key can be listed multiple times.

This is equivalent to the --dns option of podman build.

DNSOption=

Sets custom DNS options. Fancy.

This key can be listed multiple times.

This is equivalent to the --dns-option option of podman build.

  • DNSSearch= β€” Set custom DNS search domains. Use DNSSearch=. to remove the search domain. This key can be listed multiple times. This is equivalent to the --dns-search option of podman build.
  • Environment= β€” Add a value (e.g. env=value) to the built image. This uses the same format as services in systemd β€” the docs are at https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Environment= β€” and can be listed multiple times.
  • File= β€” Specifies a Containerfile which contains instructions for building the image. A URL starting with http(s):// allows you to specify a remote Containerfile to be downloaded. Note that for a given relative path to a Containerfile, or when using a http(s):// URL, you also must set SetWorkingDirectory= in order for podman build to find a valid context directory for the resources specified in the Containerfile. Note that setting a File= field is mandatory for a .build file, unless SetWorkingDirectory (or a WorkingDirectory in the Service group) has also been set. This is equivalent to the --file option of podman build.
  • ForceRM= β€” Always remove intermediate containers after a build, even if the build fails (default true). This is equivalent to the --force-rm option of podman build.
  • GlobalArgs= β€” This key contains a list of arguments passed directly between podman and build in the generated file. It can be used to access Podman features otherwise unsupported by the generator. Since the generator is unaware of what unexpected interactions can be caused by these arguments, it is not recommended to use this option. The format of this is a space separated list of arguments, which can optionally be individually escaped to allow inclusion of whitespace and other control characters. This key can be listed multiple times.
  • GroupAdd= β€” Assign additional groups to the primary user running within the container process. Also supports the keep-groups special flag. This is equivalent to the --group-add option of podman build.
  • IgnoreFile= β€” Path to an alternate .containerignore file to use when building the image. Note that when using a relative path you should also set SetWorkingDirectory=. This is equivalent to the --ignorefile option of podman build.
  • ImageTag= β€” Specifies the name which is assigned to the resulting image if the build process completes successfully. This is equivalent to the --tag option of podman build. This key can be listed multiple times. The first instance will be used as the name of the created artifact when the .build file is referenced by another Quadlet unit.
  • Label= β€” Add an image label (e.g. label=value) to the image metadata. Can be used multiple times. This is equivalent to the --label option of podman build.
  • Network= β€” Sets the configuration for network namespaces when handling RUN instructions. This has the same format as the --network option to podman build. For example, use host to use the host network, or none to not set up networking.

Special case:

  • If the name of the network ends with .network, Quadlet will look for the corresponding .network Quadlet unit. If found, Quadlet will use the name of the Network set in the Unit, otherwise, systemd-$name is used. The generated systemd service contains a dependency on the service unit generated for that .network unit, or on $name-network.service if the .network unit is not found. Note: the corresponding .network file must exist.

This key can be listed multiple times.

  • PodmanArgs= β€” This key contains a list of arguments passed directly to the end of the podman build command in the generated file (right before the image name in the command line). It can be used to access Podman features otherwise unsupported by the generator. Since the generator is unaware of what unexpected interactions can be caused by these arguments, it is not recommended to use this option. The format of this is a space separated list of arguments, which can optionally be individually escaped to allow inclusion of whitespace and other control characters. This key can be listed multiple times.
  • Pull= β€” Set the image pull policy. This is equivalent to the --pull option of podman build.
  • Retry= β€” Number of times to retry the image pull when a HTTP error occurs. Equivalent to the Podman --retry option.
  • RetryDelay= β€” Delay between retries. Equivalent to the Podman --retry-delay option.
  • Secret= β€” Pass secret information used in Containerfile build stages in a safe way. This is equivalent to the --secret option of podman build and generally has the form secret[,opt=opt ...].
  • ServiceName= β€” By default, Quadlet will name the systemd service unit by appending -build to the name of the Quadlet. Setting this key overrides this behavior by instructing Quadlet to use the provided name. Note, the name should not include the .service file extension.
  • SetWorkingDirectory= β€” Provide context (a working directory) to podman build. Supported values are a path, a URL, or the special keys file or unit to set the context directory to the parent directory of the file from the File= key or to that of the Quadlet .build unit file, respectively. This allows Quadlet to resolve relative paths. When using one of the special keys (file or unit), the WorkingDirectory field of the Service group of the Systemd service unit will also be set accordingly. Alternatively, users can explicitly set the WorkingDirectory field of the Service group in the .build file. Please note that if the WorkingDirectory field of the Service group is set by the user, Quadlet will not overwrite it even if SetWorkingDirectory is set to file or unit. By providing a URL to SetWorkingDirectory= you can instruct podman build to clone a Git repository or download an archive file extracted to a temporary location by podman build as build context. Note that in this case, the WorkingDirectory of the Systemd service unit is left untouched by Quadlet. Note that providing context directory is mandatory for a .build file, unless a File= key has also been provided.
  • Target= β€” Set the target build stage to build. Commands in the Containerfile after the target stage are skipped. This is equivalent to the --target option of podman build.
  • TLSVerify= β€” Require HTTPS and verification of certificates when contacting registries. This is equivalent to the --tls-verify option of podman build.
  • Variant= β€” Override the default architecture variant of the container image to be built. This is equivalent to the --variant option of podman build.
  • Volume= β€” Mount a volume to containers when executing RUN instructions during the build. This is equivalent to the --volume option of podman build, and generally has the form [[SOURCE-VOLUME|HOST-DIR:]CONTAINER-DIR[:OPTIONS]]. If SOURCE-VOLUME starts with ., Quadlet resolves the path relative to the location of the unit file.

Special case:

  • If SOURCE-VOLUME ends with .volume, Quadlet will look for the corresponding .volume Quadlet unit. If found, Quadlet will use the name of the Volume set in the Unit, otherwise, systemd-$name is used. The generated systemd service contains a dependency on the service unit generated for that .volume unit, or on $name-volume.service if the .volume unit is not found. Note: the corresponding .volume file must exist.

This key can be listed multiple times.

πŸ¦‹ Image units [Image] πŸ¦‹

Image files are named with a .image extension and contain a section [Image] describing the container image pull command. The generated service is a one-time command that ensures that the image exists on the host, pulling it if needed.

Using image units allows containers and volumes to depend on images being automatically pulled. This is particularly interesting when using special options to control image pulls.

Valid options for [Image] are listed below:

πŸŽ€ [Image] options β†’ podman image pull mapping

okay bestie, here’s the [Image] key-to-podman image pull pipeline, no cap: left side is the Quadlet key, right side is what it maps to. ✨

  • AllTags=true β†’ --all-tags
  • Arch=aarch64 β†’ --arch=aarch64
  • AuthFile=/etc/registry/auth.json β†’ --authfile=/etc/registry/auth.json
  • CertDir=/etc/registry/certs β†’ --cert-dir=/etc/registry/certs
  • ContainersConfModule=/etc/nvd.conf β†’ --module=/etc/nvd.conf
  • Creds=myname:mypassword β†’ --creds=myname:mypassword
  • DecryptionKey=/etc/registry.key β†’ --decryption-key=/etc/registry.key
  • GlobalArgs=--log-level=debug β†’ --log-level=debug
  • Image=quay.io/centos/centos:latest β†’ podman image pull quay.io/centos/centos:latest
  • ImageTag=quay.io/centos/centos:latest β†’ use this name when resolving .image references
  • OS=windows β†’ --os=windows
  • PodmanArgs=--os=linux β†’ --os=linux
  • Policy=always β†’ --policy=always
  • Retry=5 β†’ --retry=5
  • RetryDelay=10s β†’ --retry-delay=10s
  • ServiceName=name β†’ names the systemd unit name.service
  • TLSVerify=false β†’ --tls-verify=false
  • Variant=arm/v7 β†’ --variant=arm/v7

🌸 AllTags=

Every tagged image in the repository gets pulled. This is equivalent to Podman’s --all-tags option. 🏷️

πŸ¦‹ Arch=

Override the architecture of the image to be pulled; if you don’t, it defaults to the host’s architecture. This is equivalent to Podman’s --arch option. πŸ—οΈ

πŸŽ€ AuthFile=

Path of the authentication file. This is equivalent to Podman’s --authfile option. πŸ”

✨ CertDir=

Use certificates at the given path β€” (*.crt, *.cert, *.key) β€” to connect to the registry. This is equivalent to Podman’s --cert-dir option. 🧚

πŸ’œ ContainersConfModule=

Load the containers.conf(5) module you specify. This is equivalent to Podman’s --module option.

This key can be listed multiple times. 🫢

🌈 Creds=

The [username[:password]] to use when authenticating with the registry, if required. This is equivalent to Podman’s --creds option. πŸ’…

πŸ”‘ DecryptionKey=

The [key[:passphrase]] to be used for decryption of images. This is equivalent to Podman’s --decryption-key option. πŸ”“

πŸ’€ GlobalArgs=

This key contains a list of arguments passed directly between podman and image in the generated file. It can be used to access Podman features that the generator otherwise doesn’t support.

Since the generator is unaware of what unexpected interactions these arguments can cause, it is not recommended to use this option.

The format is a space separated list of arguments, which can optionally be individually escaped to allow inclusion of whitespace and other control characters.

This key can be listed multiple times. 😭

πŸ–ΌοΈ Image=

The image to pull. It is recommended to use a fully qualified image name rather than a short name β€” both for performance and robustness reasons.

The format of the name is the same as when passed to podman pull. So it supports using :tag or digests to guarantee the specific image version. πŸ‘‘

🧚 ImageTag=

The actual FQIN of the referenced Image. Only meaningful when the source is a file or directory archive.

For example, an image saved into a docker-archive with the following Podman command:

podman image save --format docker-archive --output /tmp/archive-file.tar quay.io/podman/stable:latest

requires setting:

  • Image=docker-archive:/tmp/archive-file.tar
  • ImageTag=quay.io/podman/stable:latest

🌍 OS=

Override the OS of the image to be pulled; if you don’t, it defaults to the host’s OS. This is equivalent to Podman’s --os option. πŸ—ΊοΈ

πŸ’– PodmanArgs=

This key contains a list of arguments passed directly to the end of the podman image pull command in the generated file β€” right before the image name in the command line. It can be used to access Podman features that the generator otherwise doesn’t support.

Since the generator is unaware of what unexpected interactions these arguments can cause, it is not recommended to use this option.

The format is a space separated list of arguments, which can optionally be individually escaped to allow inclusion of whitespace and other control characters.

This key can be listed multiple times. πŸ™ˆ

πŸ“œ Policy=

The pull policy to use when pulling the image. This is equivalent to Podman’s --policy option. 🎯

πŸ” Retry=

Number of times to retry the image pull when an HTTP error occurs. This is equivalent to Podman’s --retry option. ⏰

⏳ RetryDelay=

Delay between retries. This is equivalent to Podman’s --retry-delay option. πŸ•°οΈ

🏷️ ServiceName=

By default, Quadlet will name the systemd service unit by appending -image to the name of the Quadlet. Setting this key overrides this behavior by instructing Quadlet to use the provided name.

Note: the name should not include the .service file extension. πŸ’…

πŸ”’ TLSVerify=

Require HTTPS and verification of certificates when contacting registries. This is equivalent to Podman’s --tls-verify option. πŸ›‘οΈ

🧬 Variant=

Override the default architecture variant of the container image. This is equivalent to Podman’s --variant option. 🌱

🧸 Artifact units [Artifact]

⚠️ WARNING: Experimental Unit ⚠️

bestie, this unit is considered experimental and still in development. Inputs, options, and outputs are all subject to change. no cap, tread carefully πŸ’…


Artifact units are named with a .artifact extension and contain a [Artifact] section describing the container artifact pull command. The generated service is a one-time command that ensures that the artifact exists on the host, pulling it if needed.

Using artifact units allows containers to depend on artifacts being automatically pulled. This is particularly useful for managing artifacts that containers need to mount or access. The Artifact key is mandatory inside of the [Artifact] unit.

Valid options for [Artifact] are listed below β€” here's the cheat-sheet mapping, because we love a clear vibe check 🫢

  • Artifact=quay.io/foobar/artifact:special β†’ podman artifact pull quay.io/foobar/artifact:special
  • AuthFile=/etc/registry/auth.json β†’ --authfile=/etc/registry/auth.json
  • CertDir=/etc/registry/certs β†’ --cert-dir=/etc/registry/certs
  • ContainersConfModule=/etc/nvd.conf β†’ --module=/etc/nvd.conf
  • Creds=username:password β†’ --creds=username:password
  • DecryptionKey=/etc/registry.key β†’ --decryption-key=/etc/registry.key
  • GlobalArgs=--log-level=debug β†’ --log-level=debug
  • PodmanArgs=--pull never β†’ --pull never
  • Quiet=true β†’ --quiet
  • Retry=5 β†’ --retry=5
  • RetryDelay=10s β†’ --retry-delay=10s
  • ServiceName=my-artifact β†’ Set the systemd service name to my-artifact.service
  • TLSVerify=false β†’ --tls-verify=false

πŸ”‘ Artifact=

The artifact to pull from a registry onto the local machine. This is the only required key for artifact units.

It is required to use a fully qualified artifact name rather than a short name, both for performance and robustness reasons. periodt, use the whole name ✨

πŸ”‘ AuthFile=

Path of the authentication file.

This is equivalent to the Podman --authfile option.

πŸ”‘ CertDir=

Use certificates at path (*.crt, *.cert, *.key) to connect to the registry.

This is equivalent to the Podman --cert-dir option.

πŸ”‘ ContainersConfModule=

Load the specified containers.conf(5) module. Equivalent to the Podman --module option.

This key can be listed multiple times. yes bestie, stack 'em 🌸

πŸ”‘ Creds=

The credentials to use when contacting the registry in the format [username[:password]].

This is equivalent to the Podman --creds option.

πŸ”‘ DecryptionKey=

The [key[:passphrase]] to be used for decryption of artifacts.

This is equivalent to the Podman --decryption-key option.

πŸ”‘ GlobalArgs=

This key contains a list of arguments passed directly between podman and artifact in the generated file. It can be used to access Podman features otherwise unsupported by the generator. Since the generator is unaware of what unexpected interactions can be caused by these arguments, it is not recommended to use this option.

The format of this is a space separated list of arguments, which can optionally be individually escaped to allow inclusion of whitespace and other control characters.

This key can be listed multiple times.

ngl, this one is giving β€œI know what I'm doing” energy β€” but the manpage literally says it's not recommended, so don't come for me if it gets weird πŸ’€

πŸ”‘ PodmanArgs=

This key contains a list of arguments passed directly to the end of the podman artifact pull command in the generated file (right before the artifact name in the command line). It can be used to access Podman features otherwise unsupported by the generator. Since the generator is unaware of what unexpected interactions can be caused by these arguments, it is not recommended to use this option.

The format of this is a space separated list of arguments, which can optionally be individually escaped to allow inclusion of whitespace and other control characters.

This key can be listed multiple times.

πŸ”‘ Quiet=

Suppress output information when pulling artifacts.

This is equivalent to the Podman --quiet option.

πŸ”‘ Retry=

Number of times to retry the artifact pull when a HTTP error occurs. Equivalent to the Podman --retry option.

πŸ”‘ RetryDelay=

Delay between retries. Equivalent to the Podman --retry-delay option.

πŸ”‘ ServiceName=

The (optional) name of the systemd service. If this is not specified, the default value is the same name as the unit, but with a -artifact suffix, i.e. a $name.artifact file creates a $name-artifact.service systemd service.

πŸ”‘ TLSVerify=

Require HTTPS and verification of certificates when contacting registries.

This is equivalent to the Podman --tls-verify option.


πŸͺ„ Quadlet section [Quadlet]

Some quadlet specific configuration is shared between different unit types. Those settings can be configured in the [Quadlet] section.

Valid options for [Quadlet] are listed below:

  • DefaultDependencies=false β€” Disable implicit network dependencies to the unit

πŸŽ€ DefaultDependencies=

Add Quadlet's default network dependencies to the unit (default is true).

When set to false, Quadlet will not add a dependency (After=, Wants=) to network-online.target/podman-user-wait-network-online.service to the generated unit.

Note, this option is set in the [Quadlet] section. The systemd [Unit] section has an option with the same name but a different meaning. iykyk, don't mix them up πŸ’–

🌈 EXAMPLES 🌈

Okay bestie, this is the part you actually came for β€” the examples. Grab a snack πŸ“, we're going shopping.


πŸŽ€ Example test.container

The classic. A minimal container unit, serving looks:

[Unit]
Description=A minimal container

[Container]
# Use the centos image
Image=quay.io/centos/centos:latest

# Use volume and network defined below
Volume=test.volume:/data
Network=test.network

# In the container we just run sleep
Exec=sleep 60

[Service]
# Restart service when sleep finishes
Restart=always
# Extend Timeout to allow time to pull the image
TimeoutStartSec=900
# ExecStartPre flag and other systemd commands can go here, see systemd.unit(5) man page.
ExecStartPre=/usr/share/mincontainer/setup.sh

[Install]
# Start by default on boot
WantedBy=multi-user.target default.target

Notice the [Container] section pulling the quay.io/centos/centos:latest image, wiring up the test.volume volume and the test.network network from the examples lower down πŸ’…, and then just vibing with Exec=sleep 60. Then [Service] does its systemd thing β€” Restart=always, TimeoutStartSec=900 so the image pull has room to breathe, and an ExecStartPre=/usr/share/mincontainer/setup.sh hook. [Install] gets it starting on boot via WantedBy=multi-user.target default.target. Iconic.


🌸 Example test.kube

Kubernetes yaml, but make it a unit file:

[Unit]
Description=A kubernetes yaml based service
Before=local-fs.target

[Kube]
Yaml=/opt/k8s/deployment.yml

[Install]
# Start by default on boot
WantedBy=multi-user.target default.target

Before=local-fs.target in [Unit], the yaml living at /opt/k8s/deployment.yml, and boom β€” WantedBy=multi-user.target default.target for that boot-time slay ✨.


πŸ¦‹ Example for a locally built image used in a container, with build-specific arguments

Two files, one cute little build pipeline. First, test.build:

[Build]
# Tag the image to be built
ImageTag=localhost/imagename

# Set the working directory to the path of the unit file,
# expecting to find a Containerfile/Dockerfile
# + other files needed to build the image
SetWorkingDirectory=unit
# Set build arguments VERSION and DEBUG
BuildArg=VERSION=1.0
          DEBUG=false

Then test.container:

[Container]
Image=test.build

The ImageTag=localhost/imagename names what you're building, SetWorkingDirectory=unit points the working dir at the unit file's path (where your Containerfile/Dockerfile and other build files live 🫢), and the BuildArg=VERSION=1.0 / DEBUG=false pair sets those two build args. Then the container just says Image=test.build and it's giving "I built this myself" main character energy πŸ‘‘.


πŸ’œ Example test.volume

[Volume]
User=root
Group=root
Label=org.test.Key=value

User=root, Group=root, and a Label=org.test.Key=value. Short, sweet, perfect.


🧚 Example test.network

[Network]
Subnet=172.16.0.0/24
Gateway=172.16.0.1
IPRange=172.16.0.0/28
Label=org.test.Key=value

Subnet 172.16.0.0/24, gateway 172.16.0.1, IP range 172.16.0.0/28, plus that same Label=org.test.Key=value. Networking but make it adorable 🌈.


πŸ”₯ Example test.artifact β€” pulling with a single auth file

[Artifact]
Artifact=quay.io/example/my-artifact:latest
AuthFile=/etc/registry/auth.json
TLSVerify=false

Just the artifact, one auth file at /etc/registry/auth.json, and TLSVerify=false. Look at you being efficient πŸ’….


⭐ Example where a container depends on an artifact

Okay this one's a two-parter. my-artifact.artifact:

[Artifact]
Artifact=quay.io/example/my-config:latest

And my-app.container:

[Container]
Image=quay.io/example/my-app:latest
Mount=type=artifact,source=my-artifact.artifact,destination=/etc/config

The container mounts the artifact at /etc/config via Mount=type=artifact,source=my-artifact.artifact,destination=/etc/config. The way that dependency just works πŸ˜­πŸ’–.


πŸ“ Example for a container in a Pod

test.pod:

[Pod]
PodName=test

centos.container:

[Container]
Image=quay.io/centos/centos:latest
Exec=sh -c "sleep inf"
Pod=test.pod

The pod's named test via PodName=test, and the container joins it with Pod=test.pod while running sh -c "sleep inf" forever. No notes, flawless ✨.


πŸŽ€ Example for a Pod with a one-shot startup task

Three files. test.pod:

[Pod]
PodName=test
ExitPolicy=continue

startup-task.container:

[Service]
Type=oneshot
RemainAfterExit=yes

[Container]
Pod=test.pod
Image=quay.io/centos/centos:latest
Exec=sh -c "echo 'setup starting'; sleep 2; echo 'setup complete'"

app.container:

[Unit]
Requires=startup-task.container
After=startup-task.container

[Container]
Pod=test.pod
Image=quay.io/centos/centos:latest
Exec=sh -c "echo 'app running.'; sleep 30"

The pod uses ExitPolicy=continue so it keeps going even when a member exits, the startup task is Type=oneshot with RemainAfterExit=yes, and the app Requires= and After= the startup task. It's giving "ordered, responsible, hydrated" πŸ’œ.


πŸ’€ Example s3fs.volume

For further details, please see the s3fs-fuse project. Remember to read the FAQ.

⚠️ NOTE: Enabling the cache massively speeds up access and write times on static files/objects. However, use_cache is UNBOUNDED! Be careful, it will fill up with any files accessed on the s3 bucket through the file system.

Please remember to set S3_BUCKET, PATH, AWS_REGION. CACHE_DIRECTORY should be set up by systemd.

[Service]
CacheDirectory=s3fs
ExecStartPre=/usr/local/bin/aws s3api put-object --bucket ${S3_BUCKET} --key ${PATH}/

[Volume]
Device=${S3_BUCKET}:/${PATH}
Type=fuse.s3fs
VolumeName=s3fs-volume
Options=iam_role,endpoint=${AWS_REGION},use_xattr,listobjectsv2,del_cache,use_cache=${CACHE_DIRECTORY}
# `iam_role` assumes inside EC2, if not, Use `profile=` instead

CacheDirectory=s3fs in [Service], a put-object ExecStartPre, and then the volume itself: Type=fuse.s3fs, VolumeName=s3fs-volume, and that long Options= line. And peep the comment β€” # `iam_role` assumes inside EC2, if not, Use `profile=` instead πŸ™ˆ.

For more examples, please see the podman-quadlet-basic-usage.7.


πŸ’œ SEE ALSO πŸ’œ

Go read these next, bestie, they're all linked in the group chat:


And that's the whole page, babe 🫢 β€” you made it to the end of podman-systemd.unit(5) (that's section 5 of the manual, mwah). Go forth, write your units, and remember: use_cache is unbounded. Stay hydrated, stay caffeinated, and don't let your s3 bucket eat your disk πŸ’–βœ¨πŸŽ€

πŸ“¦ where this page comes from