If you are using a modern Linux system like Fedora 40, you may want to configure systemd which manages services, and podman to run containers as an alternative to Docker. This setup makes it possible to run rootless containers as a normal user without worrying about giving the container global permissions.
systemd uses .service files to control what to run. For example, samba, a file server, has /usr/lib/systemd/system/samba.service
[Unit] Description=Samba AD Daemon Documentation=man:samba(8) man:samba(7) man:smb.conf(5) Wants=network-online.target After=network.target network-online.target [Service] Type=notify PIDFile=/run/samba.pid LimitNOFILE=16384 EnvironmentFile=-/etc/sysconfig/samba ExecStart=/usr/sbin/samba --foreground --no-process-group $SAMBAOPTIONS ExecReload=/bin/kill -HUP $MAINPID Environment=KRB5CCNAME=FILE:/run/samba/krb5cc_samba [Install] WantedBy=multi-user.target We want our container to start automatically on boot and restart when needed, so we need a systemd .service file. In the old days we would create a container using podman and then manually put a .service file for that container somewhere.
This works for a while but our service references an exact container. When we want to update the container, we have to re-write the service file to reference the update. On my system the service file referenced the new container in one place and the old one in the other, I forgot to update both, and systemd kept restarting my container.
Six months later, I discovered Quadlet.
Quadlet is a generator to alleviate the burden of manually writing .service files, one of many found in /usr/lib/systemd/system-generators:
# ls -1 /usr/lib/systemd/system-generators kdump-dep-generator.sh nfs-server-generator podman-system-generator rpc-pipefs-generator selinux-autorelabel-generator.sh systemd-bless-boot-generator systemd-cryptsetup-generator systemd-debug-generator systemd-fstab-generator systemd-getty-generator systemd-gpt-auto-generator systemd-hibernate-resume-generator systemd-integritysetup-generator systemd-rc-local-generator systemd-run-generator systemd-system-update-generator systemd-sysv-generator systemd-veritysetup-generator zram-generator Generators convert higer-level configuration to systemd early on during startup, when we call systemctl daemon-reload, or when we call systemctl --user daemon-reload for rootless services.
Examples
Mosquitto is a simple message broker I've chosen to run as root. After placing the .container file in the right place and running systemctl daemon-reload (or rebooting), we can run systemctl start mosquitto. All the podman commands to pull, run docker.io/library/eclipse-mosquitto:latest are taken care of for us.
# cat /etc/containers/systemd/mosquitto.container [Container] ContainerName=mosquitto HostName=hass Image=docker.io/library/eclipse-mosquitto:latest Volume=/etc/mosquitto:/mosquitto/config Volume=/mosquitto/data Volume=/mosquitto/log # mqtt PublishPort=1883:1883 The service winds up in /etc/containers/systemd/mosquitto.container:
# Automatically generated by /usr/lib/systemd/system-generators/podman-system-generator # # unifi.container [X-Container] ContainerName=mosquitto HostName=hass Image=docker.io/library/eclipse-mosquitto:latest Volume=/etc/mosquitto:/mosquitto/config Volume=/mosquitto/data Volume=/mosquitto/log # mqtt PublishPort=1883:1883 [Unit] Wants=network-online.target After=network-online.target SourcePath=/etc/containers/systemd/mosquitto.container RequiresMountsFor=%t/containers RequiresMountsFor=/etc/mosquitto [Service] Environment=PODMAN_SYSTEMD_UNIT=%n KillMode=mixed ExecStop=/usr/bin/podman rm -v -f -i --cidfile=%t/%N.cid ExecStopPost=-/usr/bin/podman rm -v -f -i --cidfile=%t/%N.cid Delegate=yes Type=notify NotifyAccess=all SyslogIdentifier=%N ExecStart=/usr/bin/podman run --name=mosquitto --cidfile=%t/%N.cid --replace --rm --cgroups=split --sdnotify=conmon -d -v /etc/mosquitto:/mosquitto/config -v /mosquitto/data -v /mosquitto/log --publish 1883:1883 --hostname hass docker.io/library/eclipse-mosquitto:latest Rootless Containers
The rootless .container files go into ~/.config/containers/systemd. I'm using one to run Home Assistant.
$ find .* -name \*.container .config/containers/systemd/homeassistant.container My container file looks like this. I also gave it permission to /dev/ttyACM0 for a ZigBee radio, and CAP_NET_RAW,CAP_NET_BIND_SERVICE to attempt to allow it to bind to any port, and monitor the network for new devices.
$ cat homeassistant.container [Unit] Description=Homeassistant [Container] AddCapability=CAP_NET_RAW,CAP_NET_BIND_SERVICE AddDevice=/dev/ttyACM0 ContainerName=homeassistant Environment=TZ=America/New_York Image=ghcr.io/home-assistant/home-assistant:latest Network=host Volume=/home/me/hass/config:/config PodmanArgs=--privileged --group-add=keep-groups # GroupAdd=keep-groups # future release? https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html [Install] WantedBy=default.target The generated service file winds up in /run/<uid>/systemd. Now an up-to-date Home Assistant container runs as my user when I start my Fedora server.
/run/user/1000/systemd$ cat generator/* # Automatically generated by /usr/lib/systemd/user-generators/podman-user-generator # [Unit] Wants=network-online.target After=network-online.target Description=Homeassistant SourcePath=/home/me/.config/containers/systemd/homeassistant.container RequiresMountsFor=%t/containers RequiresMountsFor=/home/me/hass/config [X-Container] AddCapability=CAP_NET_RAW,CAP_NET_BIND_SERVICE AddDevice=/dev/ttyACM0 ContainerName=homeassistant Environment=TZ=America/New_York Image=ghcr.io/home-assistant/home-assistant:latest Network=host Volume=/home/me/hass/config:/config PodmanArgs=--privileged --group-add=keep-groups # GroupAdd=keep-groups # future release? https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html [Install] WantedBy=default.target [Service] Environment=PODMAN_SYSTEMD_UNIT=%n KillMode=mixed ExecStop=/usr/bin/podman rm -v -f -i --cidfile=%t/%N.cid ExecStopPost=-/usr/bin/podman rm -v -f -i --cidfile=%t/%N.cid Delegate=yes Type=notify NotifyAccess=all SyslogIdentifier=%N ExecStart=/usr/bin/podman run --name=homeassistant --cidfile=%t/%N.cid --replace --rm --cgroups=split --network=host --sdnotify=conmon -d --device=/dev/ttyACM0 --cap-add=cap_net_raw,cap_net_bind_service -v /home/me/hass/config:/config --env TZ=America/New_York --privileged --group-add=keep-groups ghcr.io/home-assistant/home-assistant:latest Conclusion
systemd has many features including user-mode process management, powerful cron-like service timers, and now container management features that long-term Linux users may not be familiar with. After struggling through the sometimes-spotty documentation of this newish feature, we've managed to create a maintainable set of containerized services on our home Linux server.
Top comments (0)