DEV Community

Cover image for How to Configure Podman Auto-Updates and Health-Check Rollbacks
Project-42
Project-42

Posted on

How to Configure Podman Auto-Updates and Health-Check Rollbacks

In this post, I wanted to test podman autoupdate and how the "self healing" (rollback) action works,
(you can check this post to see how to setup local repository and modify a wordpress image How to Set Up a Local Podman Registry and Customize Podman Images)

1. Change wordpress container

I currently run the docker.io wordpress image as you can see below

[|=| raspi in ~/pods/p42_wordpress ]$ podman ps
CONTAINER ID  IMAGE                               STATUS         NAMES
414c230b5e8c  docker.io/library/mariadb:latest    Up 5 days      mariadb
f95dbc9e18a6  docker.io/library/wordpress:latest  Up 5 days      systemd-wordpress
39954e773035  docker.io/library/registry:2        Up 42 minutes  local-registry

[|=| raspi in ~/pods/p42_wordpress ]$
Enter fullscreen mode Exit fullscreen mode

To change to my local image and make podman autoupdate to work, I have modified the lines entries for Image and AutoUpdate

[|=| raspi in ~/pods/p42_wordpress ]$ cat ~/.config/containers/systemd/wordpress.container
[Unit]
Description=WordPress Web Server
After=mariadb.service

[Container]
Image=localhost:5000/p42_wordpress:v1
AutoUpdate=registry
Environment=WORDPRESS_DB_HOST=mariadb:3306
Environment=WORDPRESS_DB_USER=wordpress
WORDPRESS_DB_PASSWORD=wordpress
WORDPRESS_DB_NAME=wordpress
Volume=/home/solifugo/pods/wordpress/wp-content:/var/www/html/wp-content
PublishPort=8042:80
Network=wp.network

[Service]
Restart=always

[Install]
WantedBy=multi-user.target default.target

[|=| raspi in ~/pods/p42_wordpress ]$ systemctl --user daemon-reload
[|=| raspi in ~/pods/p42_wordpress ]$
Enter fullscreen mode Exit fullscreen mode

2. Start the service with the new image

To make sure we are using the local image, we will execute the following:

[|=| raspi in ~/pods/p42_wordpress ]$ systemctl --user stop wordpress.service
[|=| raspi in ~/pods/p42_wordpress ]$ podman rm -f wordpress
[|=| raspi in ~/pods/p42_wordpress ]$ systemctl --user daemon-reload
Enter fullscreen mode Exit fullscreen mode

We can now start the service with local image:

[|=| raspi in ~/pods/p42_wordpress ]$ systemctl --user start wordpress.service

[|=| raspi in ~/pods/p42_wordpress ]$ podman ps
CONTAINER ID  IMAGE                             STATUS             NAMES
39954e773035  docker.io/library/registry:2      Up About an hour   local-registry
de197041e7b9  docker.io/library/mariadb:latest  Up 7 minutes       mariadb
72cef8edcee3  localhost:5000/p42_wordpress:v1   Up About a minute  systemd-wordpress

[|=| raspi in ~/pods/p42_wordpress ]$
Enter fullscreen mode Exit fullscreen mode

3. Modify the Containerfile

Let's add a "version" file or a simple comment so we can verify the change later.

[|=| raspi in ~/pods/p42_wordpress ]$ cat Containerfile
# Use the official WordPress image as a base
FROM docker.io/library/wordpress:latest

# Example: Increase the upload size by creating a custom PHP config
RUN echo 'file_uploads = On\nmemory_limit = 256M\nupload_max_filesize = 64M\npost_max_size = 64M\nmax_execution_time = 300' > /usr/local/etc/php/conf.d/uploads.ini

# NEW: Add a simple version file we can check
RUN echo "Update Test - Version 2.0" > /image_version.txt

[|=| raspi in ~/pods/p42_wordpress ]$
Enter fullscreen mode Exit fullscreen mode

We can now Build it and and Push the "New" v1 (We are going to overwrite the :v1 tag for simplicity)

[|=| raspi in ~/pods/p42_wordpress ]$ podman build -t localhost:5000/p42_wordpress:v1 .
STEP 1/3: FROM docker.io/library/wordpress:latest
STEP 2/3: RUN echo 'file_uploads = On\nmemory_limit = 256M\nupload_max_filesize = 64M\npost_max_size = 64M\nmax_execution_time = 300' > /usr/local/etc/php/conf.d/uploads.ini
--> Using cache 6213e109406f3ce3ac791b8a8b2d57cf3d678b7c4bb8ede40be427c149b86b18
--> 6213e109406f
STEP 3/3: RUN echo "Update Test - Version 2.0" > /image_version.txt
COMMIT localhost:5000/p42_wordpress:v1
--> fe462c1e2d14
Successfully tagged localhost:5000/p42_wordpress:v1
fe462c1e2d14c4e87261be851e6b76cf686ff4dbedf4dc050b77a0ea9ef537cc

[|=| raspi in ~/pods/p42_wordpress ]$

[|=| raspi in ~/pods/p42_wordpress ]$ podman push localhost:5000/p42_wordpress:v1
Getting image source signatures
Copying blob 38c51b789feb skipped: already exists
[...]
Copying blob 9926eecc266a done   |
Copying blob bd9ddc54bea9 skipped: already exists
Copying blob 1fb5b7dba217 skipped: already exists
Copying blob ae1a66031d13 skipped: already exists
Copying config fe462c1e2d done   |
Writing manifest to image destination

[|=| raspi in ~/pods/p42_wordpress ]$
Enter fullscreen mode Exit fullscreen mode

4. Testing Autoupdate

Now we can see how we have an update pending:

[|=| raspi in ~/pods/p42_wordpress ]$ podman auto-update --dry-run
UNIT               IMAGE                             POLICY      UPDATED
wordpress.service  localhost:5000/p42_wordpress:v1   registry    pending
mariadb.service    docker.io/library/mariadb:latest  registry    false

[|=| raspi in ~/pods/p42_wordpress ]$
Enter fullscreen mode Exit fullscreen mode

Let's execute the update and confirm we can see the new file added in the image:

[|=| raspi in ~/pods/p42_wordpress ]$ podman auto-update
Trying to pull localhost:5000/p42_wordpress:v1...
Getting image source signatures
Copying blob 9860aa0d13b7 skipped: already exists
[...]
Copying blob 723c4da029f2 skipped: already exists
Copying config fe462c1e2d done   |
Writing manifest to image destination
UNIT               IMAGE                             POLICY      UPDATED
mariadb.service    docker.io/library/mariadb:latest  registry    false
wordpress.service  localhost:5000/p42_wordpress:v1   registry    true

[|=| raspi in ~/pods/p42_wordpress ]$ podman exec systemd-wordpress cat /image_version.txt
Update Test - Version 2.0

[|=| raspi in ~/pods/p42_wordpress ]$
Enter fullscreen mode Exit fullscreen mode

5. Testing "self-healing" or "auto-rollback"

One of the best things about podman autoupdate, is the fact that will rollback an update if it is unable to complete the deployment after the update

Let's modify the local image again this time adding a break point as below:

[|=| raspi in ~/pods/p42_wordpress ]$ cat Containerfile
# Use the official WordPress image as a base
FROM docker.io/library/wordpress:latest

# Example: Increase the upload size by creating a custom PHP config
RUN echo 'file_uploads = On\nmemory_limit = 256M\nupload_max_filesize = 64M\npost_max_size = 64M\nmax_execution_time = 300' > /usr/local/etc/php/conf.d/uploads.ini

# BROKEN STEP: Force the container to fail on startup
# This tries to run a non-existent script
ENTRYPOINT ["/bin/bash", "-c", "exit 1"]

# NEW: Add a simple version file we can check
RUN echo "Update Test - Version 3.0" > /image_version.txt

[|=| raspi in ~/pods/p42_wordpress ]$

[|=| raspi in ~/pods/p42_wordpress ]$ podman build -t localhost:5000/p42_wordpress:v1 .
STEP 1/4: FROM docker.io/library/wordpress:latest
STEP 2/4: RUN echo 'file_uploads = On\nmemory_limit = 256M\nupload_max_filesize = 64M\npost_max_size = 64M\nmax_execution_time = 300' > /usr/local/etc/php/conf.d/uploads.ini
--> Using cache 6213e109406f3ce3ac791b8a8b2d57cf3d678b7c4bb8ede40be427c149b86b18
--> 6213e109406f
STEP 3/4: ENTRYPOINT ["/bin/bash", "-c", "exit 1"]
--> Using cache 009309cf75edc7d7d12c7898cf6a464a90f472c7e8c9762f24d0a7488ae6608f
--> 009309cf75ed
STEP 4/4: RUN echo "Update Test - Version 3.0" > /image_version.txt
COMMIT localhost:5000/p42_wordpress:v1
--> 3a3adcb35052
Successfully tagged localhost:5000/p42_wordpress:v1
3a3adcb35052464966b9c87f4f2f9f41d7e920e8b79b944a6346291cd6ca158d

[|=| raspi in ~/pods/p42_wordpress ]$

[|=| raspi in ~/pods/p42_wordpress ]$ podman push localhost:5000/p42_wordpress:v1
Getting image source signatures
[...]
Copying blob 3c09e98b8858 done   |
Copying blob d905cab7558a skipped: already exists
Copying blob 69a4f4da7ad4 skipped: already exists
Copying blob 3b5472ea1c23 skipped: already exists
Copying blob 843bf501e50b skipped: already exists
Copying config 3a3adcb350 done   |
Writing manifest to image destination

[|=| raspi in ~/pods/p42_wordpress ]$
Enter fullscreen mode Exit fullscreen mode

To make sure podman is aware of the issue, we can add Health Checks and make sure podman notifies systemd that health check is passed.

Remember that the health commands are executed inside the container, so the port will be 80 and not 8042 that is the host published port.

[|=| raspi in ~/pods/p42_wordpress ]$ cat ~/.config/containers/systemd/wordpress.container
[Unit]
Description=WordPress Web Server
After=mariadb.service

[Container]
Image=localhost:5000/p42_wordpress:v1
AutoUpdate=registry
#NEW Health Checks commands
HealthCmd=curl --fail http://localhost:80
HealthInterval=5s
HealthRetries=1
# Force Podman to wait for the health check before saying "I'm done"
Notify=healthy
Environment=WORDPRESS_DB_HOST=mariadb:3306 WORDPRESS_DB_USER=wordpress WORDPRESS_DB_PASSWORD=wordpress WORDPRESS_DB_NAME=wordpress
Volume=/home/solifugo/pods/wordpress/wp-content:/var/www/html/wp-content
PublishPort=8042:80
Network=wp.network

[Service]
Type=notify
# Disable the immediate restart loop so it doesn't "Repeat too quickly"
Restart=no
# Give the health check time to actually fail
TimeoutStartSec=40

[Install]
WantedBy=multi-user.target default.target

[|=| raspi in ~/pods/p42_wordpress ]$ systemctl --user daemon-reload
[|=| raspi in ~/pods/p42_wordpress ]$ podman ps
CONTAINER ID  IMAGE                             STATUS                  NAMES
39954e773035  docker.io/library/registry:2      Up 2 hours              local-registry
de197041e7b9  docker.io/library/mariadb:latest  Up About an hour        mariadb
375d7658f685  localhost:5000/p42_wordpress:v1   Up 4 minutes (healthy)  systemd-wordpress
[|=| raspi in ~/pods/p42_wordpress ]$ podman auto-update --dry-run
UNIT               IMAGE                             POLICY      UPDATED
wordpress.service  localhost:5000/p42_wordpress:v1   registry    pending
mariadb.service    docker.io/library/mariadb:latest  registry    false

[|=| raspi in ~/pods/p42_wordpress ]$
Enter fullscreen mode Exit fullscreen mode

When we execute the update, podman will rollback the update and restart the container again

[|=| raspi in ~/pods/p42_wordpress ]$ podman auto-update
Trying to pull localhost:5000/p42_wordpress:v1...
Getting image source signatures
[...]
Copying blob 843bf501e50b skipped: already exists
Copying config 3a3adcb350 done   |
Writing manifest to image destination
UNIT               IMAGE                             POLICY      UPDATED
mariadb.service    docker.io/library/mariadb:latest  registry    false
wordpress.service  localhost:5000/p42_wordpress:v1   registry    rolled back

[|=| raspi in ~/pods/p42_wordpress ]$
[|=| raspi in ~/pods/p42_wordpress ]$ podman ps
CONTAINER ID  IMAGE                             STATUS                  NAMES
39954e773035  docker.io/library/registry:2      Up 2 hours              local-registry
de197041e7b9  docker.io/library/mariadb:latest  Up 2 hours              mariadb
9cb996a3112a  localhost:5000/p42_wordpress:v1   Up 9 seconds (healthy)  systemd-wordpress

[|=| raspi in ~/pods/p42_wordpress ]$
Enter fullscreen mode Exit fullscreen mode

6. Setting systemd podman auto-update execution

Podman comes with a pre-configured systemd timer that runs podman auto-update.
Just make sure you edit the timer and enable it (by default would be executed every day midnight)

[|=| raspi in ~/pods/p42_wordpress ]$ systemctl --user edit podman-auto-update.timer
Successfully installed edited file '/home/solifugo/.config/systemd/user/podman-auto-update.timer.d/override.conf'.
[|=| raspi in ~/pods/p42_wordpress ]$ cat /home/solifugo/.config/systemd/user/podman-auto-update.timer.d/override.conf
[Timer]
OnCalendar=
OnCalendar=Mon *-*-* 03:00:00

[|=| raspi in ~/pods/p42_wordpress ]$
[|=| raspi in ~/pods/p42_wordpress ]$ systemctl --user enable --now podman-auto-update.timer
Created symlink '/home/solifugo/.config/systemd/user/timers.target.wants/podman-auto-update.timer''/usr/lib/systemd/user/podman-auto-update.timer'.

[|=| raspi in ~/pods/p42_wordpress ]$
[|=| raspi in ~/pods/p42_wordpress ]$ systemctl --user list-timers podman-auto-update.timer
NEXT                          LEFT LAST PASSED UNIT                     ACTIVATES
Mon 2026-03-02 03:10:04 GMT 5 days -         - podman-auto-update.timer podman-auto-update.service

1 timers listed.
Pass --all to see loaded but inactive timers, too.
[|=| raspi in ~/pods/p42_wordpress ]$
Enter fullscreen mode Exit fullscreen mode

Top comments (0)