Podman Quadlet Generator
{{ summaryTitle }}
{{ summaryLine }}
{{ copyStatus }}
{{ copyStatus || exportStatus }}
{{ values.container_text }}
{{ copyStatus || exportStatus }}
{{ values.volume_text }}
No named volume requested.
{{ exportStatus }}
| Level | Check | Target-host review | Copy |
|---|---|---|---|
| {{ row.level }} | {{ row.check }} | {{ row.detail }} |
{{ exportStatus }}
Chart unavailable. Generated unit files remain usable.
{{ copyStatus || exportStatus }}
{{ values.validation_text }}
Similar tools
- systemd Unit Generator Draft a systemd service unit from workload, account, restart and hardening choices with local review findings and host verification commands.
- Docker Compose Generator Build Docker Compose YAML from manual settings or supported docker run commands, with local processing and a review before applying imports.
- .env File Generator Generate reviewed .env and .env.example files with key cleanup and secret-aware placeholders while flagging quote or duplicate risks locally.
- Dockerfile Security Basics Checker Check a Dockerfile for risky base images, embedded secrets, broad copies, root users, package residue, and missing runtime safeguards.
Keeping a container available after a terminal closes involves more than choosing an image. It needs a service owner, a restart policy, deliberate network exposure, and storage that survives the container when necessary. On a Linux host that uses systemd, Podman Quadlet records these choices in text files from which Podman generates manageable systemd services.
A .container file describes a container, while a .volume file can describe named storage that it needs. These are the configuration sources to maintain. During boot or a manager reload, Quadlet translates them into .service units that systemd can start, stop, and supervise. Editing a generated service is therefore a poor place to keep lasting configuration changes, because later generation can replace it.
This arrangement suits a web server or another application intended to run as a host service. The image supplies the application and its defaults; the Quadlet source supplies the host-specific choices. An image reference does not establish which port the application listens on, which directories it must write, or whether it supports the host's architecture. Those details still come from the application and the target machine.
The account running the service also matters. A rootless Quadlet service runs through a user's systemd manager with that user's host permissions. A rootful service runs through the system manager and requires administrative management. Moving between these contexts can change access to images, volumes, files, and ports. Rootless operation limits host privileges, but a container can still affect data made accessible to its account.
- A loopback port binding makes an application available through the host's local address. A wildcard binding requests listening on all addresses in that address family, so routing, firewall rules, and authentication become consequential.
- A bind mount exposes a particular host path. A named volume provides separately managed storage. Persistent data needs an intentional mount and an appropriate backup plan; the container's name alone does not identify where that data lives.
- A restart policy determines when systemd tries to run a service again. It does not establish that the application is healthy or that repeated startup attempts can repair its configuration.
The installed Podman and systemd versions must understand the configuration, Quadlet requires cgroup v2, and the image, permissions, storage, and network must work together on the host. A syntax check cannot establish successful startup or application readiness. The filename to edit, the service to start, and the Podman resource to inspect can have different names.
How to Use This Tool:
Start with explicit settings for one container. The files update as you edit; there is no generation button. This generator does not import Docker run commands or Compose files, inspect registries, install services, or execute workloads.
- Choose Target context for the Linux account and systemd manager that will own the service. The initial choice is Rootless user.
- Enter a fully qualified Container image and a Unit filename basename without
.container. The initial example usesdocker.io/library/nginx:stableandweb; replace them with your intended workload. - Set TCP port mappings one per line. The initial
127.0.0.1:8080:80maps host loopback port 8080 to container port 80. Clear the field when no published port is wanted. - Open Advanced for bind mounts, literal environment assignments, or a named volume. A named volume needs both its basename and destination. Container naming, restart policy, and a read-only root filesystem are optional refinements.
- Review Container unit and Review checks. If Container fields need attention appears, correct the stated field. Entered values remain available, while generated results disappear and summary copying is disabled until the input is valid.
- Save the
.containerfile and, when requested, its separate.volumefile. Follow Host validation on the target machine, reviewing the generated service before installation and continuing to startup only after the host checks succeed.
Interpreting Results:
Draft files generated means the entries passed this generator's rules and produced source text. It does not mean the files have been accepted by your installed Quadlet version. A draft with zero warnings still has manager and image checks to review.
The Review checks distinguish conditions worth examining from invalid input. Any mapping not using exactly 127.0.0.1 or [::1] produces an exposure warning. Rootless host ports below 1024, overlapping wildcard bindings, bind mounts, and explicit environment assignments also produce warnings. These findings come from the entered configuration; they do not report discovered host permissions, active listeners, or detected secrets.
The Directive composition chart counts emitted key=value directives grouped by section across both source files. Section headings and blank lines are not directives. Adding a volume contributes another description to the combined Unit count and adds a Volume count. The chart measures neither risk nor readiness, and a chart-loading failure does not invalidate the generated text.
Host validation contains instructions, not completed checks. Its later commands install files and start a service if you run them. Review the image, names, paths, and exposure before doing so. The displayed byte count covers the generated unit sources, excluding the validation instructions and review exports.
Technical Details:
Quadlet combines container configuration with systemd service settings. Podman interprets the container or volume section and generates the commands and dependencies needed by systemd. The remaining admitted sections describe the service and its startup target.
Transformation core
The generated container source always has four sections. Optional entries add directives within those sections; requesting named storage also creates a second source with its own volume definition.
| Source section | Emitted directives | Meaning |
|---|---|---|
Container [Unit] |
Description=Container for web for basename web |
A readable service description. |
[Container] |
Image, with optional ContainerName, PublishPort, Volume, Environment, and ReadOnly |
The image and explicitly requested container settings. Ports, mounts, and environment assignments each use repeated directives. |
[Service] |
Restart=on-failure unless overridden |
The systemd restart policy. |
[Install] |
WantedBy=default.target or WantedBy=multi-user.target |
The selected manager's startup target. |
Volume [Unit] and [Volume] |
Description=Volume for web-data and VolumeName=systemd-web-data for basename web-data |
A matching owner for the named storage. No separate [Install] section is emitted for this source. |
File, service, and resource identities
A filename determines the generated service identity. A container name override changes only the Podman container name. For a container basename of web and a volume basename of web-data, the relationships are:
| Quadlet source | Generated systemd service | Podman resource |
|---|---|---|
web.container |
web.service |
systemd-web when no container override is entered |
web-data.volume |
web-data-volume.service |
systemd-web-data, explicitly set by VolumeName |
The container entry Volume=web-data.volume:/data:rw refers to that exact Quadlet source. Quadlet supplies the dependency on the volume service, which ensures the named volume exists. Both sources must accompany the deployment. This does not establish that an arbitrary existing volume has been selected or that its contents and permissions suit the application.
A container basename of web-volume paired with volume basename web is rejected because both would generate web-volume.service. This comparison covers the proposed pair only. Existing host services, containers, and volumes are not inspected for naming conflicts.
Manager placement and startup
The selected context changes source placement, startup target, and the manual command scope. It leaves the requested image, ports, mounts, and environment settings intact.
| Setting | Rootless user | System (rootful) |
|---|---|---|
| Source directory | ~/.config/containers/systemd/ |
/etc/containers/systemd/ |
| Startup target | WantedBy=default.target |
WantedBy=multi-user.target |
| Service commands | systemctl --user |
sudo systemctl |
These are the locations selected by this generator; Quadlet supports additional search paths. Both contexts need cgroup v2 and compatible installed software. Rootless startup also needs a functioning user manager. Starting that manager at boot and keeping it available after logout involves the host's user-lingering policy, which is not changed here.
Quadlet applies [Install] while generating services. The resulting services are transient, so the supplied instructions do not use systemctl enable on the generated service. Maintain the startup relationship in the Quadlet source instead.
For an initial rootless check, the emitted command selects the current scratch directory on the target host:
QUADLET_UNIT_DIRS="$PWD" /usr/lib/systemd/system-generators/podman-system-generator --user --dryrun
System context emits the corresponding administrative command:
sudo env QUADLET_UNIT_DIRS="$PWD" /usr/lib/systemd/system-generators/podman-system-generator --dryrun
The executable location can differ by distribution. After reviewing the generated service and errors, copy the sources into place and run the supplied systemd-analyze --user --generators=true verify web.service command for the rootless example, or sudo systemd-analyze --generators=true verify web.service for system context. Run the remaining reload, start, status, and journal commands only after verification succeeds. The instructions do not automatically stop later commands when an earlier command fails. Dry-run output and service verification still leave application startup and behavior to be checked on that machine.
Accepted configuration and bounds
The following limits belong to this generator, rather than defining general Podman maxima. Invalid input prevents all generated files from being presented.
| Configuration | Accepted scope |
|---|---|
| Workload | One image and one container, with zero or one generated named-volume source. |
| Names | 1 to 64 ASCII letters, digits, hyphens, or underscores, starting with a letter or digit. Unit and volume inputs are basenames without extensions. |
| Image reference | A qualified lower-case registry/repository, optionally followed by a tag or @sha256: and 64 hexadecimal digits. localhost/ references are accepted. The image field is limited to 512 characters. |
| TCP mappings | Up to five rows of IP:host-port:container-port, optionally ending in /tcp. Both ports must be integers from 1 to 65535. Accepted addresses are IPv4, [::1], and [::]. |
| Mounts | Up to five total mounts. One named volume leaves room for four bind mounts. Container destinations must not repeat. |
| Environment | Up to thirty explicit KEY=value lines with unique keys. Empty values are allowed. Keys begin with an ASCII letter or underscore and continue with ASCII letters, digits, or underscores. |
| Generated source size | At most 32 KiB, or 32,768 UTF-8 bytes, for the container and volume sources combined, including their formatting and final newlines. |
A short image name such as nginx:stable is rejected. A repository ending in .image or .build needs an explicit tag or digest so Quadlet does not interpret it as another source-file reference. Passing the reference check establishes only accepted spelling; image existence, architecture, contents, and availability remain unverified.
Port ranges, UDP, hostnames, and arbitrary IPv6 addresses are outside the admitted mapping syntax. IPv4 octets and ports use decimal notation without leading zeros. Repeating the same host IP and port is rejected. Reusing a host port with a wildcard and another address instead produces a Possible listening collision warning, because actual binding behavior depends on the target. Rootless mappings below host port 1024 may be denied by the host's unprivileged-port policy.
Blank rows are ignored. Port, mount, and environment text fields are capped at 2,048, 8,192, and 32,768 UTF-16 code units respectively; the named-volume destination is capped at 1,024. Some Unicode characters occupy two code units. The separate output limit counts UTF-8 bytes after escaping and can be reached before a text field is full.
Mounts and literal values
Bind mounts use /host/path:/container/path[:ro|rw[,z|Z]]. Omitted options become ro in this generator. A named volume is always emitted with rw. Paths must be absolute and cannot contain colons, backslashes, tabs, line breaks, or . and .. path components. Whitespace cannot surround an individual path, and an identical destination cannot be used twice.
Literal dollar signs and ASCII spaces are rejected specifically in host bind sources. The checked Quadlet behavior interprets mount dependencies and command arguments differently for these forms. This conservative restriction does not establish that every Podman configuration forbids such host paths. Interior spaces and literal dollar signs remain supported in container destinations, including the named-volume destination; percent signs remain supported in both source and destination paths.
Environment entries supply literal values. They do not inherit the host environment, evaluate shell expressions, or read an environment file. Bare keys, export statements, duplicate keys, and multiline values are rejected. Quotes inside a value are retained as content.
Rendering preserves admitted literals through the Quadlet text and generated systemd command. Enter the intended characters once; do not pre-escape them. The escapes below describe the emitted Quadlet text, not a universal rule for shell commands or other environment-file formats.
| Entered literal | Generated representation | Where it applies |
|---|---|---|
% |
%% |
Permitted paths and environment values. |
$ |
$$ |
Container destinations and environment values; host bind sources reject it. |
" and \ |
\" and \\ |
Environment values, with the complete KEY=value assignment enclosed in double quotes. |
Unsupported controls, including U+0080, are rejected. Tabs cannot appear within paths or environment values, and line breaks separate rows rather than becoming part of a value. Ordinary Unicode such as accented letters, Chinese or Japanese characters, emoji, and interior nonbreaking spaces remains supported where the field's other rules permit it. These character checks do not establish compatibility with every installed Podman or systemd version.
On the host, bind sources must exist and be accessible to the account running Podman. SELinux can require suitable labels even when file permissions appear correct. The optional z requests shared relabeling, while Z requests private relabeling. Both can change host labels, including on a mount requested as read-only, so they need deliberate review.
Restart and filesystem behavior
A blank restart override emits Restart=on-failure, covering unsuccessful exits, unclean signal termination, and applicable timeouts. Choose no to disable automatic restarting, on-success for clean exits, or always to include both clean exits and failures. An explicit service stop does not trigger automatic restart, and systemd's start-rate limits still apply.
The narrower on-abnormal policy covers unclean signals and timeouts without treating an ordinary nonzero exit code as a restart reason. on-abort selects unclean signal termination, while on-watchdog selects watchdog expiry. Selecting a restart policy does not configure a watchdog or an application health check.
The read-only root filesystem option is off initially and emits ReadOnly=true when enabled. Explicit writable mounts remain writable, and applications may need such paths to start. No arbitrary command, privileged-container mode, host-network mode, or additional security directive is generated.
Privacy Notes:
Generation runs in the browser without contacting the selected image registry or target host. No Podman or systemd command runs on this page, and it makes no installation or service changes. Ordinary site assets can still load over the network.
- The URL can preserve the image, unit basename, target context, TCP mappings, container name override, read-only choice, restart choice, named-volume basename, and named-volume destination. These settings can survive reload and travel with a shared link. In particular, the named-volume destination path is shareable.
- Host bind mounts and environment assignments stay out of the tool's URL state and browser storage. They are temporary and disappear on reload, so a shared link does not reproduce those parts of the configuration.
- Copied and downloaded unit files contain the entered bind paths and literal environment values. Those values are not encrypted secrets. Handle the clipboard and saved files deliberately; unit files and process inspection can expose credentials. The supplied installation commands use file mode
0644, so they should not be treated as secret-file installation instructions.
Worked Examples:
Rootless web service on loopback
Keep docker.io/library/nginx:stable, basename web, Rootless user, and 127.0.0.1:8080:80, with the advanced fields unchanged. The result is one web.container source containing PublishPort=127.0.0.1:8080:80/tcp, Restart=on-failure, and WantedBy=default.target. Its intended service is web.service, with default container name systemd-web. There are no Warning rows, but both the user-manager and image checks remain. After host validation and placement in ~/.config/containers/systemd/, the supplied startup command is systemctl --user start web.service.
System service with linked storage and a literal value
Keep the same image, basename, and loopback mapping, choose System (rootful), set Named volume unit to web-data and its destination to /data, and enter MESSAGE=hello $USER 50%. The container includes Volume=web-data.volume:/data:rw, Environment="MESSAGE=hello $$USER 50%%", and WantedBy=multi-user.target. The intended environment value is literally hello $USER 50%; the host username is not substituted. The separate web-data.volume sets VolumeName=systemd-web-data and supplies the dependency through web-data-volume.service. The literal environment assignment adds one warning. Save both under /etc/containers/systemd/ only after reviewing the dry run, verify the service, and then use the emitted sudo systemctl start web.service instruction when ready to start the workload.
References:
- Podman systemd units using Quadlet, Podman.
- General syntax of systemd configuration files, systemd.
- Service configuration and restart policies, systemd.
- Podman volume mount options, Podman.
- Rootless Podman limitations, Podman.
- User lingering and login management, systemd.