docker run to quadlet converter
Paste a docker run command, get a .container file that systemd can start at boot.
Last updated
In short: Every docker run option becomes one line in a .container file: -p becomes PublishPort=, -v becomes Volume=, -e becomes Environment= and --restart becomes Restart= in [Service]. -d and --rm are dropped because systemd and quadlet already handle them. Save the file as ~/.config/containers/systemd/<name>.container, run systemctl --user daemon-reload, then systemctl --user start <name>.service.
Your input never leaves your browser. Everything is calculated on your device; nothing you type is sent to a server.
How does the docker run to quadlet converter work?
A docker run command starts a container once. A quadlet .container
file describes the same container so that systemd can own it: start it at boot, restart it
when it crashes, stop it cleanly on shutdown and send its output to the journal. Podman's
quadlet generator reads the file and writes a normal systemd service with the matching
podman run command inside.
Most options have a key with a similar name: -p is PublishPort=,
-v is Volume=, -e is Environment=,
--name is ContainerName= and --health-cmd is
HealthCmd=. Everything after the image name becomes Exec=. Options
that podman 5.0 has no key for, such as --memory and --add-host, go
into PodmanArgs=, which passes them straight to podman run.
The restart policy is the one real change in thinking. Instead of the container runtime
restarting the container, systemd does it through Restart= in the
[Service] section. That is also why -d and --rm disappear:
systemd already runs the container in the background and removes it when it stops.
Values are escaped for systemd, so 50% becomes 50%% and
$5 becomes $$5; the container still receives the original text. If a
compose file describes several containers, use the docker-compose to quadlet converter instead.
Worked examples
Publish a port on localhost only
Input -p 127.0.0.1:8000:80 gives PublishPort=127.0.0.1:8000:80
The host IP, host port and container port carry over as-is, so the service is only reachable from the machine itself.
Keep a container running
Input --restart unless-stopped gives Restart=always
systemd owns the restart policy. unless-stopped and always both become Restart=always; systemctl --user stop still stops it for good.
Give a container the NVIDIA GPU
Input --gpus all gives AddDevice=nvidia.com/gpu=all
Podman uses CDI device names for GPUs. Generate the spec once with nvidia-ctk cdi generate from the NVIDIA Container Toolkit.
Frequently asked questions
How do I turn a docker run command into a systemd service with podman?
Convert it to a .container file and put it in ~/.config/containers/systemd/ (or /etc/containers/systemd/ for root). After systemctl --user daemon-reload, podman's quadlet generator creates <name>.service, which you start with systemctl --user start <name>.service. The keys are documented in the podman-systemd.unit(5) man page.
What happens to -d, --rm and -it?
-d and --rm are dropped: systemd runs the container in the background, and quadlet always removes it when the unit stops. -i and -t are kept as PodmanArgs=--interactive --tty, which only matters for images that expect a terminal.
Why does -v data:/data create a .volume file?
A name instead of a path means a named volume. The .volume file lets systemd create it before the container starts, and VolumeName=data keeps the existing volume, so your data is reused.
How do I start a rootless container at boot?
Keep WantedBy=default.target in [Install] (the Start at boot option) and run loginctl enable-linger $USER once. Without lingering, user services only run while you are logged in.
What happens to options the converter does not know?
--flag=value options are passed through as PodmanArgs= with a warning, so nothing disappears silently. Write unknown options with = (for example --blkio-weight=300); otherwise the converter cannot tell the value apart from the image name.