Repository: osbuild/osbuild-composer
Pull request #2618 (2022-05-05 10:18:06)

prometheus: add tenant label
This pull request includes:

- [ ] adequate testing for the new functionality or fixed issue
- [ ] adequate documentation informing people about the change such as
  - [ ] submit a PR for the [guides](https://github.com/osbuild/guides) repository if this PR changed any behavior described there: https://www.osbuild.org/guides/


kingsleyzissou: To fix the linting issue I think you need to fix the `JobQueueTest` file. There are a few errors there. And then you'll need to add the same functionality to the `dbjobqueue`. 

Also could you please add a detailed commit message :)

I added a couple of inline comments (it's a bit of a nitpick). But otherwise looks good.

Patch:
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index a241031b4..1f972fb6a 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -64,10 +64,10 @@ init:
 
 .RPM_RUNNERS_RHEL: &RPM_RUNNERS_RHEL
   RUNNER:
-    - aws/rhel-8.6-nightly-x86_64
-    - aws/rhel-8.6-nightly-aarch64
-    - aws/rhel-9.0-nightly-x86_64
-    - aws/rhel-9.0-nightly-aarch64
+    #- aws/rhel-8.6-nightly-x86_64
+    #- aws/rhel-8.6-nightly-aarch64
+    #- aws/rhel-9.0-nightly-x86_64
+    #- aws/rhel-9.0-nightly-aarch64
     - aws/centos-stream-9-x86_64
     - aws/centos-stream-9-aarch64
 
@@ -83,12 +83,12 @@ RPM:
   parallel:
     matrix:
       - RUNNER:
-          - aws/fedora-34-x86_64
-          - aws/fedora-34-aarch64
           - aws/fedora-35-x86_64
           - aws/fedora-35-aarch64
-          - aws/rhel-8.5-ga-x86_64
-          - aws/rhel-8.5-ga-aarch64
+          - aws/rhel-8.6-ga-x86_64
+          - aws/rhel-8.6-ga-aarch64
+          - aws/rhel-9.0-ga-x86_64
+          - aws/rhel-9.0-ga-aarch64
           - aws/centos-stream-8-x86_64
           - aws/centos-stream-8-aarch64
       - <<: *RPM_RUNNERS_RHEL
@@ -118,7 +118,7 @@ Container:
   parallel:
     matrix:
       - RUNNER:
-          - aws/rhel-8.5-ga-x86_64
+          - aws/rhel-8.6-ga-x86_64
 
 Packer:
   stage: test
@@ -164,8 +164,6 @@ Base:
   parallel:
     matrix:
       - RUNNER:
-          - aws/fedora-34-x86_64
-          - aws/fedora-34-aarch64
           - aws/fedora-35-x86_64
           - aws/fedora-35-aarch64
           - aws/rhel-8.5-ga-x86_64
@@ -173,28 +171,28 @@ Base:
           - aws/centos-stream-8-x86_64
           - aws/centos-stream-8-aarch64
       - RUNNER:
-          - aws/rhel-8.6-nightly-x86_64
-          - aws/rhel-8.6-nightly-aarch64
-          - aws/rhel-9.0-nightly-x86_64
-          - aws/rhel-9.0-nightly-aarch64
+          #- aws/rhel-8.6-nightly-x86_64
+          #- aws/rhel-8.6-nightly-aarch64
+          #- aws/rhel-9.0-nightly-x86_64
+          #- aws/rhel-9.0-nightly-aarch64
           - aws/centos-stream-9-x86_64
           - aws/centos-stream-9-aarch64
         INTERNAL_NETWORK: ["true"]
 
-Manifests:
-  stage: test
-  extends: .terraform
-  rules:
-    - !reference [.upstream_rules, rules]
-    - !reference [.nightly_rules, rules]
-  script:
-    - schutzbot/deploy.sh
-    - /usr/libexec/tests/osbuild-composer/manifest_tests.sh
-  parallel:
-    matrix:
-      - RUNNER:
-          - aws/rhel-9.0-nightly-x86_64
-        INTERNAL_NETWORK: ["true"]
+#Manifests:
+#  stage: test
+#  extends: .terraform
+#  rules:
+#    - !reference [.upstream_rules, rules]
+#    - !reference [.nightly_rules, rules]
+#  script:
+#    - schutzbot/deploy.sh
+#    - /usr/libexec/tests/osbuild-composer/manifest_tests.sh
+#  parallel:
+#    matrix:
+#      - RUNNER:
+#          - aws/rhel-9.0-nightly-x86_64
+#        INTERNAL_NETWORK: ["true"]
 
 .regression:
   stage: test
@@ -208,8 +206,6 @@ Manifests:
   parallel:
     matrix:
       - RUNNER:
-          - aws/fedora-34-x86_64
-          - aws/fedora-34-aarch64
           - aws/fedora-35-x86_64
           - aws/fedora-35-aarch64
           - aws/centos-stream-8-x86_64
@@ -217,10 +213,10 @@ Manifests:
       - RUNNER:
           - aws/rhel-8.5-ga-x86_64
           - aws/rhel-8.5-ga-aarch64
-          - aws/rhel-8.6-nightly-x86_64
-          - aws/rhel-8.6-nightly-aarch64
-          - aws/rhel-9.0-nightly-x86_64
-          - aws/rhel-9.0-nightly-aarch64
+          #- aws/rhel-8.6-nightly-x86_64
+          #- aws/rhel-8.6-nightly-aarch64
+          #- aws/rhel-9.0-nightly-x86_64
+          #- aws/rhel-9.0-nightly-aarch64
           - aws/centos-stream-9-x86_64
           - aws/centos-stream-9-aarch64
         INTERNAL_NETWORK: ["true"]
@@ -265,23 +261,23 @@ regression-no-explicit-rootfs-definition:
   variables:
     SCRIPT: regression-no-explicit-rootfs-definition.sh
 
-Image Tests:
-  stage: test
-  extends: .terraform
-  rules:
-    - !reference [.nightly_rules, rules]
-  script:
-    - schutzbot/deploy.sh
-    - /usr/libexec/tests/osbuild-composer/image_tests.sh
-  parallel:
-    matrix:
-      - RUNNER:
-          - aws/rhel-8.6-nightly-x86_64
-          - aws/rhel-8.6-nightly-aarch64
-          # See https://github.com/osbuild/osbuild-composer/issues/1819 and related issues
-          - aws/rhel-9.0-nightly-x86_64
-          - aws/rhel-9.0-nightly-aarch64
-        INTERNAL_NETWORK: ["true"]
+#Image Tests:
+#  stage: test
+#  extends: .terraform
+#  rules:
+#    - !reference [.nightly_rules, rules]
+#  script:
+#    - schutzbot/deploy.sh
+#    - /usr/libexec/tests/osbuild-composer/image_tests.sh
+#  parallel:
+#    matrix:
+#      - RUNNER:
+#          #- aws/rhel-8.6-nightly-x86_64
+#          #- aws/rhel-8.6-nightly-aarch64
+#          # See https://github.com/osbuild/osbuild-composer/issues/1819 and related issues
+#          #- aws/rhel-9.0-nightly-x86_64
+#          #- aws/rhel-9.0-nightly-aarch64
+#        INTERNAL_NETWORK: ["true"]
 
 OSTree:
   stage: test
@@ -296,8 +292,8 @@ OSTree:
       - RUNNER:
           - rhos-01/fedora-35-x86_64
           - rhos-01/rhel-8.5-ga-x86_64
-          - rhos-01/rhel-8.6-nightly-x86_64-large
-          - rhos-01/rhel-9.0-nightly-x86_64
+          #- rhos-01/rhel-8.6-nightly-x86_64-large
+          #- rhos-01/rhel-9.0-nightly-x86_64
           - rhos-01/centos-stream-8-x86_64
           - rhos-01/centos-stream-9-x86_64
 
@@ -311,8 +307,8 @@ New OSTree:
     matrix:
       - RUNNER:
           - rhos-01/fedora-35-x86_64-large
-          - rhos-01/rhel-8.6-nightly-x86_64-large
-          - rhos-01/rhel-9.0-nightly-x86_64-large
+          #- rhos-01/rhel-8.6-nightly-x86_64-large
+          #- rhos-01/rhel-9.0-nightly-x86_64-large
           - rhos-01/centos-stream-8-x86_64-large
           - rhos-01/centos-stream-9-x86_64-large
 
@@ -325,9 +321,9 @@ OSTree simplified installer:
   parallel:
     matrix:
       - RUNNER:
-          - rhos-01/rhel-8.6-nightly-x86_64-large
+          #- rhos-01/rhel-8.6-nightly-x86_64-large
           - rhos-01/centos-stream-8-x86_64-large
-          - rhos-01/rhel-9.0-nightly-x86_64-large
+          #- rhos-01/rhel-9.0-nightly-x86_64-large
           - rhos-01/centos-stream-9-x86_64-large
 
 OSTree raw image:
@@ -345,9 +341,9 @@ OSTree raw image:
   parallel:
     matrix:
       - RUNNER:
-          - rhos-01/rhel-8.6-nightly-x86_64-large
+          #- rhos-01/rhel-8.6-nightly-x86_64-large
           - rhos-01/centos-stream-8-x86_64-large
-          - rhos-01/rhel-9.0-nightly-x86_64-large
+          #- rhos-01/rhel-9.0-nightly-x86_64-large
           - rhos-01/centos-stream-9-x86_64-large
 
 Rebase OSTree:
@@ -359,8 +355,8 @@ Rebase OSTree:
   parallel:
     matrix:
       - RUNNER:
-          - rhos-01/rhel-8.6-nightly-x86_64-large
-          - rhos-01/rhel-9.0-nightly-x86_64-large
+          #- rhos-01/rhel-8.6-nightly-x86_64-large
+          #- rhos-01/rhel-9.0-nightly-x86_64-large
           - rhos-01/centos-stream-8-x86_64-large
           - rhos-01/centos-stream-9-x86_64-large
 
@@ -378,8 +374,8 @@ Rebase OSTree:
     RUNNER:
       - aws/centos-stream-8-x86_64
       - aws/rhel-8.5-ga-x86_64
-      - aws/rhel-8.6-nightly-x86_64
-      - aws/rhel-9.0-nightly-x86_64
+      #- aws/rhel-8.6-nightly-x86_64
+      #- aws/rhel-9.0-nightly-x86_64
       - aws/centos-stream-9-x86_64
     INTERNAL_NETWORK: ["true"]
 
@@ -391,7 +387,6 @@ Rebase OSTree:
 
 .fedora_runners: &fedora_runners
     RUNNER:
-      - aws/fedora-34-x86_64
       - aws/fedora-35-x86_64
 
 .integration_fedora:
@@ -450,6 +445,7 @@ cross-distro.sh:
     - edge-commit
     - gcp
     - vsphere
+    - edge-commit generic.s3
 
 API:
   stage: test
@@ -466,8 +462,8 @@ API:
       - <<: *API_TESTS
         RUNNER:
           - aws/rhel-8.5-ga-x86_64
-          - aws/rhel-8.6-nightly-x86_64
-          - aws/rhel-9.0-nightly-x86_64
+          #- aws/rhel-8.6-nightly-x86_64
+          #- aws/rhel-9.0-nightly-x86_64
         INTERNAL_NETWORK: ["true"]
       - IMAGE_TYPE: ["edge-commit"]
         RUNNER:
@@ -487,8 +483,8 @@ API:
       - RUNNER:
           - rhos-01/centos-stream-8-x86_64
           - rhos-01/rhel-8.5-ga-x86_64
-          - rhos-01/rhel-8.6-nightly-x86_64
-          - rhos-01/rhel-9.0-nightly-x86_64
+          #- rhos-01/rhel-8.6-nightly-x86_64
+          #- rhos-01/rhel-9.0-nightly-x86_64
           - rhos-01/centos-stream-9-x86_64
 
 libvirt.sh:
@@ -595,8 +591,8 @@ Installer:
   parallel:
     matrix:
       - RUNNER:
-          - rhos-01/rhel-8.6-nightly-x86_64
-          - rhos-01/rhel-9.0-nightly-x86_64
+          #- rhos-01/rhel-8.6-nightly-x86_64
+          #- rhos-01/rhel-9.0-nightly-x86_64
           - rhos-01/centos-stream-9-x86_64
 
 SCHEDULED_CLOUD_CLEANER:
diff --git a/Schutzfile b/Schutzfile
index 159211eff..4abaa28b8 100644
--- a/Schutzfile
+++ b/Schutzfile
@@ -1,81 +1,4 @@
 {
-  "fedora-34": {
-    "dependencies": {
-      "osbuild": {
-        "commit": "b57376d1607587d3739eb2c754ba6b27e272198a"
-      }
-    },
-    "repos": [
-      {
-        "file": "/etc/yum.repos.d/fedora.repo",
-        "x86_64": [
-          {
-            "title": "fedora",
-            "name": "fedora",
-            "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/f34/f34-x86_64-fedora-20210512"
-          }
-        ],
-        "aarch64": [
-          {
-            "title": "fedora",
-            "name": "fedora",
-            "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/f34/f34-aarch64-fedora-20210512"
-          }
-        ]
-      },
-      {
-        "file": "/etc/yum.repos.d/fedora-modular.repo",
-        "x86_64": [
-          {
-            "title": "fedora-modular",
-            "name": "fedora-modular",
-            "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/f34/f34-x86_64-fedora-modular-20210512"
-          }
-        ],
-        "aarch64": [
-          {
-            "title": "fedora-modular",
-            "name": "fedora-modular",
-            "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/f34/f34-aarch64-fedora-modular-20210512"
-          }
-        ]
-      },
-      {
-        "file": "/etc/yum.repos.d/fedora-updates.repo",
-        "x86_64": [
-          {
-            "title": "updates",
-            "name": "updates",
-            "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/f34/f34-x86_64-updates-released-20220519"
-          }
-        ],
-        "aarch64": [
-          {
-            "title": "updates",
-            "name": "updates",
-            "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/f34/f34-aarch64-updates-released-20220519"
-          }
-        ]
-      },
-      {
-        "file": "/etc/yum.repos.d/fedora-updates-modular.repo",
-        "x86_64": [
-          {
-            "title": "updates-modular",
-            "name": "updates-modular",
-            "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/f34/f34-x86_64-updates-released-modular-20220519"
-          }
-        ],
-        "aarch64": [
-          {
-            "title": "updates-modular",
-            "name": "updates-modular",
-            "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/f34/f34-aarch64-updates-released-modular-20220519"
-          }
-        ]
-      }
-    ]
-  },
   "fedora-35": {
     "dependencies": {
       "osbuild": {
@@ -170,106 +93,28 @@
   "rhel-8.6": {
     "dependencies": {
       "osbuild": {
-        "commit": "e51d08ba9ee84036d18b19d4af70d3f8a063e2c0"
-      }
-    },
-    "repos": [
-      {
-        "file": "/etc/yum.repos.d/rhel8internal.repo",
-        "x86_64": [
-          {
-            "title": "RHEL-8-RPMREPO-NIGHTLY-BaseOS",
-            "name": "baseos",
-            "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-x86_64-baseos-n8.6-20220301"
-          },
-          {
-            "title": "RHEL-8-RPMREPO-NIGHTLY-AppStream",
-            "name": "appstream",
-            "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-x86_64-appstream-n8.6-20220301"
-          },
-          {
-            "title": "RHEL-8-RPMREPO-NIGHTLY-CRB",
-            "name": "crb",
-            "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-x86_64-crb-n8.6-20220301"
-          }
-        ],
-        "aarch64": [
-          {
-            "title": "RHEL-8-RPMREPO-NIGHTLY-BaseOS",
-            "name": "baseos",
-            "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-aarch64-baseos-n8.6-20220301"
-          },
-          {
-            "title": "RHEL-8-RPMREPO-NIGHTLY-AppStream",
-            "name": "appstream",
-            "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-aarch64-appstream-n8.6-20220301"
-          },
-          {
-            "title": "RHEL-8-RPMREPO-NIGHTLY-CRB",
-            "name": "crb",
-            "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-aarch64-crb-n8.6-20220301"
-          }
-        ]
+        "commit": "7b0e1fe5fdcf1d769a7bf1df0be7694eea28b24c"
       }
-    ]
+    }
   },
   "rhel-9.0": {
     "dependencies": {
       "osbuild": {
-        "commit": "e51d08ba9ee84036d18b19d4af70d3f8a063e2c0"
+        "commit": "7b0e1fe5fdcf1d769a7bf1df0be7694eea28b24c"
       }
-    },
-    "repos": [
-      {
-        "file": "/etc/yum.repos.d/rhel9internal.repo",
-        "x86_64": [
-          {
-            "title": "RHEL-9-RPMREPO-NIGHTLY-BaseOS",
-            "name": "baseos",
-            "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el9/el9-x86_64-baseos-n9.0-20220301"
-          },
-          {
-            "title": "RHEL-9-RPMREPO-NIGHTLY-AppStream",
-            "name": "appstream",
-            "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el9/el9-x86_64-appstream-n9.0-20220301"
-          },
-          {
-            "title": "RHEL-9-RPMREPO-NIGHTLY-CRB",
-            "name": "crb",
-            "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el9/el9-x86_64-crb-n9.0-20220301"
-          }
-        ],
-        "aarch64": [
-          {
-            "title": "RHEL-9-RPMREPO-NIGHTLY-BaseOS",
-            "name": "baseos",
-            "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el9/el9-aarch64-baseos-n9.0-20220301"
-          },
-          {
-            "title": "RHEL-9-RPMREPO-NIGHTLY-AppStream",
-            "name": "appstream",
-            "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el9/el9-aarch64-appstream-n9.0-20220301"
-          },
-          {
-            "title": "RHEL-9-RPMREPO-NIGHTLY-CRB",
-            "name": "crb",
-            "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el9/el9-aarch64-crb-n9.0-20220301"
-          }
-        ]
-      }
-    ]
+    }
   },
   "centos-8": {
     "dependencies": {
       "osbuild": {
-        "commit": "e51d08ba9ee84036d18b19d4af70d3f8a063e2c0"
+        "commit": "7b0e1fe5fdcf1d769a7bf1df0be7694eea28b24c"
       }
     }
   },
   "centos-9": {
     "dependencies": {
       "osbuild": {
-        "commit": "e51d08ba9ee84036d18b19d4af70d3f8a063e2c0"
+        "commit": "7b0e1fe5fdcf1d769a7bf1df0be7694eea28b24c"
       }
     }
   },
diff --git a/cmd/osbuild-service-maintenance/db.go b/cmd/osbuild-service-maintenance/db.go
new file mode 100644
index 000000000..8943b7bb4
--- /dev/null
+++ b/cmd/osbuild-service-maintenance/db.go
@@ -0,0 +1,49 @@
+package main
+
+import (
+	"fmt"
+	"time"
+
+	"github.com/sirupsen/logrus"
+
+	"github.com/osbuild/osbuild-composer/internal/jobqueue/dbjobqueue"
+)
+
+func DBCleanup(dbURL string, dryRun bool, cutoff time.Time) error {
+	jobs, err := dbjobqueue.New(dbURL)
+	if err != nil {
+		return err
+	}
+
+	// The results of these jobs take up the most space and can contain sensitive data. Delete
+	// them after a while.
+	jobsByType, err := jobs.JobsUptoByType([]string{"manifest-id-only", "depsolve"}, cutoff)
+	if err != nil {
+		return fmt.Errorf("Error querying jobs: %v", err)
+	}
+
+	for k, v := range jobsByType {
+		logrus.Infof("Deleting results from %d %s jobs", len(v), k)
+		if dryRun {
+			logrus.Info("Dry run, skipping deletion of jobs")
+			continue
+		}
+
+		// Delete results in chunks to avoid starving the rds instance
+		for i := 0; i < len(v); i += 100 {
+			max := i + 100
+			if max > len(v) {
+				max = len(v)
+			}
+
+			rows, err := jobs.DeleteJobResult(v[i:max])
+			if err != nil {
+				logrus.Errorf("Error deleting results for jobs: %v, %d rows affected", rows, err)
+				continue
+			}
+			logrus.Infof("Deleted results from %d jobs out of %d job ids", rows, len(v))
+		}
+	}
+
+	return nil
+}
diff --git a/cmd/osbuild-service-maintenance/main.go b/cmd/osbuild-service-maintenance/main.go
index 561478565..c16fd08e2 100644
--- a/cmd/osbuild-service-maintenance/main.go
+++ b/cmd/osbuild-service-maintenance/main.go
@@ -7,15 +7,11 @@ import (
 	"time"
 
 	"github.com/sirupsen/logrus"
-
-	"github.com/osbuild/osbuild-composer/internal/jobqueue/dbjobqueue"
 )
 
 func main() {
 	logrus.SetReportCaller(true)
 
-	archs := []string{"x86_64"}
-	jobType := "osbuild"
 	// 14 days
 	cutoff := time.Now().Add(-(time.Hour * 24 * 14))
 	logrus.Infof("Cutoff date: %v", cutoff)
@@ -90,7 +86,6 @@ func main() {
 		logrus.Info("🦀🦀🦀 DB maintenance not enabled, skipping  🦀🦀🦀")
 		return
 	}
-
 	dbURL := fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=%s",
 		conf.PGUser,
 		conf.PGPassword,
@@ -99,35 +94,9 @@ func main() {
 		conf.PGDatabase,
 		conf.PGSSLMode,
 	)
-	jobs, err := dbjobqueue.New(dbURL)
-	if err != nil {
-		panic(err)
-	}
-
-	var jobTypes []string
-	for _, a := range archs {
-		jobTypes = append(jobTypes, fmt.Sprintf("%s:%s", jobType, a))
-	}
-
-	jobsByType, err := jobs.JobsUptoByType(jobTypes, cutoff)
+	err = DBCleanup(dbURL, conf.DryRun, cutoff)
 	if err != nil {
-		logrus.Errorf("Error querying jobs: %v", err)
-		return
-	}
-
-	for k, v := range jobsByType {
-		logrus.Infof("Deleting jobs and their dependencies of type %v", k)
-		if conf.DryRun {
-			logrus.Infof("Dry run, skipping deletion of jobs: %v", v)
-			continue
-		}
-
-		for _, jobId := range v {
-			err = jobs.DeleteJobIncludingDependencies(jobId)
-			if err != nil {
-				logrus.Errorf("Error deleting job: %v", jobId)
-			}
-		}
+		logrus.Fatalf("Error during DBCleanup: %v", err)
 	}
 	logrus.Info("🦀🦀🦀 dbqueue cleanup done 🦀🦀🦀")
 }
diff --git a/cmd/osbuild-worker/jobimpl-osbuild.go b/cmd/osbuild-worker/jobimpl-osbuild.go
index 7b797e11f..71e72755b 100644
--- a/cmd/osbuild-worker/jobimpl-osbuild.go
+++ b/cmd/osbuild-worker/jobimpl-osbuild.go
@@ -29,15 +29,24 @@ import (
 	"github.com/osbuild/osbuild-composer/internal/worker/clienterrors"
 )
 
+type S3Configuration struct {
+	Creds               string
+	Endpoint            string
+	Region              string
+	Bucket              string
+	CABundle            string
+	SkipSSLVerification bool
+}
+
 type OSBuildJobImpl struct {
-	Store          string
-	Output         string
-	KojiServers    map[string]koji.GSSAPICredentials
-	GCPCreds       string
-	AzureCreds     *azure.Credentials
-	AWSCreds       string
-	AWSBucket      string
-	GenericS3Creds string
+	Store       string
+	Output      string
+	KojiServers map[string]koji.GSSAPICredentials
+	GCPCreds    string
+	AzureCreds  *azure.Credentials
+	AWSCreds    string
+	AWSBucket   string
+	S3Config    S3Configuration
 }
 
 // Returns an *awscloud.AWS object with the credentials of the request. If they
@@ -53,16 +62,68 @@ func (impl *OSBuildJobImpl) getAWS(region string, accessId string, secret string
 	}
 }
 
-func (impl *OSBuildJobImpl) getAWSForEndpoint(options *target.GenericS3TargetOptions) (*awscloud.AWS, error) {
+func (impl *OSBuildJobImpl) getAWSForS3TargetFromOptions(options *target.AWSS3TargetOptions) (*awscloud.AWS, error) {
 	if options.AccessKeyID != "" && options.SecretAccessKey != "" {
 		return awscloud.NewForEndpoint(options.Endpoint, options.Region, options.AccessKeyID, options.SecretAccessKey, options.SessionToken, options.CABundle, options.SkipSSLVerification)
 	}
-	if impl.GenericS3Creds != "" {
-		return awscloud.NewForEndpointFromFile(impl.GenericS3Creds, options.Endpoint, options.Region, options.CABundle, options.SkipSSLVerification)
+	if impl.S3Config.Creds != "" {
+		return awscloud.NewForEndpointFromFile(impl.S3Config.Creds, options.Endpoint, options.Region, options.CABundle, options.SkipSSLVerification)
 	}
 	return nil, fmt.Errorf("no credentials found")
 }
 
+func (impl *OSBuildJobImpl) getAWSForS3TargetFromConfig() (*awscloud.AWS, string, error) {
+	err := impl.verifyS3TargetConfiguration()
+	if err != nil {
+		return nil, "", err
+	}
+	aws, err := awscloud.NewForEndpointFromFile(impl.S3Config.Creds, impl.S3Config.Endpoint, impl.S3Config.Region, impl.S3Config.CABundle, impl.S3Config.SkipSSLVerification)
+	return aws, impl.S3Config.Bucket, err
+}
+
+func (impl *OSBuildJobImpl) verifyS3TargetConfiguration() error {
+	if impl.S3Config.Endpoint == "" {
+		return fmt.Errorf("no default endpoint for S3 was set")
+	}
+
+	if impl.S3Config.Region == "" {
+		return fmt.Errorf("no default region for S3 was set")
+	}
+
+	if impl.S3Config.Bucket == "" {
+		return fmt.Errorf("no default bucket for S3 was set")
+	}
+
+	if impl.S3Config.Creds == "" {
+		return fmt.Errorf("no default credentials for S3 was set")
+	}
+
+	return nil
+}
+
+func (impl *OSBuildJobImpl) getAWSForS3Target(options *target.AWSS3TargetOptions) (*awscloud.AWS, string, error) {
+	var aws *awscloud.AWS = nil
+	var err error
+
+	bucket := options.Bucket
+
+	// Endpoint == "" && Region != "" => AWS (Weldr and Composer)
+	if options.Endpoint == "" && options.Region != "" {
+		aws, err = impl.getAWS(options.Region, options.AccessKeyID, options.SecretAccessKey, options.SessionToken)
+		if impl.AWSBucket != "" {
+			bucket = impl.AWSBucket
+		}
+	} else if options.Endpoint != "" && options.Region != "" { // Endpoint != "" && Region != "" => Generic S3 Weldr API
+		aws, err = impl.getAWSForS3TargetFromOptions(options)
+	} else if options.Endpoint == "" && options.Region == "" { // Endpoint == "" && Region == "" => Generic S3 Composer API
+		aws, bucket, err = impl.getAWSForS3TargetFromConfig()
+	} else {
+		err = fmt.Errorf("s3 server configuration is incomplete")
+	}
+
+	return aws, bucket, err
+}
+
 // getGCP returns an *gcp.GCP object using credentials based on the following
 // predefined preference:
 //
@@ -113,7 +174,7 @@ func validateResult(result *worker.OSBuildJobResult, jobID string) {
 	result.Success = true
 }
 
-func uploadToS3(a *awscloud.AWS, outputDirectory, exportPath, bucket, key, filename string, osbuildJobResult *worker.OSBuildJobResult, genericS3 bool, streamOptimized bool, streamOptimizedPath string) (err error) {
+func uploadToS3(a *awscloud.AWS, outputDirectory, exportPath, bucket, key, filename string, osbuildJobResult *worker.OSBuildJobResult, streamOptimized bool, streamOptimizedPath string) (err error) {
 	imagePath := path.Join(outputDirectory, exportPath, filename)
 
 	// TODO: delete the stream-optimized handling after "some" time (kept for backward compatibility)
@@ -159,13 +220,7 @@ func uploadToS3(a *awscloud.AWS, outputDirectory, exportPath, bucket, key, filen
 		return
 	}
 
-	var targetResult *target.TargetResult
-	if genericS3 {
-		targetResult = target.NewGenericS3TargetResult(&target.GenericS3TargetResultOptions{URL: url})
-	} else {
-		targetResult = target.NewAWSS3TargetResult(&target.AWSS3TargetResultOptions{URL: url})
-	}
-	osbuildJobResult.TargetResults = append(osbuildJobResult.TargetResults, targetResult)
+	osbuildJobResult.TargetResults = append(osbuildJobResult.TargetResults, target.NewAWSS3TargetResult(&target.AWSS3TargetResultOptions{URL: url}))
 
 	osbuildJobResult.Success = true
 	osbuildJobResult.UploadStatus = "success"
@@ -421,29 +476,13 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 			osbuildJobResult.Success = true
 			osbuildJobResult.UploadStatus = "success"
 		case *target.AWSS3TargetOptions:
-			a, err := impl.getAWS(options.Region, options.AccessKeyID, options.SecretAccessKey, options.SessionToken)
-			if err != nil {
-				osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error())
-				return nil
-			}
-
-			bucket := options.Bucket
-			if impl.AWSBucket != "" {
-				bucket = impl.AWSBucket
-			}
-
-			err = uploadToS3(a, outputDirectory, exportPath, bucket, options.Key, options.Filename, osbuildJobResult, false, args.StreamOptimized, streamOptimizedPath)
-			if err != nil {
-				return nil
-			}
-		case *target.GenericS3TargetOptions:
-			a, err := impl.getAWSForEndpoint(options)
+			a, bucket, err := impl.getAWSForS3Target(options)
 			if err != nil {
 				osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error())
 				return nil
 			}
 
-			err = uploadToS3(a, outputDirectory, exportPath, options.Bucket, options.Key, options.Filename, osbuildJobResult, true, args.StreamOptimized, streamOptimizedPath)
+			err = uploadToS3(a, outputDirectory, exportPath, bucket, options.Key, options.Filename, osbuildJobResult, args.StreamOptimized, streamOptimizedPath)
 			if err != nil {
 				return nil
 			}
diff --git a/cmd/osbuild-worker/main.go b/cmd/osbuild-worker/main.go
index be2d528d4..a3e029f90 100644
--- a/cmd/osbuild-worker/main.go
+++ b/cmd/osbuild-worker/main.go
@@ -214,7 +214,12 @@ func main() {
 			Bucket      string `toml:"bucket"`
 		} `toml:"aws"`
 		GenericS3 *struct {
-			Credentials string `toml:"credentials"`
+			Credentials         string `toml:"credentials"`
+			Endpoint            string `toml:"endpoint"`
+			Region              string `toml:"region"`
+			Bucket              string `toml:"bucket"`
+			CABundle            string `toml:"ca_bundle"`
+			SkipSSLVerification bool   `toml:"skip_ssl_verification"`
 		} `toml:"generic_s3"`
 		Authentication *struct {
 			OAuthURL         string `toml:"oauth_url"`
@@ -392,8 +397,18 @@ func main() {
 	}
 
 	var genericS3Credentials = ""
+	var genericS3Endpoint = ""
+	var genericS3Region = ""
+	var genericS3Bucket = ""
+	var genericS3CABundle = ""
+	var genericS3SkipSSLVerification = false
 	if config.GenericS3 != nil {
 		genericS3Credentials = config.GenericS3.Credentials
+		genericS3Endpoint = config.GenericS3.Endpoint
+		genericS3Region = config.GenericS3.Region
+		genericS3Bucket = config.GenericS3.Bucket
+		genericS3CABundle = config.GenericS3.CABundle
+		genericS3SkipSSLVerification = config.GenericS3.SkipSSLVerification
 	}
 
 	// depsolve jobs can be done during other jobs
@@ -434,14 +449,21 @@ func main() {
 	// non-depsolve job
 	jobImpls := map[string]JobImplementation{
 		"osbuild": &OSBuildJobImpl{
-			Store:          store,
-			Output:         output,
-			KojiServers:    kojiServers,
-			GCPCreds:       gcpCredentials,
-			AzureCreds:     azureCredentials,
-			AWSCreds:       awsCredentials,
-			AWSBucket:      awsBucket,
-			GenericS3Creds: genericS3Credentials,
+			Store:       store,
+			Output:      output,
+			KojiServers: kojiServers,
+			GCPCreds:    gcpCredentials,
+			AzureCreds:  azureCredentials,
+			AWSCreds:    awsCredentials,
+			AWSBucket:   awsBucket,
+			S3Config: S3Configuration{
+				Creds:               genericS3Credentials,
+				Endpoint:            genericS3Endpoint,
+				Region:              genericS3Region,
+				Bucket:              genericS3Bucket,
+				CABundle:            genericS3CABundle,
+				SkipSSLVerification: genericS3SkipSSLVerification,
+			},
 		},
 		"osbuild-koji": &OSBuildKojiJobImpl{
 			Store:              store,
diff --git a/internal/distro/rhel90/distro.go b/internal/distro/rhel90/distro.go
index 46421f5f0..ddc435eaa 100644
--- a/internal/distro/rhel90/distro.go
+++ b/internal/distro/rhel90/distro.go
@@ -950,18 +950,6 @@ func newDistro(distroName string) distro.Distro {
 						osbuild.NewModprobeConfigCmdBlacklist("floppy"),
 					},
 				},
-				{
-					Filename: "blacklist-skylake-edac.conf",
-					Commands: osbuild.ModprobeConfigCmdList{
-						osbuild.NewModprobeConfigCmdBlacklist("skx_edac"),
-					},
-				},
-				{
-					Filename: "blacklist-intel-cstate.conf",
-					Commands: osbuild.ModprobeConfigCmdList{
-						osbuild.NewModprobeConfigCmdBlacklist("intel_cstate"),
-					},
-				},
 			},
 			CloudInit: []*osbuild.CloudInitStageOptions{
 				{
diff --git a/internal/jobqueue/dbjobqueue/dbjobqueue.go b/internal/jobqueue/dbjobqueue/dbjobqueue.go
index 6679032bd..3b0819e29 100644
--- a/internal/jobqueue/dbjobqueue/dbjobqueue.go
+++ b/internal/jobqueue/dbjobqueue/dbjobqueue.go
@@ -67,7 +67,7 @@ const (
 		FROM jobs
 		WHERE id = $1`
 	sqlQueryJobStatus = `
-		SELECT type, result, queued_at, started_at, finished_at, canceled
+		SELECT type, channel, result, queued_at, started_at, finished_at, canceled
 		FROM jobs
 		WHERE id = $1`
 	sqlQueryRunningId = `
@@ -106,21 +106,9 @@ const (
                 FROM jobs
                 WHERE type = ANY($1) AND finished_at < $2
                 GROUP BY type`
-	sqlQueryDepedenciesRecursively = `
-                WITH RECURSIVE dependencies(d) AS (
-                                SELECT dependency_id
-                                FROM job_dependencies
-                                WHERE job_id = $1
-                                UNION ALL
-                                SELECT dependency_id
-                                FROM dependencies, job_dependencies
-                                WHERE job_dependencies.job_id = d  )
-                SELECT * FROM dependencies`
-	sqlDeleteJobDependencies = `
-                DELETE FROM job_dependencies
-                WHERE dependency_id = ANY($1)`
-	sqlDeleteJobs = `
-                DELETE FROM jobs
+	sqlDeleteJobResult = `
+                UPDATE jobs
+                SET result = NULL
                 WHERE id = ANY($1)`
 )
 
@@ -485,7 +473,7 @@ func (q *DBJobQueue) CancelJob(id uuid.UUID) error {
 	return nil
 }
 
-func (q *DBJobQueue) JobStatus(id uuid.UUID) (jobType string, result json.RawMessage, queued, started, finished time.Time, canceled bool, deps []uuid.UUID, err error) {
+func (q *DBJobQueue) JobStatus(id uuid.UUID) (jobType string, channel string, result json.RawMessage, queued, started, finished time.Time, canceled bool, deps []uuid.UUID, err error) {
 	conn, err := q.pool.Acquire(context.Background())
 	if err != nil {
 		return
@@ -495,7 +483,7 @@ func (q *DBJobQueue) JobStatus(id uuid.UUID) (jobType string, result json.RawMes
 	// Use double pointers for timestamps because they might be NULL, which would result in *time.Time == nil
 	var sp, fp *time.Time
 	var rp pgtype.JSON
-	err = conn.QueryRow(context.Background(), sqlQueryJobStatus, id).Scan(&jobType, &rp, &queued, &sp, &fp, &canceled)
+	err = conn.QueryRow(context.Background(), sqlQueryJobStatus, id).Scan(&jobType, &channel, &rp, &queued, &sp, &fp, &canceled)
 	if err != nil {
 		return
 	}
@@ -657,59 +645,16 @@ func (q *DBJobQueue) JobsUptoByType(jobTypes []string, upto time.Time) (result m
 	return
 }
 
-// Deletes single job and dependencies (recursively)
-func (q *DBJobQueue) DeleteJobIncludingDependencies(jobId uuid.UUID) error {
+func (q *DBJobQueue) DeleteJobResult(jobIds []uuid.UUID) (int64, error) {
 	conn, err := q.pool.Acquire(context.Background())
 	if err != nil {
-
-		return fmt.Errorf("error connecting to database: %v", err)
+		return -1, fmt.Errorf("error connecting to database: %v", err)
 	}
 	defer conn.Release()
 
-	tx, err := conn.Begin(context.Background())
-	if err != nil {
-		return fmt.Errorf("error starting database transaction: %v", err)
-	}
-	defer func() {
-		err := tx.Rollback(context.Background())
-		if err != nil && !errors.As(err, &pgx.ErrTxClosed) {
-			logrus.Error("error rolling back enqueue transaction: ", err)
-		}
-	}()
-
-	rows, err := tx.Query(context.Background(), sqlQueryDepedenciesRecursively, jobId)
-	if err != nil {
-		return fmt.Errorf("error querying the job's dependencies: %v", err)
-	}
-
-	var dependencies []uuid.UUID
-	for rows.Next() {
-		var dep uuid.UUID
-		err = rows.Scan(&dep)
-		if err != nil {
-			return err
-		}
-
-		dependencies = append(dependencies, dep)
-	}
-
-	depTag, err := tx.Exec(context.Background(), sqlDeleteJobDependencies, dependencies)
-	if err != nil {
-		return fmt.Errorf("Error removing from dependencies recursively for job %v: %v", jobId, err)
-	}
-
-	jobAndDependencies := append(dependencies, jobId)
-	jobsTag, err := tx.Exec(context.Background(), sqlDeleteJobs, jobAndDependencies)
-	if err != nil {
-		return fmt.Errorf("Error removing from jobs recursively for job %v: %v", jobId, err)
-	}
-
-	err = tx.Commit(context.Background())
+	tag, err := conn.Exec(context.Background(), sqlDeleteJobResult, jobIds)
 	if err != nil {
-		return fmt.Errorf("unable to commit database transaction: %v", err)
+		return tag.RowsAffected(), fmt.Errorf("Error deleting results from jobs: %v", err)
 	}
-
-	logrus.Infof("Removed %d rows from dependencies for job %v", depTag.RowsAffected(), jobId)
-	logrus.Infof("Removed %d rows from jobs for job %v, this includes dependencies", jobsTag.RowsAffected(), jobId)
-	return nil
+	return tag.RowsAffected(), nil
 }
diff --git a/internal/jobqueue/dbjobqueue/schemas/003_dependencies_cascade.sql b/internal/jobqueue/dbjobqueue/schemas/003_dependencies_cascade.sql
new file mode 100644
index 000000000..61a3e8f31
--- /dev/null
+++ b/internal/jobqueue/dbjobqueue/schemas/003_dependencies_cascade.sql
@@ -0,0 +1,11 @@
+ALTER TABLE job_dependencies
+
+DROP CONSTRAINT job_dependencies_dependency_id_fkey,
+
+DROP CONSTRAINT job_dependencies_job_id_fkey,
+
+ADD CONSTRAINT job_dependencies_dependency_id_fkey
+FOREIGN KEY (job_id) REFERENCES jobs(id) ON DELETE CASCADE,
+
+ADD CONSTRAINT job_dependencies_job_id_fkey
+FOREIGN KEY (dependency_id) REFERENCES jobs(id) ON DELETE CASCADE;
diff --git a/internal/jobqueue/dbjobqueue/schemas/004_heartbeats_cascade.sql b/internal/jobqueue/dbjobqueue/schemas/004_heartbeats_cascade.sql
new file mode 100644
index 000000000..693da709c
--- /dev/null
+++ b/internal/jobqueue/dbjobqueue/schemas/004_heartbeats_cascade.sql
@@ -0,0 +1,6 @@
+ALTER TABLE heartbeats
+
+DROP CONSTRAINT heartbeats_id_fkey,
+
+ADD CONSTRAINT heartbeats_id_fkey
+FOREIGN KEY (id) REFERENCES jobs(id) ON DELETE CASCADE;
diff --git a/internal/jobqueue/fsjobqueue/fsjobqueue.go b/internal/jobqueue/fsjobqueue/fsjobqueue.go
index e3c8c5797..3a38d7483 100644
--- a/internal/jobqueue/fsjobqueue/fsjobqueue.go
+++ b/internal/jobqueue/fsjobqueue/fsjobqueue.go
@@ -343,13 +343,14 @@ func (q *fsJobQueue) CancelJob(id uuid.UUID) error {
 	return nil
 }
 
-func (q *fsJobQueue) JobStatus(id uuid.UUID) (jobType string, result json.RawMessage, queued, started, finished time.Time, canceled bool, deps []uuid.UUID, err error) {
+func (q *fsJobQueue) JobStatus(id uuid.UUID) (jobType string, channel string, result json.RawMessage, queued, started, finished time.Time, canceled bool, deps []uuid.UUID, err error) {
 	j, err := q.readJob(id)
 	if err != nil {
 		return
 	}
 
 	jobType = j.Type
+	channel = j.Channel
 	result = j.Result
 	queued = j.QueuedAt
 	started = j.StartedAt
diff --git a/internal/jobqueue/jobqueue.go b/internal/jobqueue/jobqueue.go
index 28ae645d2..d23133728 100644
--- a/internal/jobqueue/jobqueue.go
+++ b/internal/jobqueue/jobqueue.go
@@ -64,7 +64,7 @@ type JobQueue interface {
 	// finished, respectively.
 	//
 	// Lastly, the IDs of the jobs dependencies are returned.
-	JobStatus(id uuid.UUID) (jobType string, result json.RawMessage, queued, started, finished time.Time, canceled bool, deps []uuid.UUID, err error)
+	JobStatus(id uuid.UUID) (jobType string, channel string, result json.RawMessage, queued, started, finished time.Time, canceled bool, deps []uuid.UUID, err error)
 
 	// Job returns all the parameters that define a job (everything provided during Enqueue).
 	Job(id uuid.UUID) (jobType string, args json.RawMessage, dependencies []uuid.UUID, channel string, err error)
diff --git a/internal/prometheus/job_metrics.go b/internal/prometheus/job_metrics.go
index b0f63c3fa..0b720830f 100644
--- a/internal/prometheus/job_metrics.go
+++ b/internal/prometheus/job_metrics.go
@@ -16,7 +16,7 @@ var (
 		Namespace: namespace,
 		Subsystem: workerSubsystem,
 		Help:      "Total jobs",
-	}, []string{"type", "status"})
+	}, []string{"type", "status", "tenant"})
 )
 
 var (
@@ -25,7 +25,7 @@ var (
 		Namespace: namespace,
 		Subsystem: workerSubsystem,
 		Help:      "Currently pending jobs",
-	}, []string{"type"})
+	}, []string{"type", "tenant"})
 )
 
 var (
@@ -34,7 +34,7 @@ var (
 		Namespace: namespace,
 		Subsystem: workerSubsystem,
 		Help:      "Currently running jobs",
-	}, []string{"type"})
+	}, []string{"type", "tenant"})
 )
 
 var (
@@ -44,7 +44,7 @@ var (
 		Subsystem: workerSubsystem,
 		Help:      "Duration spent by workers on a job.",
 		Buckets:   []float64{.1, .2, .5, 1, 2, 4, 8, 16, 32, 40, 48, 64, 96, 128, 160, 192, 224, 256, 320, 382, 448, 512, 640, 768, 896, 1024, 1280, 1536, 1792, 2049},
-	}, []string{"type", "status"})
+	}, []string{"type", "status", "tenant"})
 )
 
 var (
@@ -54,35 +54,35 @@ var (
 		Subsystem: workerSubsystem,
 		Help:      "Duration a job spends on the queue.",
 		Buckets:   []float64{.1, .2, .5, 1, 2, 4, 8, 16, 32, 40, 48, 64, 96, 128, 160, 192, 224, 256, 320, 382, 448, 512, 640, 768, 896, 1024, 1280, 1536, 1792, 2049},
-	}, []string{"type"})
+	}, []string{"type", "tenant"})
 )
 
-func EnqueueJobMetrics(jobType string) {
-	PendingJobs.WithLabelValues(jobType).Inc()
+func EnqueueJobMetrics(jobType, tenant string) {
+	PendingJobs.WithLabelValues(jobType, tenant).Inc()
 }
 
-func DequeueJobMetrics(pending time.Time, started time.Time, jobType string) {
+func DequeueJobMetrics(pending time.Time, started time.Time, jobType, tenant string) {
 	if !started.IsZero() && !pending.IsZero() {
 		diff := started.Sub(pending).Seconds()
-		JobWaitDuration.WithLabelValues(jobType).Observe(diff)
-		PendingJobs.WithLabelValues(jobType).Dec()
-		RunningJobs.WithLabelValues(jobType).Inc()
+		JobWaitDuration.WithLabelValues(jobType, tenant).Observe(diff)
+		PendingJobs.WithLabelValues(jobType, tenant).Dec()
+		RunningJobs.WithLabelValues(jobType, tenant).Inc()
 	}
 }
 
-func CancelJobMetrics(started time.Time, jobType string) {
+func CancelJobMetrics(started time.Time, jobType string, tenant string) {
 	if !started.IsZero() {
-		RunningJobs.WithLabelValues(jobType).Dec()
+		RunningJobs.WithLabelValues(jobType, tenant).Dec()
 	} else {
-		PendingJobs.WithLabelValues(jobType).Dec()
+		PendingJobs.WithLabelValues(jobType, tenant).Dec()
 	}
 }
 
-func FinishJobMetrics(started time.Time, finished time.Time, canceled bool, jobType string, status clienterrors.StatusCode) {
+func FinishJobMetrics(started time.Time, finished time.Time, canceled bool, jobType, tenant string, status clienterrors.StatusCode) {
 	if !finished.IsZero() && !canceled {
 		diff := finished.Sub(started).Seconds()
-		JobDuration.WithLabelValues(jobType, status.ToString()).Observe(diff)
-		TotalJobs.WithLabelValues(jobType, status.ToString()).Inc()
-		RunningJobs.WithLabelValues(jobType).Dec()
+		JobDuration.WithLabelValues(jobType, status.ToString(), tenant).Observe(diff)
+		TotalJobs.WithLabelValues(jobType, status.ToString(), tenant).Inc()
+		RunningJobs.WithLabelValues(jobType, tenant).Dec()
 	}
 }
diff --git a/internal/target/aws_target.go b/internal/target/aws_target.go
index 72973882e..756e0f073 100644
--- a/internal/target/aws_target.go
+++ b/internal/target/aws_target.go
@@ -29,13 +29,16 @@ func NewAWSTargetResult(options *AWSTargetResultOptions) *TargetResult {
 }
 
 type AWSS3TargetOptions struct {
-	Filename        string `json:"filename"`
-	Region          string `json:"region"`
-	AccessKeyID     string `json:"accessKeyID"`
-	SecretAccessKey string `json:"secretAccessKey"`
-	SessionToken    string `json:"sessionToken"`
-	Bucket          string `json:"bucket"`
-	Key             string `json:"key"`
+	Filename            string `json:"filename"`
+	Region              string `json:"region"`
+	AccessKeyID         string `json:"accessKeyID"`
+	SecretAccessKey     string `json:"secretAccessKey"`
+	SessionToken        string `json:"sessionToken"`
+	Bucket              string `json:"bucket"`
+	Key                 string `json:"key"`
+	Endpoint            string `json:"endpoint"`
+	CABundle            string `json:"ca_bundle"`
+	SkipSSLVerification bool   `json:"skip_ssl_verification"`
 }
 
 func (AWSS3TargetOptions) isTargetOptions() {}
diff --git a/internal/target/generic_s3_target.go b/internal/target/generic_s3_target.go
deleted file mode 100644
index f95be6293..000000000
--- a/internal/target/generic_s3_target.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package target
-
-type GenericS3TargetOptions struct {
-	AWSS3TargetOptions
-	Endpoint            string `json:"endpoint"`
-	CABundle            string `json:"ca_bundle"`
-	SkipSSLVerification bool   `json:"skip_ssl_verification"`
-}
-
-func (GenericS3TargetOptions) isTargetOptions() {}
-
-func NewGenericS3Target(options *GenericS3TargetOptions) *Target {
-	return newTarget("org.osbuild.generic.s3", options)
-}
-
-type GenericS3TargetResultOptions AWSS3TargetResultOptions
-
-func (GenericS3TargetResultOptions) isTargetResultOptions() {}
-
-func NewGenericS3TargetResult(options *GenericS3TargetResultOptions) *TargetResult {
-	return newTargetResult("org.osbuild.generic.s3", options)
-}
diff --git a/internal/target/target.go b/internal/target/target.go
index ace383c8a..fccc4c696 100644
--- a/internal/target/target.go
+++ b/internal/target/target.go
@@ -83,8 +83,6 @@ func UnmarshalTargetOptions(targetName string, rawOptions json.RawMessage) (Targ
 		options = new(VMWareTargetOptions)
 	case "org.osbuild.oci":
 		options = new(OCITargetOptions)
-	case "org.osbuild.generic.s3":
-		options = new(GenericS3TargetOptions)
 	default:
 		return nil, errors.New("unexpected target name")
 	}
diff --git a/internal/target/targetresult.go b/internal/target/targetresult.go
index e3b29277c..f1f143968 100644
--- a/internal/target/targetresult.go
+++ b/internal/target/targetresult.go
@@ -55,8 +55,6 @@ func UnmarshalTargetResultOptions(trName string, rawOptions json.RawMessage) (Ta
 		options = new(AzureImageTargetResultOptions)
 	case "org.osbuild.oci":
 		options = new(OCITargetResultOptions)
-	case "org.osbuild.generic.s3":
-		options = new(GenericS3TargetResultOptions)
 	default:
 		return nil, fmt.Errorf("unexpected target result name: %s", trName)
 	}
diff --git a/internal/weldr/upload.go b/internal/weldr/upload.go
index d5e194554..9faeb54ee 100644
--- a/internal/weldr/upload.go
+++ b/internal/weldr/upload.go
@@ -40,12 +40,15 @@ type awsUploadSettings struct {
 func (awsUploadSettings) isUploadSettings() {}
 
 type awsS3UploadSettings struct {
-	Region          string `json:"region"`
-	AccessKeyID     string `json:"accessKeyID,omitempty"`
-	SecretAccessKey string `json:"secretAccessKey,omitempty"`
-	SessionToken    string `json:"sessionToken,omitempty"`
-	Bucket          string `json:"bucket"`
-	Key             string `json:"key"`
+	Region              string `json:"region"`
+	AccessKeyID         string `json:"accessKeyID,omitempty"`
+	SecretAccessKey     string `json:"secretAccessKey,omitempty"`
+	SessionToken        string `json:"sessionToken,omitempty"`
+	Bucket              string `json:"bucket"`
+	Key                 string `json:"key"`
+	Endpoint            string `json:"endpoint"`
+	CABundle            string `json:"ca_bundle"`
+	SkipSSLVerification bool   `json:"skip_ssl_verification"`
 }
 
 func (awsS3UploadSettings) isUploadSettings() {}
@@ -94,15 +97,6 @@ type ociUploadSettings struct {
 
 func (ociUploadSettings) isUploadSettings() {}
 
-type genericS3UploadSettings struct {
-	awsS3UploadSettings
-	Endpoint            string `json:"endpoint"`
-	CABundle            string `json:"ca_bundle"`
-	SkipSSLVerification bool   `json:"skip_ssl_verification"`
-}
-
-func (genericS3UploadSettings) isUploadSettings() {}
-
 type uploadRequest struct {
 	Provider  string         `json:"provider"`
 	ImageName string         `json:"image_name"`
@@ -137,7 +131,9 @@ func (u *uploadRequest) UnmarshalJSON(data []byte) error {
 	case "oci":
 		settings = new(ociUploadSettings)
 	case "generic.s3":
-		settings = new(genericS3UploadSettings)
+		// While the API still accepts provider type "generic.s3", the request is handled
+		// in the same way as for a request with provider type "aws.s3"
+		settings = new(awsS3UploadSettings)
 	default:
 		return errors.New("unexpected provider name")
 	}
@@ -227,20 +223,6 @@ func targetsToUploadResponses(targets []*target.Target, state ComposeState) []up
 				// AccessKeyID and SecretAccessKey are intentionally not included.
 			}
 			uploads = append(uploads, upload)
-		case *target.GenericS3TargetOptions:
-			upload.ProviderName = "generic.s3"
-			upload.Settings = &genericS3UploadSettings{
-				awsS3UploadSettings: awsS3UploadSettings{
-					Region: options.Region,
-					Bucket: options.Bucket,
-					Key:    options.Key,
-					// AccessKeyID and SecretAccessKey are intentionally not included.
-				},
-				Endpoint:            options.Endpoint,
-				CABundle:            options.CABundle,
-				SkipSSLVerification: options.SkipSSLVerification,
-			}
-			uploads = append(uploads, upload)
 		}
 	}
 
@@ -270,13 +252,16 @@ func uploadRequestToTarget(u uploadRequest, imageType distro.ImageType) *target.
 	case *awsS3UploadSettings:
 		t.Name = "org.osbuild.aws.s3"
 		t.Options = &target.AWSS3TargetOptions{
-			Filename:        imageType.Filename(),
-			Region:          options.Region,
-			AccessKeyID:     options.AccessKeyID,
-			SecretAccessKey: options.SecretAccessKey,
-			SessionToken:    options.SessionToken,
-			Bucket:          options.Bucket,
-			Key:             options.Key,
+			Filename:            imageType.Filename(),
+			Region:              options.Region,
+			AccessKeyID:         options.AccessKeyID,
+			SecretAccessKey:     options.SecretAccessKey,
+			SessionToken:        options.SessionToken,
+			Bucket:              options.Bucket,
+			Key:                 options.Key,
+			Endpoint:            options.Endpoint,
+			CABundle:            options.CABundle,
+			SkipSSLVerification: options.SkipSSLVerification,
 		}
 	case *azureUploadSettings:
 		t.Name = "org.osbuild.azure"
@@ -337,22 +322,6 @@ func uploadRequestToTarget(u uploadRequest, imageType distro.ImageType) *target.
 			Namespace:   options.Namespace,
 			Compartment: options.Compartment,
 		}
-	case *genericS3UploadSettings:
-		t.Name = "org.osbuild.generic.s3"
-		t.Options = &target.GenericS3TargetOptions{
-			AWSS3TargetOptions: target.AWSS3TargetOptions{
-				Filename:        imageType.Filename(),
-				Region:          options.Region,
-				AccessKeyID:     options.AccessKeyID,
-				SecretAccessKey: options.SecretAccessKey,
-				SessionToken:    options.SessionToken,
-				Bucket:          options.Bucket,
-				Key:             options.Key,
-			},
-			Endpoint:            options.Endpoint,
-			CABundle:            options.CABundle,
-			SkipSSLVerification: options.SkipSSLVerification,
-		}
 	}
 
 	return &t
diff --git a/internal/worker/server.go b/internal/worker/server.go
index 1416fb6ef..149ac82d0 100644
--- a/internal/worker/server.go
+++ b/internal/worker/server.go
@@ -133,7 +133,7 @@ func (s *Server) EnqueueManifestJobByID(job *ManifestJobByID, parent uuid.UUID,
 }
 
 func (s *Server) enqueue(jobType string, job interface{}, dependencies []uuid.UUID, channel string) (uuid.UUID, error) {
-	prometheus.EnqueueJobMetrics(jobType)
+	prometheus.EnqueueJobMetrics(jobType, channel)
 	return s.jobs.Enqueue(jobType, job, dependencies, channel)
 }
 
@@ -166,7 +166,7 @@ func (s *Server) CheckBuildDependencies(dep uuid.UUID, jobErr *clienterrors.Erro
 }
 
 func (s *Server) OSBuildJobStatus(id uuid.UUID, result *OSBuildJobResult) (*JobStatus, []uuid.UUID, error) {
-	jobType, status, deps, err := s.jobStatus(id, result)
+	jobType, _, status, deps, err := s.jobStatus(id, result)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -194,7 +194,7 @@ func (s *Server) OSBuildJobStatus(id uuid.UUID, result *OSBuildJobResult) (*JobS
 }
 
 func (s *Server) OSBuildKojiJobStatus(id uuid.UUID, result *OSBuildKojiJobResult) (*JobStatus, []uuid.UUID, error) {
-	jobType, status, deps, err := s.jobStatus(id, result)
+	jobType, _, status, deps, err := s.jobStatus(id, result)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -217,7 +217,7 @@ func (s *Server) OSBuildKojiJobStatus(id uuid.UUID, result *OSBuildKojiJobResult
 }
 
 func (s *Server) KojiInitJobStatus(id uuid.UUID, result *KojiInitJobResult) (*JobStatus, []uuid.UUID, error) {
-	jobType, status, deps, err := s.jobStatus(id, result)
+	jobType, _, status, deps, err := s.jobStatus(id, result)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -234,7 +234,7 @@ func (s *Server) KojiInitJobStatus(id uuid.UUID, result *KojiInitJobResult) (*Jo
 }
 
 func (s *Server) KojiFinalizeJobStatus(id uuid.UUID, result *KojiFinalizeJobResult) (*JobStatus, []uuid.UUID, error) {
-	jobType, status, deps, err := s.jobStatus(id, result)
+	jobType, _, status, deps, err := s.jobStatus(id, result)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -251,7 +251,7 @@ func (s *Server) KojiFinalizeJobStatus(id uuid.UUID, result *KojiFinalizeJobResu
 }
 
 func (s *Server) DepsolveJobStatus(id uuid.UUID, result *DepsolveJobResult) (*JobStatus, []uuid.UUID, error) {
-	jobType, status, deps, err := s.jobStatus(id, result)
+	jobType, _, status, deps, err := s.jobStatus(id, result)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -272,7 +272,7 @@ func (s *Server) DepsolveJobStatus(id uuid.UUID, result *DepsolveJobResult) (*Jo
 }
 
 func (s *Server) ManifestJobStatus(id uuid.UUID, result *ManifestJobByIDResult) (*JobStatus, []uuid.UUID, error) {
-	jobType, status, deps, err := s.jobStatus(id, result)
+	jobType, _, status, deps, err := s.jobStatus(id, result)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -284,20 +284,20 @@ func (s *Server) ManifestJobStatus(id uuid.UUID, result *ManifestJobByIDResult)
 	return status, deps, nil
 }
 
-func (s *Server) jobStatus(id uuid.UUID, result interface{}) (string, *JobStatus, []uuid.UUID, error) {
-	jobType, rawResult, queued, started, finished, canceled, deps, err := s.jobs.JobStatus(id)
+func (s *Server) jobStatus(id uuid.UUID, result interface{}) (string, string, *JobStatus, []uuid.UUID, error) {
+	jobType, channel, rawResult, queued, started, finished, canceled, deps, err := s.jobs.JobStatus(id)
 	if err != nil {
-		return "", nil, nil, err
+		return "", "", nil, nil, err
 	}
 
 	if result != nil && !finished.IsZero() && !canceled {
 		err = json.Unmarshal(rawResult, result)
 		if err != nil {
-			return "", nil, nil, fmt.Errorf("error unmarshaling result for job '%s': %v", id, err)
+			return "", "", nil, nil, fmt.Errorf("error unmarshaling result for job '%s': %v", id, err)
 		}
 	}
 
-	return jobType, &JobStatus{
+	return jobType, channel, &JobStatus{
 		Queued:   queued,
 		Started:  started,
 		Finished: finished,
@@ -350,11 +350,11 @@ func (s *Server) JobType(id uuid.UUID) (string, error) {
 }
 
 func (s *Server) Cancel(id uuid.UUID) error {
-	jobType, status, _, err := s.jobStatus(id, nil)
+	jobType, channel, status, _, err := s.jobStatus(id, nil)
 	if err != nil {
 		logrus.Errorf("error getting job status: %v", err)
 	} else {
-		prometheus.CancelJobMetrics(status.Started, jobType)
+		prometheus.CancelJobMetrics(status.Started, jobType, channel)
 	}
 	return s.jobs.CancelJob(id)
 }
@@ -366,7 +366,7 @@ func (s *Server) JobArtifact(id uuid.UUID, name string) (io.Reader, int64, error
 		return nil, 0, errors.New("Artifacts not enabled")
 	}
 
-	_, status, _, err := s.jobStatus(id, nil)
+	_, _, status, _, err := s.jobStatus(id, nil)
 	if err != nil {
 		return nil, 0, err
 	}
@@ -395,7 +395,7 @@ func (s *Server) DeleteArtifacts(id uuid.UUID) error {
 		return errors.New("Artifacts not enabled")
 	}
 
-	_, status, _, err := s.jobStatus(id, nil)
+	_, _, status, _, err := s.jobStatus(id, nil)
 	if err != nil {
 		return err
 	}
@@ -449,10 +449,11 @@ func (s *Server) requestJob(ctx context.Context, arch string, jobTypes []string,
 		return
 	}
 
-	jobType, status, _, err := s.jobStatus(jobId, nil)
+	jobType, channel, status, _, err := s.jobStatus(jobId, nil)
 	if err != nil {
 		logrus.Errorf("error retrieving job status: %v", err)
-		return
+	} else {
+		prometheus.DequeueJobMetrics(status.Queued, status.Started, jobType, channel)
 	}
 
 	// Record how long the job has been pending for, that is either how
@@ -465,7 +466,7 @@ func (s *Server) requestJob(ctx context.Context, arch string, jobTypes []string,
 		// TODO: include type of arguments
 		var result json.RawMessage
 		var finished time.Time
-		_, result, _, _, finished, _, _, err = s.jobs.JobStatus(depID)
+		_, _, result, _, _, finished, _, _, err = s.jobs.JobStatus(depID)
 		if err != nil {
 			return
 		}
@@ -484,7 +485,7 @@ func (s *Server) requestJob(ctx context.Context, arch string, jobTypes []string,
 
 	// TODO: Drop the ':$architecture' for metrics too, first prometheus queries for alerts and
 	// dashboards need to be adjusted.
-	prometheus.DequeueJobMetrics(pending, status.Started, jobType)
+	prometheus.DequeueJobMetrics(pending, status.Started, jobType, channel)
 	if jobType == "osbuild:"+arch {
 		jobType = "osbuild"
 	} else if jobType == "osbuild-koji:"+arch {
@@ -516,12 +517,12 @@ func (s *Server) FinishJob(token uuid.UUID, result json.RawMessage) error {
 	}
 
 	var jobResult JobResult
-	jobType, status, _, err := s.jobStatus(jobId, &jobResult)
+	jobType, channel, status, _, err := s.jobStatus(jobId, &jobResult)
 	if err != nil {
 		logrus.Errorf("error finding job status: %v", err)
 	} else {
 		statusCode := clienterrors.GetStatusCode(jobResult.JobError)
-		prometheus.FinishJobMetrics(status.Started, status.Finished, status.Canceled, jobType, statusCode)
+		prometheus.FinishJobMetrics(status.Started, status.Finished, status.Canceled, jobType, channel, statusCode)
 	}
 
 	// Move artifacts from the temporary location to the final job
@@ -664,7 +665,7 @@ func (h *apiHandlers) GetJob(ctx echo.Context, tokenstr string) error {
 
 	h.server.jobs.RefreshHeartbeat(token)
 
-	_, status, _, err := h.server.jobStatus(jobId, nil)
+	_, _, status, _, err := h.server.jobStatus(jobId, nil)
 	if err != nil {
 		return api.HTTPErrorWithInternal(api.ErrorRetrievingJobStatus, err)
 	}
diff --git a/osbuild-composer.spec b/osbuild-composer.spec
index 34074711f..111d6b382 100644
--- a/osbuild-composer.spec
+++ b/osbuild-composer.spec
@@ -9,7 +9,7 @@
 
 %global goipath         github.com/osbuild/osbuild-composer
 
-Version:        54
+Version:        55
 
 %gometa
 
diff --git a/schutzbot/terraform b/schutzbot/terraform
index 3fc5444c6..bc8996c56 100644
--- a/schutzbot/terraform
+++ b/schutzbot/terraform
@@ -1,1 +1,1 @@
-62b0a63de7d864c21d9e7d19e0fe54ca120bd680
+98bf823964bec1ddfe35bca003afaa680a8e0d28
diff --git a/templates/composer.yml b/templates/composer.yml
index 76fd92c01..42f386aac 100644
--- a/templates/composer.yml
+++ b/templates/composer.yml
@@ -320,6 +320,33 @@ objects:
                   cpu: "${CPU_LIMIT}"
                   memory: "${MEMORY_LIMIT}"
               env:
+              - name: PGHOST
+                valueFrom:
+                  secretKeyRef:
+                    name: composer-db
+                    key: db.host
+              - name: PGPORT
+                valueFrom:
+                  secretKeyRef:
+                    name: composer-db
+                    key: db.port
+              - name: PGDATABASE
+                valueFrom:
+                  secretKeyRef:
+                    name: composer-db
+                    key: db.name
+              - name: PGUSER
+                valueFrom:
+                  secretKeyRef:
+                    name: composer-db
+                    key: db.user
+              - name: PGPASSWORD
+                valueFrom:
+                  secretKeyRef:
+                    name: composer-db
+                    key: db.password
+              - name: PGSSLMODE
+                value: "${PGSSLMODE}"
               - name: GCP_AUTH_PROVIDER_X509_CERT_URL
                 valueFrom:
                   secretKeyRef:
diff --git a/templates/packer/ansible/roles/common/tasks/packages.yml b/templates/packer/ansible/roles/common/tasks/packages.yml
index 93c689ec6..3b33eb645 100644
--- a/templates/packer/ansible/roles/common/tasks/packages.yml
+++ b/templates/packer/ansible/roles/common/tasks/packages.yml
@@ -51,7 +51,7 @@
       - jq
       - unzip
       - monit
-      - vector
+      - vector-0.21.2 # vector-0.22 has broken the journald source, see https://github.com/vectordotdev/vector/issues/12966
   register: result
   retries: 5
   until: result is success


Test patch:
diff --git a/cmd/osbuild-composer-dbjobqueue-tests/main_test.go b/cmd/osbuild-composer-dbjobqueue-tests/main_test.go
index be3f20ed2..5e3623575 100644
--- a/cmd/osbuild-composer-dbjobqueue-tests/main_test.go
+++ b/cmd/osbuild-composer-dbjobqueue-tests/main_test.go
@@ -4,6 +4,7 @@ package main
 
 import (
 	"context"
+	"encoding/json"
 	"fmt"
 	"testing"
 	"time"
@@ -62,7 +63,7 @@ func TestJobQueueInterface(t *testing.T) {
 	}
 
 	t.Run("maintenance-query-jobs-before", wrap(testJobsUptoByType))
-	t.Run("maintenance-delete-job-and-dependencies", wrap(testDeleteJobAndDependencies))
+	t.Run("maintenance-delete-job-results", wrap(testDeleteJobResult))
 }
 
 func setFinishedAt(t *testing.T, q *dbjobqueue.DBJobQueue, id uuid.UUID, finished time.Time) {
@@ -109,112 +110,37 @@ func testJobsUptoByType(t *testing.T, q *dbjobqueue.DBJobQueue) {
 	require.ElementsMatch(t, []uuid.UUID{id80, id85}, ids["octopus"])
 }
 
-func testDeleteJobAndDependencies(t *testing.T, q *dbjobqueue.DBJobQueue) {
-	// id1 -> id2 -> id3
-	id1, err := q.Enqueue("octopus", nil, nil, "")
+func testDeleteJobResult(t *testing.T, q *dbjobqueue.DBJobQueue) {
+	id, err := q.Enqueue("octopus", nil, nil, "")
 	require.NoError(t, err)
-	require.NotEqual(t, uuid.Nil, id1)
-	id2, err := q.Enqueue("octopus", nil, []uuid.UUID{id1}, "")
-	require.NoError(t, err)
-	require.NotEqual(t, uuid.Nil, id2)
-	id3, err := q.Enqueue("octopus", nil, []uuid.UUID{id2}, "")
-	require.NoError(t, err)
-	require.NotEqual(t, uuid.Nil, id3)
-
-	c1, err := q.Enqueue("octopus", nil, nil, "")
-	require.NoError(t, err)
-	require.NotEqual(t, uuid.Nil, c1)
-	c2, err := q.Enqueue("octopus", nil, []uuid.UUID{c1}, "")
-	require.NoError(t, err)
-	require.NotEqual(t, uuid.Nil, c2)
-	c3, err := q.Enqueue("octopus", nil, []uuid.UUID{c2}, "")
-	require.NoError(t, err)
-	require.NotEqual(t, uuid.Nil, c3)
-	controls := []uuid.UUID{c1, c2, c3}
-
-	_, _, _, _, err = q.Job(c1)
+	require.NotEqual(t, uuid.Nil, id)
+	_, _, _, _, _, err = q.Dequeue(context.Background(), []string{"octopus"}, []string{""})
 	require.NoError(t, err)
 
-	require.NoError(t, q.DeleteJobIncludingDependencies(id3))
-	for _, id := range []uuid.UUID{id1, id2, id3} {
-		_, _, _, _, err = q.Job(id)
-		require.ErrorIs(t, err, jobqueue.ErrNotExist)
+	type Result struct {
+		Result string `json:"result"`
 	}
-
-	// controls should still exist
-	for _, c := range controls {
-		_, _, _, _, err = q.Job(c)
-		require.NoError(t, err)
+	result := Result{
+		"deleteme",
 	}
 
-	// id1 -> id2 -> id4 && id3 -> id4
-	id1, err = q.Enqueue("octopus", nil, nil, "")
-	require.NoError(t, err)
-	require.NotEqual(t, uuid.Nil, id1)
-	id2, err = q.Enqueue("octopus", nil, []uuid.UUID{id1}, "")
+	res, err := json.Marshal(result)
 	require.NoError(t, err)
-	require.NotEqual(t, uuid.Nil, id2)
-	id3, err = q.Enqueue("octopus", nil, nil, "")
+	err = q.FinishJob(id, res)
 	require.NoError(t, err)
-	require.NotEqual(t, uuid.Nil, id3)
-	id4, err := q.Enqueue("octopus", nil, []uuid.UUID{id2, id3}, "")
-	require.NoError(t, err)
-	require.NotEqual(t, uuid.Nil, id4)
-
-	require.NoError(t, q.DeleteJobIncludingDependencies(id4))
-	for _, id := range []uuid.UUID{id1, id2, id3, id4} {
-		_, _, _, _, err = q.Job(id)
-		require.ErrorIs(t, err, jobqueue.ErrNotExist)
-	}
 
-	// controls should still exist
-	for _, c := range controls {
-		_, _, _, _, err = q.Job(c)
-		require.NoError(t, err)
-	}
-
-	// id1 has 2 dependants, and the maintenance queries currently do not account for this
-	// situation as it does not occur in the service.  This should be changed once we allow
-	// multiple build job per depsolve job, and the depsolve job should only be removed once all
-	// the build jobs have been dealt with.
-	id1, err = q.Enqueue("octopus", nil, nil, "")
-	require.NoError(t, err)
-	require.NotEqual(t, uuid.Nil, id1)
-	id2a, err := q.Enqueue("octopus", nil, []uuid.UUID{id1}, "")
+	_,r,_,_,_,_,_,err := q.JobStatus(id)
 	require.NoError(t, err)
-	require.NotEqual(t, uuid.Nil, id2a)
-	id2b, err := q.Enqueue("octopus", nil, []uuid.UUID{id1}, "")
-	require.NoError(t, err)
-	require.NotEqual(t, uuid.Nil, id2b)
-	id3, err = q.Enqueue("octopus", nil, []uuid.UUID{id2a}, "")
-	require.NoError(t, err)
-	require.NotEqual(t, uuid.Nil, id3)
 
-	require.NoError(t, q.DeleteJobIncludingDependencies(id3))
-	for _, id := range []uuid.UUID{id1, id2a, id3} {
-		_, _, _, _, err = q.Job(id)
-		require.ErrorIs(t, err, jobqueue.ErrNotExist)
-	}
+	var r1 Result
+	require.NoError(t, json.Unmarshal(r, &r1))
+	require.Equal(t, result, r1)
 
-	// id2b still exists
-	_, _, _, _, err = q.Job(id2b)
+	rows, err := q.DeleteJobResult([]uuid.UUID{id})
 	require.NoError(t, err)
+	require.Equal(t, int64(1), rows)
 
-	// id2b can still be deleted with it's dependencies missing
-	require.NoError(t, q.DeleteJobIncludingDependencies(id2b))
-	_, _, _, _, err = q.Job(id2b)
-	require.ErrorIs(t, err, jobqueue.ErrNotExist)
-
-	// controls should still exist
-	for _, c := range controls {
-		_, _, _, _, err = q.Job(c)
-		require.NoError(t, err)
-	}
-
-	require.NoError(t, q.DeleteJobIncludingDependencies(uuid.Nil))
-	// controls should still exist
-	for _, c := range controls {
-		_, _, _, _, err = q.Job(c)
-		require.NoError(t, err)
-	}
+	_,r,_,_,_,_,_,err = q.JobStatus(id)
+	require.NoError(t, err)
+	require.Nil(t, r)
 }
diff --git a/internal/jobqueue/jobqueuetest/jobqueuetest.go b/internal/jobqueue/jobqueuetest/jobqueuetest.go
index 4cf262d3d..fd391ad1a 100644
--- a/internal/jobqueue/jobqueuetest/jobqueuetest.go
+++ b/internal/jobqueue/jobqueuetest/jobqueuetest.go
@@ -212,7 +212,7 @@ func testDependencies(t *testing.T, q jobqueue.JobQueue) {
 		require.ElementsMatch(t, []uuid.UUID{one, two}, r)
 
 		j := pushTestJob(t, q, "test", nil, []uuid.UUID{one, two}, "")
-		jobType, _, queued, started, finished, canceled, deps, err := q.JobStatus(j)
+		jobType, _, _, queued, started, finished, canceled, deps, err := q.JobStatus(j)
 		require.NoError(t, err)
 		require.Equal(t, jobType, "test")
 		require.True(t, !queued.IsZero())
@@ -223,7 +223,7 @@ func testDependencies(t *testing.T, q jobqueue.JobQueue) {
 
 		require.Equal(t, j, finishNextTestJob(t, q, "test", testResult{}, []uuid.UUID{one, two}))
 
-		jobType, result, queued, started, finished, canceled, deps, err := q.JobStatus(j)
+		jobType, _, result, queued, started, finished, canceled, deps, err := q.JobStatus(j)
 		require.NoError(t, err)
 		require.Equal(t, jobType, "test")
 		require.True(t, !queued.IsZero())
@@ -241,7 +241,7 @@ func testDependencies(t *testing.T, q jobqueue.JobQueue) {
 		two := pushTestJob(t, q, "test", nil, nil, "")
 
 		j := pushTestJob(t, q, "test", nil, []uuid.UUID{one, two}, "")
-		jobType, _, queued, started, finished, canceled, deps, err := q.JobStatus(j)
+		jobType, _, _, queued, started, finished, canceled, deps, err := q.JobStatus(j)
 		require.NoError(t, err)
 		require.Equal(t, jobType, "test")
 		require.True(t, !queued.IsZero())
@@ -257,7 +257,7 @@ func testDependencies(t *testing.T, q jobqueue.JobQueue) {
 
 		require.Equal(t, j, finishNextTestJob(t, q, "test", testResult{}, []uuid.UUID{one, two}))
 
-		jobType, result, queued, started, finished, canceled, deps, err := q.JobStatus(j)
+		jobType, _, result, queued, started, finished, canceled, deps, err := q.JobStatus(j)
 		require.NoError(t, err)
 		require.Equal(t, jobType, "test")
 		require.True(t, !queued.IsZero())
@@ -353,7 +353,7 @@ func testCancel(t *testing.T, q jobqueue.JobQueue) {
 	require.NotEmpty(t, id)
 	err = q.CancelJob(id)
 	require.NoError(t, err)
-	jobType, result, _, _, _, canceled, _, err := q.JobStatus(id)
+	jobType, _, result, _, _, _, canceled, _, err := q.JobStatus(id)
 	require.NoError(t, err)
 	require.Equal(t, jobType, "clownfish")
 	require.True(t, canceled)
@@ -373,7 +373,7 @@ func testCancel(t *testing.T, q jobqueue.JobQueue) {
 	require.Equal(t, json.RawMessage("null"), args)
 	err = q.CancelJob(id)
 	require.NoError(t, err)
-	jobType, result, _, _, _, canceled, _, err = q.JobStatus(id)
+	jobType, _, result, _, _, _, canceled, _, err = q.JobStatus(id)
 	require.NoError(t, err)
 	require.Equal(t, jobType, "clownfish")
 	require.True(t, canceled)
@@ -396,7 +396,7 @@ func testCancel(t *testing.T, q jobqueue.JobQueue) {
 	err = q.CancelJob(id)
 	require.Error(t, err)
 	require.Equal(t, jobqueue.ErrNotRunning, err)
-	jobType, result, _, _, _, canceled, _, err = q.JobStatus(id)
+	jobType, _, result, _, _, _, canceled, _, err = q.JobStatus(id)
 	require.NoError(t, err)
 	require.Equal(t, jobType, "clownfish")
 	require.False(t, canceled)
@@ -613,7 +613,7 @@ func test100dequeuers(t *testing.T, q jobqueue.JobQueue) {
 	// try to do some other operations on the jobqueue
 	id := pushTestJob(t, q, "clownfish", nil, nil, "")
 
-	_, _, _, _, _, _, _, err := q.JobStatus(id)
+	_, _, _, _, _, _, _, _, err := q.JobStatus(id)
 	require.NoError(t, err)
 
 	finishNextTestJob(t, q, "clownfish", testResult{}, nil)
diff --git a/test/cases/api.sh b/test/cases/api.sh
index 7f04e6b91..5102a0e60 100755
--- a/test/cases/api.sh
+++ b/test/cases/api.sh
@@ -12,9 +12,36 @@
 # from a run on a remote continuous integration system.
 #
 
-if (( $# != 1 )); then
-    echo "$0 requires exactly one argument"
-    echo "Please specify an image type to build"
+#
+# Cloud provider / target names
+#
+
+CLOUD_PROVIDER_AWS="aws"
+CLOUD_PROVIDER_GCP="gcp"
+CLOUD_PROVIDER_AZURE="azure"
+CLOUD_PROVIDER_AWS_S3="aws.s3"
+CLOUD_PROVIDER_GENERIC_S3="generic.s3"
+
+#
+# Supported Image type names
+#
+IMAGE_TYPE_AWS="aws"
+IMAGE_TYPE_AZURE="azure"
+IMAGE_TYPE_EDGE_COMMIT="edge-commit"
+IMAGE_TYPE_EDGE_CONTAINER="edge-container"
+IMAGE_TYPE_EDGE_INSTALLER="edge-installer"
+IMAGE_TYPE_GCP="gcp"
+IMAGE_TYPE_IMAGE_INSTALLER="image-installer"
+IMAGE_TYPE_GUEST="guest-image"
+IMAGE_TYPE_VSPHERE="vsphere"
+
+if (( $# > 2 )); then
+    echo "$0 does not support more than two arguments"
+    exit 1
+fi
+
+if (( $# == 0 )); then
+    echo "$0 requires that you set the image type to build"
     exit 1
 fi
 
@@ -22,6 +49,32 @@ set -euxo pipefail
 
 IMAGE_TYPE="$1"
 
+# select cloud provider based on image type
+#
+# the supported image types are listed in the api spec (internal/cloudapi/v2/openapi.v2.yml)
+case ${IMAGE_TYPE} in
+    "$IMAGE_TYPE_AWS")
+        CLOUD_PROVIDER="${CLOUD_PROVIDER_AWS}"
+        ;;
+    "$IMAGE_TYPE_AZURE")
+        CLOUD_PROVIDER="${CLOUD_PROVIDER_AZURE}"
+        ;;
+    "$IMAGE_TYPE_GCP")
+        CLOUD_PROVIDER="${CLOUD_PROVIDER_GCP}"
+        ;;
+    "$IMAGE_TYPE_EDGE_COMMIT"|"$IMAGE_TYPE_EDGE_CONTAINER"|"$IMAGE_TYPE_EDGE_INSTALLER"|"$IMAGE_TYPE_IMAGE_INSTALLER"|"$IMAGE_TYPE_GUEST"|"$IMAGE_TYPE_VSPHERE")
+        # blobby image types: upload to s3 and provide download link
+        CLOUD_PROVIDER="${2:-$CLOUD_PROVIDER_AWS_S3}"
+        if [ "${CLOUD_PROVIDER}" != "${CLOUD_PROVIDER_AWS_S3}" ] && [ "${CLOUD_PROVIDER}" != "${CLOUD_PROVIDER_GENERIC_S3}" ]; then
+            echo "${IMAGE_TYPE} can only be uploaded to either ${CLOUD_PROVIDER_AWS_S3} or ${CLOUD_PROVIDER_GENERIC_S3}"
+            exit 1
+        fi
+        ;;
+    *)
+        echo "Unknown image type: ${IMAGE_TYPE}"
+        exit 1
+esac
+
 # Colorful timestamped output.
 function greenprint {
     echo -e "\033[1;32m[$(date -Isecond)] ${1}\033[0m"
@@ -96,51 +149,6 @@ EOF
 
 sudo systemctl restart osbuild-composer
 
-#
-# Cloud provider / target names
-#
-
-CLOUD_PROVIDER_AWS="aws"
-CLOUD_PROVIDER_GCP="gcp"
-CLOUD_PROVIDER_AZURE="azure"
-CLOUD_PROVIDER_AWS_S3="aws.s3"
-
-#
-# Supported Image type names
-#
-IMAGE_TYPE_AWS="aws"
-IMAGE_TYPE_AZURE="azure"
-IMAGE_TYPE_EDGE_COMMIT="edge-commit"
-IMAGE_TYPE_EDGE_CONTAINER="edge-container"
-IMAGE_TYPE_EDGE_INSTALLER="edge-installer"
-IMAGE_TYPE_GCP="gcp"
-IMAGE_TYPE_IMAGE_INSTALLER="image-installer"
-IMAGE_TYPE_GUEST="guest-image"
-IMAGE_TYPE_VSPHERE="vsphere"
-
-# select cloud provider based on image type
-#
-# the supported image types are listed in the api spec (internal/cloudapi/v2/openapi.v2.yml)
-
-case ${IMAGE_TYPE} in
-    "$IMAGE_TYPE_AWS")
-        CLOUD_PROVIDER="${CLOUD_PROVIDER_AWS}"
-        ;;
-    "$IMAGE_TYPE_AZURE")
-        CLOUD_PROVIDER="${CLOUD_PROVIDER_AZURE}"
-        ;;
-    "$IMAGE_TYPE_GCP")
-        CLOUD_PROVIDER="${CLOUD_PROVIDER_GCP}"
-        ;;
-    "$IMAGE_TYPE_EDGE_COMMIT"|"$IMAGE_TYPE_EDGE_CONTAINER"|"$IMAGE_TYPE_EDGE_INSTALLER"|"$IMAGE_TYPE_IMAGE_INSTALLER"|"$IMAGE_TYPE_GUEST"|"$IMAGE_TYPE_VSPHERE")
-        # blobby image types: upload to s3 and provide download link
-        CLOUD_PROVIDER="${CLOUD_PROVIDER_AWS_S3}"
-        ;;
-    *)
-        echo "Unknown image type: ${IMAGE_TYPE}"
-        exit 1
-esac
-
 greenprint "Using Cloud Provider / Target ${CLOUD_PROVIDER} for Image Type ${IMAGE_TYPE}"
 
 #
@@ -305,6 +313,13 @@ function cleanupVSphere() {
         "${VSPHERE_VM_NAME}"
 }
 
+function cleanupGenericS3() {
+  MINIO_CONTAINER_NAME="${MINIO_CONTAINER_NAME:-}"
+  if [ -n "${MINIO_CONTAINER_NAME}" ]; then
+    sudo ${CONTAINER_RUNTIME} kill "${MINIO_CONTAINER_NAME}"
+  fi
+}
+
 function dump_db() {
   # Disable -x for these commands to avoid printing the whole result and manifest into the log
   set +x
@@ -337,6 +352,10 @@ function cleanup() {
     "$CLOUD_PROVIDER_AZURE")
       cleanupAzure
       ;;
+    "$CLOUD_PROVIDER_GENERIC_S3")
+      cleanupGenericS3
+      [[ "${IMAGE_TYPE}" == "${IMAGE_TYPE_VSPHERE}" ]] && cleanupVSphere
+    ;;
   esac
 
   # dump the DB here to ensure that it gets dumped even if the test fails
@@ -472,6 +491,81 @@ function installClientVSphere() {
     $GOVC_CMD version
 }
 
+function installGenericS3() {
+  local CONTAINER_MINIO_SERVER="quay.io/minio/minio:latest"
+  MINIO_CONTAINER_NAME="minio-server"
+  MINIO_ENDPOINT="http://localhost:9000"
+  local MINIO_ROOT_USER="X29DU5Q6C5NKDQ8PLGVT"
+  local MINIO_ROOT_PASSWORD
+  MINIO_ROOT_PASSWORD=$(date +%s | sha256sum | base64 | head -c 32 ; echo)
+  MINIO_BUCKET="ci-test"
+  local MINIO_REGION="us-east-1"
+  local MINIO_CREDENTIALS_FILE="/etc/osbuild-worker/minio-creds"
+
+  sudo ${CONTAINER_RUNTIME} run --rm -d \
+    --name ${MINIO_CONTAINER_NAME} \
+    -p 9000:9000 \
+    -e MINIO_BROWSER=off \
+    -e MINIO_ROOT_USER="${MINIO_ROOT_USER}" \
+    -e MINIO_ROOT_PASSWORD="${MINIO_ROOT_PASSWORD}" \
+    ${CONTAINER_MINIO_SERVER} server /data
+
+  if ! hash aws; then
+    echo "Using 'awscli' from a container"
+    sudo ${CONTAINER_RUNTIME} pull ${CONTAINER_IMAGE_CLOUD_TOOLS}
+
+    AWS_CMD="sudo ${CONTAINER_RUNTIME} run --rm \
+      -e AWS_ACCESS_KEY_ID=${MINIO_ROOT_USER} \
+      -e AWS_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD} \
+      -v ${WORKDIR}:${WORKDIR}:Z \
+      --network host \
+      ${CONTAINER_IMAGE_CLOUD_TOOLS} aws"
+  else
+    echo "Using pre-installed 'aws' from the system"
+    AWS_CMD="AWS_ACCESS_KEY_ID=${MINIO_ROOT_USER} \
+      AWS_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD} \
+      aws"
+  fi
+  AWS_CMD+=" --region $MINIO_REGION --output json --color on --endpoint-url $MINIO_ENDPOINT"
+  $AWS_CMD --version
+
+  # Configure the local server (retry until the service is up)
+  MINIO_CONFIGURE_RETRY=0
+  MINIO_CONFIGURE_MAX_RETRY=5
+  MINIO_RETRY_INTERVAL=15
+  until [ "${MINIO_CONFIGURE_RETRY}" -ge "${MINIO_CONFIGURE_MAX_RETRY}" ]
+  do
+      ${AWS_CMD} s3 ls && break
+      MINIO_CONFIGURE_RETRY=$((MINIO_CONFIGURE_RETRY + 1))
+    echo "Retrying [${MINIO_CONFIGURE_RETRY}/${MINIO_CONFIGURE_MAX_RETRY}] in ${MINIO_RETRY_INTERVAL}(s) "
+    sleep ${MINIO_RETRY_INTERVAL}
+  done
+
+  if [ "${MINIO_CONFIGURE_RETRY}" -ge "${MINIO_CONFIGURE_MAX_RETRY}" ]; then
+    echo "Failed to communicate with the MinIO server after ${MINIO_CONFIGURE_MAX_RETRY} attempts!"
+    exit 1
+  fi
+
+  # Create the bucket
+  ${AWS_CMD} s3 mb s3://${MINIO_BUCKET}
+
+  cat <<EOF | sudo tee "${MINIO_CREDENTIALS_FILE}"
+[default]
+aws_access_key_id = ${MINIO_ROOT_USER}
+aws_secret_access_key = ${MINIO_ROOT_PASSWORD}
+EOF
+
+  cat <<EOF | sudo tee "/etc/osbuild-worker/osbuild-worker.toml"
+[generic_s3]
+credentials = "${MINIO_CREDENTIALS_FILE}"
+endpoint = "${MINIO_ENDPOINT}"
+region = "${MINIO_REGION}"
+bucket = "${MINIO_BUCKET}"
+EOF
+
+  sudo systemctl restart "osbuild-worker@1"
+}
+
 case $CLOUD_PROVIDER in
   "$CLOUD_PROVIDER_AWS" | "$CLOUD_PROVIDER_AWS_S3")
     installClientAWS
@@ -483,6 +577,10 @@ case $CLOUD_PROVIDER in
   "$CLOUD_PROVIDER_AZURE")
     installClientAzure
     ;;
+  "$CLOUD_PROVIDER_GENERIC_S3")
+    installGenericS3
+    [[ "${IMAGE_TYPE}" == "${IMAGE_TYPE_VSPHERE}" ]] && installClientVSphere
+    ;;
 esac
 
 #
@@ -607,7 +705,8 @@ EOF
 #
 OSTREE_REF="test/rhel/8/edge"
 
-function createReqFileAWSS3() {
+function createReqFileS3() {
+  local IMAGE_REQUEST_REGION=${1:-""}
   cat > "$REQUEST_FILE" << EOF
 {
   "distribution": "$DISTRO",
@@ -641,7 +740,7 @@ function createReqFileAWSS3() {
       "ref": "${OSTREE_REF}"
     },
     "upload_options": {
-      "region": "${AWS_REGION}"
+      "region": "${IMAGE_REQUEST_REGION}"
     }
   }
 }
@@ -650,7 +749,8 @@ EOF
 
 # the VSphere test case does not create any additional users,
 # since this is not supported by the service UI
-function createReqFileAWSS3VSphere() {
+function createReqFileS3VSphere() {
+  local IMAGE_REQUEST_REGION=${1:-""}
   cat > "$REQUEST_FILE" << EOF
 {
   "distribution": "$DISTRO",
@@ -670,7 +770,7 @@ function createReqFileAWSS3VSphere() {
     "image_type": "${IMAGE_TYPE}",
     "repositories": $(jq ".\"$ARCH\"" /usr/share/tests/osbuild-composer/repositories/"$DISTRO".json),
     "upload_options": {
-      "region": "${AWS_REGION}"
+      "region": "${IMAGE_REQUEST_REGION}"
     }
   }
 }
@@ -767,9 +867,9 @@ case $CLOUD_PROVIDER in
     ;;
   "$CLOUD_PROVIDER_AWS_S3")
     if [[ "${IMAGE_TYPE}" == "${IMAGE_TYPE_VSPHERE}" ]]; then
-      createReqFileAWSS3VSphere
+      createReqFileS3VSphere "${AWS_REGION}"
     else
-      createReqFileAWSS3
+      createReqFileS3 "${AWS_REGION}"
     fi
     ;;
   "$CLOUD_PROVIDER_GCP")
@@ -778,6 +878,13 @@ case $CLOUD_PROVIDER in
   "$CLOUD_PROVIDER_AZURE")
     createReqFileAzure
     ;;
+  "$CLOUD_PROVIDER_GENERIC_S3")
+    if [[ "${IMAGE_TYPE}" == "${IMAGE_TYPE_VSPHERE}" ]]; then
+      createReqFileS3VSphere
+    else
+      createReqFileS3
+    fi
+    ;;
 esac
 
 #
@@ -879,7 +986,11 @@ waitForState
 SUBS_COMPOSES="$(collectMetrics)"
 
 test "$UPLOAD_STATUS" = "success"
-test "$UPLOAD_TYPE" = "$CLOUD_PROVIDER"
+EXPECTED_UPLOAD_TYPE="$CLOUD_PROVIDER"
+if [ "${CLOUD_PROVIDER}" == "${CLOUD_PROVIDER_GENERIC_S3}" ]; then
+  EXPECTED_UPLOAD_TYPE="${CLOUD_PROVIDER_AWS_S3}"
+fi
+test "$UPLOAD_TYPE" = "$EXPECTED_UPLOAD_TYPE"
 test $((INIT_COMPOSES+1)) = "$SUBS_COMPOSES"
 
 #
@@ -925,6 +1036,15 @@ function checkUploadStatusOptionsAzure() {
   test "$IMAGE_NAME" = "$AZURE_IMAGE_NAME"
 }
 
+function checkUploadStatusOptionsGenericS3() {
+  local S3_URL
+  S3_URL=$(echo "$UPLOAD_OPTIONS" | jq -r '.url')
+
+  # S3 URL contains endpoint and bucket name
+  echo "$S3_URL" | grep -F "$MINIO_ENDPOINT" -
+  echo "$S3_URL" | grep -F "$MINIO_BUCKET" -
+}
+
 case $CLOUD_PROVIDER in
   "$CLOUD_PROVIDER_AWS")
     checkUploadStatusOptionsAWS
@@ -938,6 +1058,9 @@ case $CLOUD_PROVIDER in
   "$CLOUD_PROVIDER_AZURE")
     checkUploadStatusOptionsAzure
     ;;
+  "$CLOUD_PROVIDER_GENERIC_S3")
+    checkUploadStatusOptionsGenericS3
+    ;;
 esac
 
 #
@@ -1313,7 +1436,8 @@ function verifyInVSphere() {
 }
 
 # Verify s3 blobs
-function verifyInAWSS3() {
+function verifyInS3() {
+    local BUCKET_NAME=${1}
     local S3_URL
     S3_URL=$(echo "$UPLOAD_OPTIONS" | jq -r '.url')
     greenprint "Verifying S3 object at ${S3_URL}"
@@ -1324,7 +1448,7 @@ function verifyInAWSS3() {
 
     # tag the object, also verifying that it exists in the bucket as expected
     $AWS_CMD s3api put-object-tagging \
-        --bucket "${AWS_BUCKET}" \
+        --bucket "${BUCKET_NAME}" \
         --key "${S3_FILENAME}" \
         --tagging '{"TagSet": [{ "Key": "gitlab-ci-test", "Value": "true" }]}'
 
@@ -1481,7 +1605,7 @@ case $CLOUD_PROVIDER in
     verifyInAWS
     ;;
   "$CLOUD_PROVIDER_AWS_S3")
-    verifyInAWSS3
+    verifyInS3 "${AWS_BUCKET}"
     ;;
   "$CLOUD_PROVIDER_GCP")
     verifyInGCP
@@ -1489,6 +1613,9 @@ case $CLOUD_PROVIDER in
   "$CLOUD_PROVIDER_AZURE")
     verifyInAzure
     ;;
+  "$CLOUD_PROVIDER_GENERIC_S3")
+    verifyInS3 "${MINIO_BUCKET}"
+    ;;
 esac
 
 # Verify selected package (postgresql) is included in package list
diff --git a/test/data/manifests/rhel_90-x86_64-azure_rhui-boot.json b/test/data/manifests/rhel_90-x86_64-azure_rhui-boot.json
index a98a2bf39..90f2f8027 100644
--- a/test/data/manifests/rhel_90-x86_64-azure_rhui-boot.json
+++ b/test/data/manifests/rhel_90-x86_64-azure_rhui-boot.json
@@ -2728,30 +2728,6 @@
               ]
             }
           },
-          {
-            "type": "org.osbuild.modprobe",
-            "options": {
-              "filename": "blacklist-skylake-edac.conf",
-              "commands": [
-                {
-                  "command": "blacklist",
-                  "modulename": "skx_edac"
-                }
-              ]
-            }
-          },
-          {
-            "type": "org.osbuild.modprobe",
-            "options": {
-              "filename": "blacklist-intel-cstate.conf",
-              "commands": [
-                {
-                  "command": "blacklist",
-                  "modulename": "intel_cstate"
-                }
-              ]
-            }
-          },
           {
             "type": "org.osbuild.systemd.unit",
             "options": {
diff --git a/test/data/manifests/rhel_91-x86_64-azure_rhui-boot.json b/test/data/manifests/rhel_91-x86_64-azure_rhui-boot.json
index 071ff8118..4e27b115a 100644
--- a/test/data/manifests/rhel_91-x86_64-azure_rhui-boot.json
+++ b/test/data/manifests/rhel_91-x86_64-azure_rhui-boot.json
@@ -6914,30 +6914,6 @@
               ]
             }
           },
-          {
-            "type": "org.osbuild.modprobe",
-            "options": {
-              "filename": "blacklist-skylake-edac.conf",
-              "commands": [
-                {
-                  "command": "blacklist",
-                  "modulename": "skx_edac"
-                }
-              ]
-            }
-          },
-          {
-            "type": "org.osbuild.modprobe",
-            "options": {
-              "filename": "blacklist-intel-cstate.conf",
-              "commands": [
-                {
-                  "command": "blacklist",
-                  "modulename": "intel_cstate"
-                }
-              ]
-            }
-          },
           {
             "type": "org.osbuild.systemd.unit",
             "options": {