Skip to content
Search AlmaLinux

    Browse this documentation

    AlmaLinux 9: Quadlet file syntax and debugging

    In an AlmaLinux 9.8 privileged container (host kernel 5.14.0-687.54.1.el9_8, podman 5.8.2): wrote a .container file and ran /usr/libexec/podman/quadlet -dryrun -v. Log: plans/content-expansion/evidence/logs/alma-quadlet-services.log.

    The container has no systemd, so service start and boot autostart were not verified; this page focuses on how to write the file and whether the generated unit is correct.

    ModeDirectory
    root/etc/containers/systemd/
    rootless~/.config/containers/systemd/

    The file name decides the unit name: web.container → web.service. A wrong directory produces no error — the unit simply does not exist, which is the most common “I wrote it but nothing happened”.

    [Unit]
    Description=web
    After=network-online.target
    [Container]
    Image=docker.io/library/nginx:alpine
    ContainerName=web
    PublishPort=8080:80
    Volume=webdata:/usr/share/nginx/html:Z
    Environment=NGINX_ENTRYPOINT_QUIET_LOGS=1
    [Service]
    Restart=always
    [Install]
    WantedBy=multi-user.target

    Four sections, four jobs: [Unit] dependencies and description, [Container] container arguments, [Service] restart policy, [Install] boot startup.

    Terminal window
    sudo /usr/libexec/podman/quadlet -dryrun -v
    [Unit]
    Wants=network-online.target
    After=network-online.target
    Description=web
    SourcePath=/etc/containers/systemd/web.container
    [Service]
    Restart=always
    ExecStop=/usr/bin/podman rm -v -f -i web
    Type=notify
    ExecStart=/usr/bin/podman run --name web --replace --rm --cgroups=split \
    --sdnotify=conmon -d -v webdata:/usr/share/nginx/html:Z \
    --publish 8080:80 --env NGINX_ENTRYPOINT_QUIET_LOGS=1 \
    docker.io/library/nginx:alpine

    Three places confirm the configuration: --publish, -v and --env inside ExecStart, plus whether After=network-online.target was preserved.

    Terminal window
    sudo systemctl daemon-reload
    sudo systemctl restart web.service
    systemctl status web.service --no-pager

    daemon-reload triggers Quadlet to regenerate the unit; a restart alone keeps using the old unit.

    • Missing [Install]: systemctl enable reports the unit as not found;
    • Missing dependencies: the container starts before the network and fails to bind or reach the peer;
    • Wrong directory for rootless: files in /etc do not apply to a normal user.

    Practice: write, inspect, change a parameter

    Section titled “Practice: write, inspect, change a parameter”
    Terminal window
    sudo mkdir -p /etc/containers/systemd
    sudo tee /etc/containers/systemd/lab.container >/dev/null <<'EOF'
    [Unit]
    Description=lab
    [Container]
    Image=docker.io/library/nginx:alpine
    PublishPort=8082:80
    [Install]
    WantedBy=multi-user.target
    EOF
    sudo /usr/libexec/podman/quadlet -dryrun -v | grep -E "ExecStart|PublishPort"

    Checks:

    1. ExecStart contains --publish 8082:80
    2. Change the port to 8083 and rerun dry-run; ExecStart follows
    3. After daemon-reload, systemctl list-unit-files | grep lab shows the generated unit

    常见问题

    The Quadlet file exists but systemctl cannot find the unit?

    Check the directory first (root uses /etc/containers/systemd/), then run systemctl daemon-reload; Quadlet units are generated dynamically and do not exist before the reload.

    Which field maps to ports, volumes and environment?

    PublishPort maps to --publish, Volume to -v, Environment to --env; run quadlet -dryrun -v and check ExecStart field by field.

    The container starts before the network and fails?

    Add After=network-online.target and Wants=network-online.target in [Unit] so systemd waits for the network before starting the container.