diff --git a/.cockpit-podman.metadata b/.cockpit-podman.metadata
index 97077e3..1285362 100644
--- a/.cockpit-podman.metadata
+++ b/.cockpit-podman.metadata
@@ -1 +1 @@
-95ffa11c361ca2958d692219f884b10a31b424a6 SOURCES/cockpit-podman-27.1.tar.gz
+77aa54b1a76e5f42600de56f980ce4b3176d64d3 SOURCES/cockpit-podman-29.tar.gz
diff --git a/.gitignore b/.gitignore
index 9b16e3b..7b05f49 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1 @@
-SOURCES/cockpit-podman-27.1.tar.gz
+SOURCES/cockpit-podman-29.tar.gz
diff --git a/SOURCES/cockpit-podman-1914884.patch b/SOURCES/cockpit-podman-1914884.patch
deleted file mode 100644
index ce24d9b..0000000
--- a/SOURCES/cockpit-podman-1914884.patch
+++ /dev/null
@@ -1,224 +0,0 @@
-From 5aad868e606ab11ac37a58e734207f9b0007e7bf Mon Sep 17 00:00:00 2001
-From: Matej Marusak <mmarusak@redhat.com>
-Date: Sat, 16 Jan 2021 12:55:44 +0100
-Subject: [PATCH 1/2] test: Properly check status of container
-
----
- test/check-application | 61 +++++++++++++++++++++++++++++-------------
- 1 file changed, 42 insertions(+), 19 deletions(-)
-
-diff -up cockpit-podman/test/check-application.gating cockpit-podman/test/check-application
---- cockpit-podman/test/check-application.gating	2021-01-07 15:49:11.000000000 +0100
-+++ cockpit-podman/test/check-application	2021-01-18 10:00:55.782048398 +0100
-@@ -24,6 +24,8 @@ registries = ['localhost:5000', 'localho
- registries = ['localhost:5000', 'localhost:6000']
- """
- 
-+NOT_RUNNING = ["exited", "stopped"]
-+
- def checkImage(browser, name, owner):
-     browser.wait_visible("#containers-images table")
-     browser.wait_js_func("""(function (first, last) {
-@@ -99,7 +101,8 @@ class TestApplication(testlib.MachineCas
-     def waitPodContainer(self, podName, containerList):
-         if len(containerList):
-             for container in containerList:
--                self.check_container(container["id"], True, [container["name"], container["image"], container["command"], container["state"]], podName)
-+                self.waitContainer(container["id"], True, name=container["name"], image=container["image"],
-+                    cmd=container["command"], state=container["state"], pod=podName)
-         else:
-             if self.browser.val("#containers-containers-filter") == "all":
-                 self.browser.wait_in_text("#table-" + podName + " .ct-table-empty", "No containers in this pod ")
-@@ -133,22 +136,22 @@ class TestApplication(testlib.MachineCas
-         containerId = self.machine.execute("podman run -d --pod pod-1 --name test-pod-1-system alpine sleep 100").strip()
-         self.waitPodContainer("pod-1", [{ "name": "test-pod-1-system", "image": "alpine", "command": "sleep 100", "state": "running", "id": containerId }])
-         self.machine.execute("podman pod stop pod-1")
--        self.waitPodContainer("pod-1", [{ "name": "test-pod-1-system", "image": "alpine", "command": "sleep 100", "state": "exited", "id": containerId  }])
-+        self.waitPodContainer("pod-1", [{ "name": "test-pod-1-system", "image": "alpine", "command": "sleep 100", "state": NOT_RUNNING, "id": containerId  }])
-         self.filter_containers("running")
-         self.waitPodRow("pod-1", False)
- 
-         self.filter_containers("all")
-         b.set_input_text('#containers-filter', 'pod-1')
--        self.waitPodContainer("pod-1", [{ "name": "test-pod-1-system", "image": "alpine", "command": "sleep 100", "state": "exited", "id": containerId  }])
-+        self.waitPodContainer("pod-1", [{ "name": "test-pod-1-system", "image": "alpine", "command": "sleep 100", "state": NOT_RUNNING, "id": containerId  }])
-         b.set_input_text('#containers-filter', 'test-pod-1-system')
--        self.waitPodContainer("pod-1", [{ "name": "test-pod-1-system", "image": "alpine", "command": "sleep 100", "state": "exited", "id": containerId  }])
-+        self.waitPodContainer("pod-1", [{ "name": "test-pod-1-system", "image": "alpine", "command": "sleep 100", "state": NOT_RUNNING, "id": containerId  }])
- 
-         # Check Pod Actions
-         self.performPodAction("pod-1", "system", "Start")
-         self.waitPodContainer("pod-1", [{ "name": "test-pod-1-system", "image": "alpine", "command": "sleep 100", "state": "running", "id": containerId }])
- 
-         self.performPodAction("pod-1", "system", "Stop")
--        self.waitPodContainer("pod-1", [{ "name": "test-pod-1-system", "image": "alpine", "command": "sleep 100", "state": "exited", "id": containerId }])
-+        self.waitPodContainer("pod-1", [{ "name": "test-pod-1-system", "image": "alpine", "command": "sleep 100", "state": NOT_RUNNING, "id": containerId }])
- 
-         self.machine.execute("podman pod start pod-1")
-         self.waitPodContainer("pod-1", [{ "name": "test-pod-1-system", "image": "alpine", "command": "sleep 100", "state": "running", "id": containerId }])
-@@ -381,10 +384,12 @@ class TestApplication(testlib.MachineCas
-         # running busybox shown
-         if auth:
-             b.wait_visible("#containers-containers .container-name:contains('swamped-crate-system')")
--            self.check_container(system_containers["swamped-crate-system"], True, ['swamped-crate-system', 'busybox:latest', 'sleep 1000', 'running'])
-+            self.waitContainer(system_containers["swamped-crate-system"], True, name='swamped-crate-system',
-+                    image='busybox:latest', cmd="sleep 1000", state='running')
- 
-         b.wait_visible("#containers-containers .container-name:contains('swamped-crate-user')")
--        self.check_container(user_containers["swamped-crate-user"], False, ['swamped-crate-user', 'busybox:latest', 'sleep 1000', 'running'])
-+        self.waitContainer(user_containers["swamped-crate-user"], False, name='swamped-crate-user',
-+                image='busybox:latest', cmd="sleep 1000", state='running')
- 
-         # exited alpine not shown
-         b.wait_not_in_text("#containers-containers", "alpine:latest")
-@@ -395,9 +400,11 @@ class TestApplication(testlib.MachineCas
-         # exited alpine under everything list
-         b.wait_visible("#containers-containers")
-         if auth:
--            self.check_container(system_containers["test-sh-system"], True, ['test-sh-system', 'alpine:latest', 'sh', 'exited'])
-+            self.waitContainer(system_containers["test-sh-system"], True, name='test-sh-system', image='alpine:latest',
-+                    cmd='sh', state=NOT_RUNNING)
- 
--        self.check_container(user_containers["test-sh-user"], False, ['test-sh-user', 'alpine:latest', 'sh', 'exited'])
-+        self.waitContainer(user_containers["test-sh-user"], False, name='test-sh-user', image='alpine:latest',
-+                cmd='sh', state=NOT_RUNNING)
- 
-         b.click('#containers-containers tbody tr:contains("swamped-crate-user") td.pf-c-table__toggle button')
-         b.wait_visible('#containers-containers tbody tr:contains("swamped-crate-user") + tr button.btn-delete')
-@@ -415,8 +422,10 @@ class TestApplication(testlib.MachineCas
-         # show running container
-         self.filter_containers('running')
-         if auth:
--            self.check_container(system_containers["swamped-crate-system"], True, ['swamped-crate-system', 'busybox:latest', 'sleep 1000', 'running'])
--        self.check_container(user_containers["swamped-crate-user"], False, ['swamped-crate-user', 'busybox:latest', 'sleep 1000', 'running'])
-+            self.waitContainer(system_containers["swamped-crate-system"], True, name='swamped-crate-system',
-+                    image='busybox:latest', cmd="sleep 1000", state='running')
-+        self.waitContainer(user_containers["swamped-crate-user"], False, name='swamped-crate-user',
-+                image='busybox:latest', cmd="sleep 1000", state='running')
-         # check exited alpine not in running list
-         b.wait_not_in_text("#containers-containers", "alpine:latest")
- 
-@@ -587,7 +596,7 @@ class TestApplication(testlib.MachineCas
-             b.wait_collected_text("#containers-containers .container-name", "bac")
-             self.execute(False, "podman run -d --name doremi registry:2 sh")
-             b.wait_collected_text("#containers-containers .container-name", "bdoremiac")
--            b.wait_in_text("#containers-containers tbody tr:contains('doremi') > td:nth-child(6)", "exited")
-+            b.wait(lambda: b.text('#containers-containers tr:contains(doremi) td:nth-of-type(6)') in NOT_RUNNING)
-         else:
-             b.wait_collected_text("#containers-containers .container-name", "abc")
- 
-@@ -805,7 +814,8 @@ class TestApplication(testlib.MachineCas
-         container_sha = self.execute(auth, "podman inspect --format '{{.Id}}' swamped-crate").strip()
- 
-         with b.wait_timeout(5):
--            self.check_container(container_sha, auth, ['swamped-crate', 'busybox:latest', 'sh', 'running', "system" if auth else "admin"])
-+            self.waitContainer(container_sha, auth, name='swamped-crate', image='busybox:latest', cmd='sh',
-+                    state='running', owner="system" if auth else "admin")
-         # Check we show usage
-         b.wait(lambda: b.text("#containers-containers tbody tr:contains('busybox:latest') > td:nth-child(3)") != "")
-         cpu = b.text("#containers-containers tbody tr:contains('busybox:latest') > td:nth-child(3)")
-@@ -841,7 +851,7 @@ class TestApplication(testlib.MachineCas
-         b.wait(lambda: old_pid != self.execute(auth, "podman inspect --format '{{.State.Pid}}' swamped-crate".strip()))
- 
-         with b.wait_timeout(5):
--            self.check_container(container_sha, auth, ['swamped-crate', 'busybox:latest', 'sh', 'running'])
-+            self.waitContainer(container_sha, auth, name='swamped-crate', image='busybox:latest', cmd='sh', state='running')
- 
-         self.filter_containers('all')
-         b.wait_visible("#containers-containers")
-@@ -855,8 +865,8 @@ class TestApplication(testlib.MachineCas
-             b.wait_not_present('#containers-containers tr:contains("busybox:latest") + tr #Stop-dropdown ul li:has(a:contains(Checkpoint))')
-         b.click('#containers-containers tbody tr:contains("busybox:latest") + tr ul.pf-c-dropdown__menu li a:contains(Force stop)')
- 
--        self.check_container(container_sha, auth, ['swamped-crate', 'busybox:latest', 'sh'])
--        b.wait(lambda: b.text('#containers-containers tr:contains(swamped-crate) td:nth-of-type(6)') in ['stopped', 'exited'])
-+        self.waitContainer(container_sha, auth, name='swamped-crate', image='busybox:latest', cmd='sh')
-+        b.wait(lambda: b.text('#containers-containers tr:contains(swamped-crate) td:nth-of-type(6)') in NOT_RUNNING)
-         b.wait_text("#containers-containers tbody tr:contains('busybox:latest') > td:nth-child(3)", "")
-         b.wait_text("#containers-containers tbody tr:contains('busybox:latest') > td:nth-child(4)", "")
- 
-@@ -941,7 +951,7 @@ class TestApplication(testlib.MachineCas
-             b.wait_not_present(".pf-c-modal-box")
- 
-         if self.has_criu:
--            b.wait(lambda: b.text('#containers-containers tr:contains(swamped-crate) td:nth-of-type(6)') in ['stopped', 'exited'])
-+            b.wait(lambda: b.text('#containers-containers tr:contains(swamped-crate) td:nth-of-type(6)') in NOT_RUNNING)
-         else:
-             # expect proper error message
-             b.wait_in_text(".pf-c-alert.pf-m-danger", "Failed to checkpoint container swamped-crate")
-@@ -971,7 +981,7 @@ class TestApplication(testlib.MachineCas
-         b.wait_visible('#containers-containers td[data-label="Container"]:contains("busybox:latest")')
-         b.click('#containers-containers tbody tr:contains("busybox:latest") + tr button:contains(Stop) + button')
-         b.click('#containers-containers tbody tr:contains("busybox:latest") + tr ul.pf-c-dropdown__menu li a:contains(Force stop)')
--        b.wait(lambda: b.text('#containers-containers tr:contains(swamped-crate) td:nth-of-type(6)') in ['stopped', 'exited'])
-+        b.wait(lambda: b.text('#containers-containers tr:contains(swamped-crate) td:nth-of-type(6)') in NOT_RUNNING)
- 
-         # Restore the container
-         b.wait_visible('#containers-containers td[data-label="Container"]:contains("busybox:latest")')
-@@ -1238,7 +1248,9 @@ class TestApplication(testlib.MachineCas
-         b.wait_not_present("div.pf-c-modal-box")
-         b.wait_visible('#containers-containers td[data-label="Container"]:contains("busybox:latest")')
-         sha = self.execute(auth, "podman inspect --format '{{.Id}}' busybox-with-tty").strip()
--        self.check_container(sha, auth, ['busybox-with-tty', 'busybox:latest', 'sh -c "for i in $(seq 20); do sleep 1; echo $i; done; sleep infinity"', 'running', "system" if auth else "admin"])
-+        self.waitContainer(sha, auth, name='busybox-with-tty', image='busybox:latest',
-+                           cmd='sh -c "for i in $(seq 20); do sleep 1; echo $i; done; sleep infinity"',
-+                           state='running', owner="system" if auth else "admin")
-         hasTTY = self.execute(auth, "podman inspect --format '{{.Config.Tty}}' busybox-with-tty").strip()
-         self.assertEqual(hasTTY, 'true')
-         # Only works with CGroupsV2
-@@ -1352,13 +1364,13 @@ class TestApplication(testlib.MachineCas
-         b.click("div.pf-c-dropdown.pf-m-expanded ul li:nth-child(1) a")
-         b.wait_text(".pf-m-expanded .xterm-accessibility-tree > div:nth-child(3)", "/ #  disconnected ")
-         sha = self.execute(auth, "podman inspect --format '{{.Id}}' busybox-without-publish").strip()
--        self.check_container(sha, auth, ['busybox-without-publish', 'busybox:latest', '/bin/sh', 'exited'])
-+        self.waitContainer(sha, auth, name='busybox-without-publish', image='busybox:latest', state=NOT_RUNNING)
-         b.click('#containers-containers tbody tr:contains("busybox-without-publish") + tr button:contains(Start)')
--        self.check_container(sha, auth, ['busybox-without-publish', 'busybox:latest', '/bin/sh', 'running'])
-+        self.waitContainer(sha, auth, state='running')
-         b.wait_text(".pf-m-expanded .xterm-accessibility-tree > div:nth-child(1)", "/ # ")
-         b.click('#containers-containers tbody tr:contains("busybox-without-publish") + tr button:contains(Stop) + button')
-         b.click("div.pf-c-dropdown.pf-m-expanded ul li:nth-child(1) a")
--        self.check_container(sha, auth, ['busybox-without-publish', 'busybox:latest', '/bin/sh', 'exited'])
-+        self.waitContainer(sha, auth, state=NOT_RUNNING)
-         b.click('#containers-containers tr:contains("busybox-without-publish")')
- 
-         b.set_input_text('#containers-filter', 'tty')
-@@ -1375,7 +1387,7 @@ class TestApplication(testlib.MachineCas
-         self.check_images(["busybox:latest", "alpine:latest", "registry:2"], [])
- 
-         b.set_val("#containers-containers-filter", "all")
--        self.check_container(sha, auth, ['busybox-without-publish', 'busybox:latest', '/bin/sh', 'exited'])
-+        self.waitContainer(sha, auth, name='busybox-without-publish', image='busybox:latest', state=NOT_RUNNING)
-         b.click('#containers-containers tr:contains("busybox-without-publish") .pf-c-table__toggle button')
-         b.click(".pf-m-expanded a:contains('Console')")
-         b.wait_text(".pf-m-expanded .pf-c-title", "Container is not running")
-@@ -1412,12 +1424,25 @@ class TestApplication(testlib.MachineCas
-     def check_images(self, present, not_present):
-         self.check_content("images", present, not_present)
- 
--    def check_container(self, row_id, auth, expected_strings, pod="no-pod"):
--        """Check the container with row_name has the expected_string shown in the row"""
-+    def waitContainer(self, row_id, auth, name="", image="", cmd="", owner="", state=[], pod="no-pod"):
-+        """Check the container with row_name has the expected values
-+            "image" can be substring, "state" might be string or array of possible states, other are
-+            checked for exact match.
-+        """
-         sel = "#containers-containers #table-" + pod + " tbody tr[data-row-id={0}{1}]".format(row_id, auth).lower()
-         b = self.browser
--        for str in expected_strings:
--            b.wait_in_text(sel, str)
-+        if name:
-+            b.wait_text(sel + " .container-name", name)
-+        if image:
-+            b.wait_in_text(sel + " .container-block small:nth-child(2)", image)
-+        if cmd:
-+            b.wait_text(sel + " .container-block small:last-child", cmd)
-+        if owner:
-+            b.wait_text(sel + " td[data-label=Owner]", owner)
-+        if state:
-+            if not isinstance(state, list):
-+                state = [state]
-+            b.wait(lambda: b.text(sel + " td[data-label=State]") in state)
- 
-     def filter_containers(self, value):
-         """Use dropdown menu in the header to filter containers"""
diff --git a/SOURCES/cockpit-podman-1937863.patch b/SOURCES/cockpit-podman-1937863.patch
new file mode 100644
index 0000000..a802707
--- /dev/null
+++ b/SOURCES/cockpit-podman-1937863.patch
@@ -0,0 +1,24 @@
+From d4f06e0e0b9529bfa3d17c1ef73c028752ee1bb7 Mon Sep 17 00:00:00 2001
+From: Martin Pitt <martin@piware.de>
+Date: Thu, 4 Mar 2021 20:11:47 +0100
+Subject: [PATCH] test: Generalize cgroupsV2() for all rhel-8-* versions
+
+It looks like RHEL newer 8.y versions won't get cgroupsv2 either, so
+generalize the check.
+---
+ test/check-application | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/test/check-application b/test/check-application
+index 0b163ce7..a40f2d90 100755
+--- a/test/check-application
++++ b/test/check-application
+@@ -80,7 +80,7 @@ class TestApplication(testlib.MachineCase):
+         self.has_selinux = self.machine.image not in ["debian-testing", "ubuntu-stable"]
+ 
+     def cgroupsV2(self):
+-        return self.machine.image not in ["ubuntu-stable", "rhel-8-4"]
++        return self.machine.image != 'ubuntu-stable' and not self.machine.image.startswith('rhel-8')
+ 
+     def execute(self, system, cmd):
+         if system:
diff --git a/SPECS/cockpit-podman.spec b/SPECS/cockpit-podman.spec
index ea09cd9..ecf3c56 100644
--- a/SPECS/cockpit-podman.spec
+++ b/SPECS/cockpit-podman.spec
@@ -1,20 +1,19 @@
 # This spec file has been automatically updated
-Version: 27.1
-Release:        4%{?dist}
+Version: 29
+Release:        3%{?dist}
 Name:           cockpit-podman
 Summary:        Cockpit component for Podman containers
 License:        LGPLv2+
 URL:            https://github.com/cockpit-project/cockpit-podman
 
 Source0:        https://github.com/cockpit-project/cockpit-podman/releases/download/%{version}/cockpit-podman-%{version}.tar.gz
-# related bug: https://bugzilla.redhat.com/show_bug.cgi?id=1914884
-# backported:  https://github.com/cockpit-project/cockpit-podman/pull/653.patch
-Patch0:         cockpit-podman-1914884.patch
+# related bug: https://bugzilla.redhat.com/show_bug.cgi?id=1937863
+# patch:       https://patch-diff.githubusercontent.com/raw/cockpit-project/cockpit-podman/pull/688.patch
+Patch0: cockpit-podman-1937863.patch
 BuildArch:      noarch
 BuildRequires:  libappstream-glib
 
 Requires:       cockpit-bridge >= 138
-Requires:       cockpit-shell >= 138
 Requires:       podman >= 1.3.0
 
 %description
@@ -23,7 +22,7 @@ The Cockpit user interface for Podman containers.
 %prep
 %setup -q -n cockpit-podman
 
-# https://bugzilla.redhat.com/show_bug.cgi?id=1914884
+# https://bugzilla.redhat.com/show_bug.cgi?id=1937863
 %patch0 -p1
 
 %build
@@ -40,6 +39,38 @@ appstream-util validate-relax --nonet %{buildroot}/%{_datadir}/metainfo/*
 %{_datadir}/metainfo/*
 
 %changelog
+* Thu Mar 11 2021 Jindrich Novy <jnovy@redhat.com> - 29-3
+- fix gating test failure for cockpit-podman
+- Related: #1934415
+
+* Thu Mar 04 2021 Jindrich Novy <jnovy@redhat.com> - 29-2
+- gating test fix - properly install browser
+- Related: #1934415
+
+* Mon Feb 22 2021 Jindrich Novy <jnovy@redhat.com> - 29-1
+- update to https://github.com/cockpit-project/cockpit-podman/releases/tag/29
+- Related: #1883490
+
+* Fri Feb 12 2021 Jindrich Novy <jnovy@redhat.com> - 28.1-1
+- update to https://github.com/cockpit-project/cockpit-podman/releases/tag/28.1
+- Related: #1883490
+
+* Wed Feb 10 2021 Jindrich Novy <jnovy@redhat.com> - 28-4
+- readd cockpit-system
+Related: #1914884
+
+* Mon Feb 08 2021 Jindrich Novy <jnovy@redhat.com> - 28-3
+- fix gating tests for cockpit-podman - thanks for Matej Marusak
+- Related: #1883490
+
+* Sat Feb 06 2021 Jindrich Novy <jnovy@redhat.com> - 28-2
+- remove applied patch and cockpit-shell dependency
+- Related: #1883490
+
+* Sat Feb 06 2021 Jindrich Novy <jnovy@redhat.com> - 28-1
+- update to https://github.com/cockpit-project/cockpit-podman/releases/tag/28
+- Related: #1883490
+
 * Mon Jan 18 2021 Jindrich Novy <jnovy@redhat.com> - 27.1-4
 - fix "Fix gating tests of container-tools for 8.4.0"
 - Related: #1883490