I want to tell you about how to make container run very good on Linux. Sometimes people use docker-compose but it is hard when you want the server to start the container automatically when the computer turn on. This is why we use systemd with Podman. Podman has a very special thing called Quadlet. It make your container look like a real systemd service. I am only sixteen and my English is not very good because I am from other country, but I try this on my Fedora machine and it works very nice. You do not need to write very long systemd service file because Quadlet do the job for you with just small file called dot container.
First thing we must do is we need to write the configuration file. This file must go to special place so systemd can see it. The place is /etc/containers/systemd/. If you do rootful container, you must put it there. Rootful means it run with root permissions, which is good for some network ports like port 80 or 8080. We use sudo to make this file. We can use vi or nano editor. Let run this command first.
sudo vi /etc/containers/systemd/web.container
Inside this file, we write some lines. I will show you the lines and then I explain why we write it.
[Unit]
Description=Nginx demo container via Quadlet
After=local-fs.target
[Container]
ContainerName=web
Image=docker.io/library/nginx:1.27-alpine
PublishPort=8080:80
AutoUpdate=registry
Environment=NGINX_HOST=example.com
[Service]
Restart=always
[Install]
WantedBy=multi-user.target default.target
Now I explain the parts because it is very important to understand what you write. If you do not understand, you will make mistake and then nothing works. The first part is [Unit]. This is for systemd. We write Description so we know what this container is. We also write After=local-fs.target because we want the container to start only after the hard disk is ready. If hard disk is not ready, container cannot read files and it will crash.
The second part is [Container]. This is the most important part for Podman Quadlet. Here we write ContainerName. I name it web. The image is nginx:1.27-alpine. This image is very small, maybe only 20 megabytes, so it downloads very fast. The PublishPort is 8080:80. This means if you go to your browser and type localhost with port 8080, it will send the traffic inside the container to port 80. AutoUpdate is registry, this is cool because if there is new image in docker hub, Podman can update it. The Environment is NGINX_HOST=example.com, this is just to tell Nginx what is our domain name.
The third part is [Service]. We write Restart=always. This means if the container crash or stop because of some error, systemd will start it again. You do not have to wake up at night to start it yourself. The last part is [Install]. This has WantedBy. It tell systemd when to start this container when the computer boots up. We put multi-user.target and default.target so it starts during normal boot.
After we save the file, systemd does not know about it yet. Systemd is like a big boss, it needs you to tell it when something changes. We have a special generator in Podman that looks at the /etc/containers/systemd/ folder. When we reload systemd, this generator will read our web.container file and turn it into a real systemd service file. To make this happen, we must run this command in terminal.
sudo systemctl daemon-reload
This command might take one or two seconds. It does not show any message if it works good. If you see some error, maybe you made typo in your file or you put the file in wrong folder. Make sure the folder is exactly /etc/containers/systemd/ and the name ends with .container.
When the reload is finished, Podman generator has made a new file in temporary place. This place is usually /run/systemd/generator/. We do not need to go there and edit it, because if we edit it, it will be deleted when computer restarts. But we can inspect it to see what the generator made for us. We use systemctl cat command to look inside it.
sudo systemctl cat web.service | head -25
When you run this command, you will see some interesting lines. You will see SourcePath=/etc/containers/systemd/web.container. This is very good because it tells systemd that this service file is not written by hand, but it is generated from our Quadlet file. It also writes many long commands with podman run and podman rm. It does all the hard work of managing the container life cycle. You do not need to remember all those long Podman options because the generator writes them for you.
Now we are ready to start our container. We do not use podman run command. We use systemctl command because our container is now a real systemd service. Let start it with this command.
sudo systemctl start web.service
Because this is the first time we run it, systemd has to pull the Nginx image from the internet. This might take some seconds depending on how fast your internet connection is. Nginx alpine is very small, so it should be fast. After the command finishes, we want to check if it is running correctly. We use the status command.
sudo systemctl status web.service --no-pager | head -10
You should look at the output. If you see Active: active (running) in green text, then you did a very great job. It means the container is running now. If you see some red text or status is failed, you must check your journal logs to see what is wrong.
Even if systemd says it is running, I like to be very sure. I always test it with Podman tool and curl tool. First, let us check if Podman can see the container. We run this command.
sudo podman ps
This command will show a list of running containers. You should see a container with name web and image nginx. It will also show that port 8080 is redirected to port 80. Next, we will use curl to send a request to our Nginx web server. This will test if the network is working good.
curl -sI http://localhost:8080/
If everything is fine, the output will show HTTP/1.1 200 OK. It will also show some other lines like Server: nginx and the date. This means your Nginx container is working and serving web pages correctly.
You might ask why we do all this instead of just running podman run command. I tell you why. When you use Quadlet, your container is a real systemd citizen. This means you can use all systemd tools. For example, if you want to see the logs of your container, you do not need to use podman logs. You can just use journalctl.
sudo journalctl -u web.service
This is very useful because systemd manages all logs in one place. Also, if your container needs to start after another service, like a database, you can easily add After=database.service in your unit file. It makes container management very clean.
Sometimes we want to stop our container or change something inside the configuration. Let me explain how we can do this easily. If you want to stop the container, you just run this command.
sudo systemctl stop web.service
This command will stop the service and Podman will automatically remove the container. This is very good because it does not leave any dirty, stopped containers on your system disk. If you run sudo podman ps -a after stopping, you will see that the container is completely gone. This keeps your system clean.
If you want to change something, for example you want to change the port from 8080 to 9090, you must open the file /etc/containers/systemd/web.container again with your editor and change PublishPort=8080:80 to PublishPort=9090:80. After you save the file, you must run sudo systemctl daemon-reload again. If you do not run daemon-reload, systemd will still use the old configuration because it does not know you changed the file. After reloading, you just restart the service.
sudo systemctl restart web.service
Now the service will use the new port 9090. This is so much easier than manually stopping, deleting, and running podman commands with different port arguments.
I also want to share some problems I had when I first tried this. One time, my container did not start and I got very confused. First problem is typo in the folder name. You must make sure you put the file in /etc/containers/systemd/. If you put it in /etc/systemd/system/ by mistake, systemd will get confused because it does not know what a .container file is. The systemd program only understands .service files. The Quadlet generator is the one that looks inside /etc/containers/systemd/ and translates it.
Another problem is file permissions. Since we are doing rootful containers, the file must be owned by root and have correct permissions. You can check permissions using ls -l /etc/containers/systemd/web.container. It should be owned by root. Also, sometimes the container fails to start because the port you chose is already used by another program. For example, if you already have Apache running on port 8080, Nginx cannot start. You will see error in journal logs saying address already in use. You can fix this by changing the port in the .container file to something else, like 8081.
In conclusion, using Podman Quadlet with .container file is very nice way to run your containers as systemd services. It is much cleaner than writing long systemd service files by hand or using external scripts. It makes your system manage the container lifecycles easily, and you can start, stop, and auto update them with simple commands.
