Repository: osbuild/osbuild-composer
Pull request #3025 (2022-09-27 18:13:42)

worker: log error details on job failure
Currently errors like clienterror 28 ("at least one target failed") have all the relevant information in the details, don't omit these details from the worker logs.




Patch:
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 60e0a79ce..c7f0dbfb7 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -376,8 +376,8 @@ New OSTree:
     matrix:
       - RUNNER:
           - rhos-01/fedora-35-x86_64
-          # - rhos-01/fedora-36-x86_64
-          # - rhos-01/fedora-37-x86_64
+          - rhos-01/fedora-36-x86_64
+          - rhos-01/fedora-37-x86_64
           - rhos-01/rhel-8.7-nightly-x86_64
           - rhos-01/rhel-9.1-nightly-x86_64
           - rhos-01/centos-stream-8-x86_64
diff --git a/Schutzfile b/Schutzfile
index b87549608..f94c4ea0f 100644
--- a/Schutzfile
+++ b/Schutzfile
@@ -79,7 +79,7 @@
   "fedora-36": {
     "dependencies": {
       "osbuild": {
-        "commit": "c824e18b458823eb7dd8ca582b0a990bfdc7f257"
+        "commit": "8a7b6d382de16b7be30c4d37e10f24c416a294f8"
       }
     },
     "repos": [
@@ -156,7 +156,7 @@
   "fedora-37": {
     "dependencies": {
       "osbuild": {
-        "commit": "c824e18b458823eb7dd8ca582b0a990bfdc7f257"
+        "commit": "8a7b6d382de16b7be30c4d37e10f24c416a294f8"
       }
     },
     "repos": [
diff --git a/cmd/osbuild-worker/jobimpl-osbuild.go b/cmd/osbuild-worker/jobimpl-osbuild.go
index d87b0f33a..de8d0622e 100644
--- a/cmd/osbuild-worker/jobimpl-osbuild.go
+++ b/cmd/osbuild-worker/jobimpl-osbuild.go
@@ -170,6 +170,9 @@ func validateResult(result *worker.OSBuildJobResult, jobID string) {
 	logWithId := logrus.WithField("jobId", jobID)
 	if result.JobError != nil {
 		logWithId.Errorf("osbuild job failed: %s", result.JobError.Reason)
+		if result.JobError.Details != nil {
+			logWithId.Errorf("failure details : %v", result.JobError.Details)
+		}
 		return
 	}
 	// if the job failed, but the JobError is
diff --git a/go.mod b/go.mod
index 57d2a2d10..ce744142c 100644
--- a/go.mod
+++ b/go.mod
@@ -14,7 +14,7 @@ require (
 	github.com/Azure/go-autorest/autorest v0.11.28
 	github.com/Azure/go-autorest/autorest/azure/auth v0.5.11
 	github.com/BurntSushi/toml v1.2.0
-	github.com/aws/aws-sdk-go v1.44.104
+	github.com/aws/aws-sdk-go v1.44.108
 	github.com/containers/common v0.49.1
 	github.com/containers/image/v5 v5.22.0
 	github.com/coreos/go-semver v0.3.0
@@ -48,7 +48,7 @@ require (
 	golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1
 	golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
 	golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10
-	google.golang.org/api v0.97.0
+	google.golang.org/api v0.98.0
 	google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006
 	gopkg.in/ini.v1 v1.67.0
 )
diff --git a/go.sum b/go.sum
index 25b8444ab..1c73a5d64 100644
--- a/go.sum
+++ b/go.sum
@@ -266,8 +266,8 @@ github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi
 github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
 github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
 github.com/aws/aws-sdk-go v1.44.44/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
-github.com/aws/aws-sdk-go v1.44.104 h1:NiPYL60aOSH0TsAzQngx/aBdxC12TXhgw07DQFh76GU=
-github.com/aws/aws-sdk-go v1.44.104/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
+github.com/aws/aws-sdk-go v1.44.108 h1:L8N9GmP9UYDNqBtJO6OC4zSuEkQxAR770VkbRXAUmRk=
+github.com/aws/aws-sdk-go v1.44.108/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
 github.com/aws/aws-sdk-go-v2 v1.16.5/go.mod h1:Wh7MEsmEApyL5hrWzpDkba4gwAPc5/piwLVLFnCxp48=
 github.com/aws/aws-sdk-go-v2/config v1.15.11/go.mod h1:mD5tNFciV7YHNjPpFYqJ6KGpoSfY107oZULvTHIxtbI=
 github.com/aws/aws-sdk-go-v2/credentials v1.12.6/go.mod h1:mQgnRmBPF2S/M01W4T4Obp3ZaZB6o1s/R8cOUda9vtI=
@@ -2375,8 +2375,9 @@ google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOI
 google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
 google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI=
 google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
-google.golang.org/api v0.97.0 h1:x/vEL1XDF/2V4xzdNgFPaKHluRESo2aTsL7QzHnBtGQ=
 google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
+google.golang.org/api v0.98.0 h1:yxZrcxXESimy6r6mdL5Q6EnZwmewDJK2dVg3g75s5Dg=
+google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
diff --git a/internal/distro/fedora/distro.go b/internal/distro/fedora/distro.go
index dd7f44ff0..ac89a2949 100644
--- a/internal/distro/fedora/distro.go
+++ b/internal/distro/fedora/distro.go
@@ -126,7 +126,7 @@ var (
 		bootISO:          true,
 		image:            iotInstallerImage,
 		buildPipelines:   []string{"build"},
-		payloadPipelines: []string{"anaconda-tree", "bootiso-tree", "bootiso"},
+		payloadPipelines: []string{"anaconda-tree", "rootfs-image", "efiboot-tree", "bootiso-tree"},
 		exports:          []string{"bootiso"},
 	}
 
diff --git a/internal/distro/fedora/images.go b/internal/distro/fedora/images.go
index bf9d59aa3..82239e0cd 100644
--- a/internal/distro/fedora/images.go
+++ b/internal/distro/fedora/images.go
@@ -257,8 +257,8 @@ func iotInstallerImage(workload workload.Workload,
 
 	img.Platform = t.platform
 	img.ExtraBasePackages = packageSets[installerPkgsKey]
-	img.Users = customizations.GetUsers()
-	img.Groups = customizations.GetGroups()
+	img.Users = users.UsersFromBP(customizations.GetUsers())
+	img.Groups = users.GroupsFromBP(customizations.GetGroups())
 
 	img.ISOLabelTempl = d.isolabelTmpl
 	img.Product = d.product
@@ -288,7 +288,7 @@ func iotRawImage(workload workload.Workload,
 	img.Users = users.UsersFromBP(customizations.GetUsers())
 	img.Groups = users.GroupsFromBP(customizations.GetGroups())
 
-	img.KernelOptionsAppend = []string{"modprobe.blacklist=vc4"}
+	img.KernelOptionsAppend = []string{"modprobe.blacklist=vc4", "rw"}
 	img.Keyboard = "us"
 	img.Locale = "C.UTF-8"
 
diff --git a/internal/distro/rhel7/pipelines.go b/internal/distro/rhel7/pipelines.go
index c2134e6d4..b283c8a1a 100644
--- a/internal/distro/rhel7/pipelines.go
+++ b/internal/distro/rhel7/pipelines.go
@@ -8,6 +8,7 @@ import (
 	"github.com/osbuild/osbuild-composer/internal/distro"
 	"github.com/osbuild/osbuild-composer/internal/osbuild"
 	"github.com/osbuild/osbuild-composer/internal/rpmmd"
+	"github.com/osbuild/osbuild-composer/internal/users"
 )
 
 func buildPipeline(repos []rpmmd.RepoConfig, buildPackageSpecs []rpmmd.PackageSpec, runner string) *osbuild.Pipeline {
@@ -87,10 +88,10 @@ func osPipeline(t *imageType,
 	}
 
 	if groups := c.GetGroups(); len(groups) > 0 {
-		p.AddStage(osbuild.NewGroupsStage(osbuild.NewGroupsStageOptions(groups)))
+		p.AddStage(osbuild.NewGroupsStage(osbuild.NewGroupsStageOptions(users.GroupsFromBP(groups))))
 	}
 
-	if userOptions, err := osbuild.NewUsersStageOptions(c.GetUsers(), false); err != nil {
+	if userOptions, err := osbuild.NewUsersStageOptions(users.UsersFromBP(c.GetUsers()), false); err != nil {
 		return nil, err
 	} else if userOptions != nil {
 		p.AddStage(osbuild.NewUsersStage(userOptions))
@@ -315,7 +316,7 @@ func liveImagePipeline(inputPipelineName string, outputFilename string, pt *disk
 
 	inputName := "root-tree"
 	copyOptions, copyDevices, copyMounts := osbuild.GenCopyFSTreeOptions(inputName, inputPipelineName, outputFilename, pt)
-	copyInputs := osbuild.NewCopyStagePipelineTreeInputs(inputName, inputPipelineName)
+	copyInputs := osbuild.NewPipelineTreeInputs(inputName, inputPipelineName)
 	p.AddStage(osbuild.NewCopyStage(copyOptions, copyInputs, copyDevices, copyMounts))
 
 	for _, stage := range osbuild.GenImageFinishStages(pt, outputFilename) {
diff --git a/internal/distro/rhel8/pipelines.go b/internal/distro/rhel8/pipelines.go
index 1fe347ecd..06b2dc5f2 100644
--- a/internal/distro/rhel8/pipelines.go
+++ b/internal/distro/rhel8/pipelines.go
@@ -216,7 +216,7 @@ func edgeInstallerPipelines(t *imageType, customizations *blueprint.Customizatio
 	kernelVer := rpmmd.GetVerStrFromPackageSpecListPanic(installerPackages, "kernel")
 	ostreeRepoPath := "/ostree/repo"
 	payloadStages := ostreePayloadStages(options, ostreeRepoPath)
-	kickstartOptions, err := osbuild.NewKickstartStageOptions(kspath, "", customizations.GetUsers(), customizations.GetGroups(), makeISORootPath(ostreeRepoPath), options.OSTree.Ref, "rhel")
+	kickstartOptions, err := osbuild.NewKickstartStageOptions(kspath, "", users.UsersFromBP(customizations.GetUsers()), users.GroupsFromBP(customizations.GetGroups()), makeISORootPath(ostreeRepoPath), options.OSTree.Ref, "rhel")
 	if err != nil {
 		return nil, err
 	}
@@ -254,8 +254,8 @@ func imageInstallerPipelines(t *imageType, customizations *blueprint.Customizati
 	kernelVer := fmt.Sprintf("%s-%s.%s", kernelPkg.Version, kernelPkg.Release, kernelPkg.Arch)
 
 	tarPath := "/liveimg.tar"
-	tarPayloadStages := []*osbuild.Stage{osbuild.NewTarStage(&osbuild.TarStageOptions{Filename: tarPath}, osbuild.NewTarStagePipelineTreeInputs(treePipeline.Name))}
-	kickstartOptions, err := osbuild.NewKickstartStageOptions(kspath, makeISORootPath(tarPath), customizations.GetUsers(), customizations.GetGroups(), "", "", "rhel")
+	tarPayloadStages := []*osbuild.Stage{osbuild.NewTarStage(&osbuild.TarStageOptions{Filename: tarPath}, treePipeline.Name)}
+	kickstartOptions, err := osbuild.NewKickstartStageOptions(kspath, makeISORootPath(tarPath), users.UsersFromBP(customizations.GetUsers()), users.GroupsFromBP(customizations.GetGroups()), "", "", "rhel")
 	if err != nil {
 		return nil, err
 	}
@@ -449,17 +449,17 @@ func osPipeline(t *imageType,
 		// don't put users and groups in the payload of an installer
 		// add them via kickstart instead
 		if groups := c.GetGroups(); len(groups) > 0 {
-			p.AddStage(osbuild.NewGroupsStage(osbuild.NewGroupsStageOptions(groups)))
+			p.AddStage(osbuild.NewGroupsStage(osbuild.NewGroupsStageOptions(users.GroupsFromBP(groups))))
 		}
 
-		if userOptions, err := osbuild.NewUsersStageOptions(c.GetUsers(), false); err != nil {
+		if userOptions, err := osbuild.NewUsersStageOptions(users.UsersFromBP(c.GetUsers()), false); err != nil {
 			return nil, err
 		} else if userOptions != nil {
 			if t.rpmOstree {
 				// for ostree, writing the key during user creation is
 				// redundant and can cause issues so create users without keys
 				// and write them on first boot
-				userOptionsSansKeys, err := osbuild.NewUsersStageOptions(c.GetUsers(), true)
+				userOptionsSansKeys, err := osbuild.NewUsersStageOptions(users.UsersFromBP(c.GetUsers()), true)
 				if err != nil {
 					return nil, err
 				}
@@ -683,18 +683,15 @@ func ostreeCommitPipeline(options distro.ImageOptions, osVersion string) *osbuil
 	p.Build = "name:build"
 	p.AddStage(osbuild.NewOSTreeInitStage(&osbuild.OSTreeInitStageOptions{Path: "/repo"}))
 
-	commitStageInput := new(osbuild.OSTreeCommitStageInput)
-	commitStageInput.Type = "org.osbuild.tree"
-	commitStageInput.Origin = "org.osbuild.pipeline"
-	commitStageInput.References = osbuild.OSTreeCommitStageReferences{"name:ostree-tree"}
-
-	p.AddStage(osbuild.NewOSTreeCommitStage(
-		&osbuild.OSTreeCommitStageOptions{
-			Ref:       options.OSTree.Ref,
-			OSVersion: osVersion,
-			Parent:    options.OSTree.Parent,
-		},
-		&osbuild.OSTreeCommitStageInputs{Tree: commitStageInput}),
+	p.AddStage(
+		osbuild.NewOSTreeCommitStage(
+			&osbuild.OSTreeCommitStageOptions{
+				Ref:       options.OSTree.Ref,
+				OSVersion: osVersion,
+				Parent:    options.OSTree.Parent,
+			},
+			"ostree-tree",
+		),
 	)
 	return p
 }
@@ -740,10 +737,7 @@ func containerPipeline(t *imageType, nginxConfigPath, listenPort string) *osbuil
 			ExposedPorts: []string{listenPort},
 		},
 	}
-	baseInput := new(osbuild.OCIArchiveStageInput)
-	baseInput.Type = "org.osbuild.tree"
-	baseInput.Origin = "org.osbuild.pipeline"
-	baseInput.References = []string{"name:container-tree"}
+	baseInput := osbuild.NewTreeInput("name:container-tree")
 	inputs := &osbuild.OCIArchiveStageInputs{Base: baseInput}
 	p.AddStage(osbuild.NewOCIArchiveStage(options, inputs))
 	return p
@@ -847,12 +841,12 @@ func simplifiedInstallerBootISOTreePipeline(archivePipelineName, kver string, rn
 	}
 
 	inputName := "root-tree"
-	copyInputs := osbuild.NewCopyStagePipelineTreeInputs(inputName, "efiboot-tree")
+	copyInputs := osbuild.NewPipelineTreeInputs(inputName, "efiboot-tree")
 	copyOptions, copyDevices, copyMounts := osbuild.GenCopyFSTreeOptions(inputName, "efiboot-tree", filename, &pt)
 	p.AddStage(osbuild.NewCopyStage(copyOptions, copyInputs, copyDevices, copyMounts))
 
 	inputName = "coi"
-	copyInputs = osbuild.NewCopyStagePipelineTreeInputs(inputName, "coi-tree")
+	copyInputs = osbuild.NewPipelineTreeInputs(inputName, "coi-tree")
 	p.AddStage(osbuild.NewCopyStageSimple(
 		&osbuild.CopyStageOptions{
 			Paths: []osbuild.CopyStagePath{
@@ -870,7 +864,7 @@ func simplifiedInstallerBootISOTreePipeline(archivePipelineName, kver string, rn
 	))
 
 	inputName = "efi-tree"
-	copyInputs = osbuild.NewCopyStagePipelineTreeInputs(inputName, "efiboot-tree")
+	copyInputs = osbuild.NewPipelineTreeInputs(inputName, "efiboot-tree")
 	p.AddStage(osbuild.NewCopyStageSimple(
 		&osbuild.CopyStageOptions{
 			Paths: []osbuild.CopyStagePath{
@@ -1087,7 +1081,7 @@ func bootISOPipeline(filename, isolabel, arch string, isolinux bool) *osbuild.Pi
 	p.Name = "bootiso"
 	p.Build = "name:build"
 
-	p.AddStage(osbuild.NewXorrisofsStage(xorrisofsStageOptions(filename, isolabel, arch, isolinux), osbuild.NewXorrisofsStagePipelineTreeInputs("bootiso-tree")))
+	p.AddStage(osbuild.NewXorrisofsStage(xorrisofsStageOptions(filename, isolabel, arch, isolinux), "bootiso-tree"))
 	p.AddStage(osbuild.NewImplantisomd5Stage(&osbuild.Implantisomd5StageOptions{Filename: filename}))
 
 	return p
@@ -1104,7 +1098,7 @@ func liveImagePipeline(inputPipelineName string, outputFilename string, pt *disk
 
 	inputName := "root-tree"
 	copyOptions, copyDevices, copyMounts := osbuild.GenCopyFSTreeOptions(inputName, inputPipelineName, outputFilename, pt)
-	copyInputs := osbuild.NewCopyStagePipelineTreeInputs(inputName, inputPipelineName)
+	copyInputs := osbuild.NewPipelineTreeInputs(inputName, inputPipelineName)
 	p.AddStage(osbuild.NewCopyStage(copyOptions, copyInputs, copyDevices, copyMounts))
 
 	for _, stage := range osbuild.GenImageFinishStages(pt, outputFilename) {
@@ -1134,7 +1128,7 @@ func tarArchivePipeline(name, inputPipelineName string, tarOptions *osbuild.TarS
 	p := new(osbuild.Pipeline)
 	p.Name = name
 	p.Build = "name:build"
-	p.AddStage(osbuild.NewTarStage(tarOptions, osbuild.NewTarStagePipelineTreeInputs(inputPipelineName)))
+	p.AddStage(osbuild.NewTarStage(tarOptions, inputPipelineName))
 	return p
 }
 
diff --git a/internal/distro/rhel9/pipelines.go b/internal/distro/rhel9/pipelines.go
index cfe96582a..65b7e2ec7 100644
--- a/internal/distro/rhel9/pipelines.go
+++ b/internal/distro/rhel9/pipelines.go
@@ -211,7 +211,7 @@ func edgeInstallerPipelines(t *imageType, customizations *blueprint.Customizatio
 	kernelVer := rpmmd.GetVerStrFromPackageSpecListPanic(installerPackages, "kernel")
 	ostreeRepoPath := "/ostree/repo"
 	payloadStages := ostreePayloadStages(options, ostreeRepoPath)
-	kickstartOptions, err := osbuild.NewKickstartStageOptions(kspath, "", customizations.GetUsers(), customizations.GetGroups(), makeISORootPath(ostreeRepoPath), options.OSTree.Ref, "rhel")
+	kickstartOptions, err := osbuild.NewKickstartStageOptions(kspath, "", users.UsersFromBP(customizations.GetUsers()), users.GroupsFromBP(customizations.GetGroups()), makeISORootPath(ostreeRepoPath), options.OSTree.Ref, "rhel")
 	if err != nil {
 		return nil, err
 	}
@@ -249,8 +249,8 @@ func imageInstallerPipelines(t *imageType, customizations *blueprint.Customizati
 	kernelVer := fmt.Sprintf("%s-%s.%s", kernelPkg.Version, kernelPkg.Release, kernelPkg.Arch)
 
 	tarPath := "/liveimg.tar"
-	tarPayloadStages := []*osbuild.Stage{osbuild.NewTarStage(&osbuild.TarStageOptions{Filename: tarPath}, osbuild.NewTarStagePipelineTreeInputs(treePipeline.Name))}
-	kickstartOptions, err := osbuild.NewKickstartStageOptions(kspath, makeISORootPath(tarPath), customizations.GetUsers(), customizations.GetGroups(), "", "", "rhel")
+	tarPayloadStages := []*osbuild.Stage{osbuild.NewTarStage(&osbuild.TarStageOptions{Filename: tarPath}, treePipeline.Name)}
+	kickstartOptions, err := osbuild.NewKickstartStageOptions(kspath, makeISORootPath(tarPath), users.UsersFromBP(customizations.GetUsers()), users.GroupsFromBP(customizations.GetGroups()), "", "", "rhel")
 	if err != nil {
 		return nil, err
 	}
@@ -444,17 +444,17 @@ func osPipeline(t *imageType,
 		// don't put users and groups in the payload of an installer
 		// add them via kickstart instead
 		if groups := c.GetGroups(); len(groups) > 0 {
-			p.AddStage(osbuild.NewGroupsStage(osbuild.NewGroupsStageOptions(groups)))
+			p.AddStage(osbuild.NewGroupsStage(osbuild.NewGroupsStageOptions(users.GroupsFromBP(groups))))
 		}
 
-		if userOptions, err := osbuild.NewUsersStageOptions(c.GetUsers(), false); err != nil {
+		if userOptions, err := osbuild.NewUsersStageOptions(users.UsersFromBP(c.GetUsers()), false); err != nil {
 			return nil, err
 		} else if userOptions != nil {
 			if t.rpmOstree {
 				// for ostree, writing the key during user creation is
 				// redundant and can cause issues so create users without keys
 				// and write them on first boot
-				userOptionsSansKeys, err := osbuild.NewUsersStageOptions(c.GetUsers(), true)
+				userOptionsSansKeys, err := osbuild.NewUsersStageOptions(users.UsersFromBP(c.GetUsers()), true)
 				if err != nil {
 					return nil, err
 				}
@@ -684,18 +684,13 @@ func ostreeCommitPipeline(options distro.ImageOptions, osVersion string) *osbuil
 	p.Build = "name:build"
 	p.AddStage(osbuild.NewOSTreeInitStage(&osbuild.OSTreeInitStageOptions{Path: "/repo"}))
 
-	commitStageInput := new(osbuild.OSTreeCommitStageInput)
-	commitStageInput.Type = "org.osbuild.tree"
-	commitStageInput.Origin = "org.osbuild.pipeline"
-	commitStageInput.References = osbuild.OSTreeCommitStageReferences{"name:ostree-tree"}
-
 	p.AddStage(osbuild.NewOSTreeCommitStage(
 		&osbuild.OSTreeCommitStageOptions{
 			Ref:       options.OSTree.Ref,
 			OSVersion: osVersion,
 			Parent:    options.OSTree.Parent,
 		},
-		&osbuild.OSTreeCommitStageInputs{Tree: commitStageInput}),
+		"ostree-tree"),
 	)
 	return p
 }
@@ -742,10 +737,7 @@ func containerPipeline(t *imageType, nginxConfigPath, listenPort string) *osbuil
 			ExposedPorts: []string{listenPort},
 		},
 	}
-	baseInput := new(osbuild.OCIArchiveStageInput)
-	baseInput.Type = "org.osbuild.tree"
-	baseInput.Origin = "org.osbuild.pipeline"
-	baseInput.References = []string{"name:container-tree"}
+	baseInput := osbuild.NewTreeInput("name:container-tree")
 	inputs := &osbuild.OCIArchiveStageInputs{Base: baseInput}
 	p.AddStage(osbuild.NewOCIArchiveStage(options, inputs))
 	return p
@@ -848,12 +840,12 @@ func simplifiedInstallerBootISOTreePipeline(archivePipelineName, kver string, rn
 	}
 
 	inputName := "root-tree"
-	copyInputs := osbuild.NewCopyStagePipelineTreeInputs(inputName, "efiboot-tree")
+	copyInputs := osbuild.NewPipelineTreeInputs(inputName, "efiboot-tree")
 	copyOptions, copyDevices, copyMounts := osbuild.GenCopyFSTreeOptions(inputName, "efiboot-tree", filename, &pt)
 	p.AddStage(osbuild.NewCopyStage(copyOptions, copyInputs, copyDevices, copyMounts))
 
 	inputName = "coi"
-	copyInputs = osbuild.NewCopyStagePipelineTreeInputs(inputName, "coi-tree")
+	copyInputs = osbuild.NewPipelineTreeInputs(inputName, "coi-tree")
 	p.AddStage(osbuild.NewCopyStageSimple(
 		&osbuild.CopyStageOptions{
 			Paths: []osbuild.CopyStagePath{
@@ -871,7 +863,7 @@ func simplifiedInstallerBootISOTreePipeline(archivePipelineName, kver string, rn
 	))
 
 	inputName = "efi-tree"
-	copyInputs = osbuild.NewCopyStagePipelineTreeInputs(inputName, "efiboot-tree")
+	copyInputs = osbuild.NewPipelineTreeInputs(inputName, "efiboot-tree")
 	p.AddStage(osbuild.NewCopyStageSimple(
 		&osbuild.CopyStageOptions{
 			Paths: []osbuild.CopyStagePath{
@@ -1090,7 +1082,7 @@ func bootISOPipeline(filename, isolabel, arch string, isolinux bool) *osbuild.Pi
 	p.Name = "bootiso"
 	p.Build = "name:build"
 
-	p.AddStage(osbuild.NewXorrisofsStage(xorrisofsStageOptions(filename, isolabel, arch, isolinux), osbuild.NewXorrisofsStagePipelineTreeInputs("bootiso-tree")))
+	p.AddStage(osbuild.NewXorrisofsStage(xorrisofsStageOptions(filename, isolabel, arch, isolinux), "bootiso-tree"))
 	p.AddStage(osbuild.NewImplantisomd5Stage(&osbuild.Implantisomd5StageOptions{Filename: filename}))
 
 	return p
@@ -1107,7 +1099,7 @@ func liveImagePipeline(inputPipelineName string, outputFilename string, pt *disk
 
 	inputName := "root-tree"
 	copyOptions, copyDevices, copyMounts := osbuild.GenCopyFSTreeOptions(inputName, inputPipelineName, outputFilename, pt)
-	copyInputs := osbuild.NewCopyStagePipelineTreeInputs(inputName, inputPipelineName)
+	copyInputs := osbuild.NewPipelineTreeInputs(inputName, inputPipelineName)
 	p.AddStage(osbuild.NewCopyStage(copyOptions, copyInputs, copyDevices, copyMounts))
 
 	for _, stage := range osbuild.GenImageFinishStages(pt, outputFilename) {
@@ -1136,7 +1128,7 @@ func tarArchivePipeline(name, inputPipelineName string, tarOptions *osbuild.TarS
 	p := new(osbuild.Pipeline)
 	p.Name = name
 	p.Build = "name:build"
-	p.AddStage(osbuild.NewTarStage(tarOptions, osbuild.NewTarStagePipelineTreeInputs(inputPipelineName)))
+	p.AddStage(osbuild.NewTarStage(tarOptions, inputPipelineName))
 	return p
 }
 
diff --git a/internal/image/ostree_installer.go b/internal/image/ostree_installer.go
index 4d9f17b09..7dffd217d 100644
--- a/internal/image/ostree_installer.go
+++ b/internal/image/ostree_installer.go
@@ -1,22 +1,25 @@
 package image
 
 import (
+	"fmt"
 	"math/rand"
 
 	"github.com/osbuild/osbuild-composer/internal/artifact"
-	"github.com/osbuild/osbuild-composer/internal/blueprint"
+	"github.com/osbuild/osbuild-composer/internal/common"
+	"github.com/osbuild/osbuild-composer/internal/disk"
 	"github.com/osbuild/osbuild-composer/internal/manifest"
 	"github.com/osbuild/osbuild-composer/internal/platform"
 	"github.com/osbuild/osbuild-composer/internal/rpmmd"
 	"github.com/osbuild/osbuild-composer/internal/runner"
+	"github.com/osbuild/osbuild-composer/internal/users"
 )
 
 type OSTreeInstaller struct {
 	Base
 	Platform          platform.Platform
 	ExtraBasePackages rpmmd.PackageSet
-	Users             []blueprint.UserCustomization
-	Groups            []blueprint.GroupCustomization
+	Users             []users.User
+	Groups            []users.Group
 
 	ISOLabelTempl string
 	Product       string
@@ -48,7 +51,8 @@ func (img *OSTreeInstaller) InstantiateManifest(m *manifest.Manifest,
 	anacondaPipeline := manifest.NewAnaconda(m,
 		buildPipeline,
 		img.Platform,
-		repos, "kernel",
+		repos,
+		"kernel",
 		img.Product,
 		img.OSVersion)
 	anacondaPipeline.ExtraPackages = img.ExtraBasePackages.Include
@@ -58,18 +62,48 @@ func (img *OSTreeInstaller) InstantiateManifest(m *manifest.Manifest,
 	anacondaPipeline.Biosdevname = (img.Platform.GetArch() == platform.ARCH_X86_64)
 	anacondaPipeline.Checkpoint()
 
+	rootfsPartitionTable := &disk.PartitionTable{
+		Size: 20 * common.MebiByte,
+		Partitions: []disk.Partition{
+			{
+				Start: 0,
+				Size:  20 * common.MebiByte,
+				Payload: &disk.Filesystem{
+					Type:       "vfat",
+					Mountpoint: "/",
+					UUID:       disk.NewVolIDFromRand(rng),
+				},
+			},
+		},
+	}
+
+	// TODO: replace isoLabelTmpl with more high-level properties
+	isoLabel := fmt.Sprintf(img.ISOLabelTempl, img.Platform.GetArch())
+
+	rootfsImagePipeline := manifest.NewISORootfsImg(m, buildPipeline, anacondaPipeline)
+	rootfsImagePipeline.Size = 4 * common.GibiByte
+
+	bootTreePipeline := manifest.NewEFIBootTree(m, buildPipeline, anacondaPipeline)
+	bootTreePipeline.Platform = img.Platform
+	bootTreePipeline.UEFIVendor = img.Platform.GetUEFIVendor()
+	bootTreePipeline.KSPath = "/ostree.ks"
+	bootTreePipeline.ISOLabel = isoLabel
+
 	isoTreePipeline := manifest.NewISOTree(m,
 		buildPipeline,
 		anacondaPipeline,
+		rootfsImagePipeline,
+		bootTreePipeline,
 		img.OSTreeCommit,
 		img.OSTreeURL,
 		img.OSTreeRef,
-		img.ISOLabelTempl)
+		isoLabel)
+	isoTreePipeline.PartitionTable = rootfsPartitionTable
 	isoTreePipeline.Release = img.Release
 	isoTreePipeline.OSName = img.OSName
-	isoTreePipeline.UEFIVendor = img.Platform.GetUEFIVendor()
 	isoTreePipeline.Users = img.Users
 	isoTreePipeline.Groups = img.Groups
+	isoTreePipeline.KSPath = "/ostree.ks"
 
 	isoPipeline := manifest.NewISO(m, buildPipeline, isoTreePipeline)
 	isoPipeline.Filename = img.Filename
diff --git a/internal/manifest/commit.go b/internal/manifest/commit.go
index ad9b2520b..5d7dce497 100644
--- a/internal/manifest/commit.go
+++ b/internal/manifest/commit.go
@@ -49,11 +49,6 @@ func (p *OSTreeCommit) serialize() osbuild.Pipeline {
 
 	pipeline.AddStage(osbuild.NewOSTreeInitStage(&osbuild.OSTreeInitStageOptions{Path: "/repo"}))
 
-	commitStageInput := new(osbuild.OSTreeCommitStageInput)
-	commitStageInput.Type = "org.osbuild.tree"
-	commitStageInput.Origin = "org.osbuild.pipeline"
-	commitStageInput.References = osbuild.OSTreeCommitStageReferences{"name:" + p.treePipeline.Name()}
-
 	var parent string
 	if p.treePipeline.OSTree.Parent != nil {
 		parent = p.treePipeline.OSTree.Parent.Checksum
@@ -64,7 +59,7 @@ func (p *OSTreeCommit) serialize() osbuild.Pipeline {
 			OSVersion: p.OSVersion,
 			Parent:    parent,
 		},
-		&osbuild.OSTreeCommitStageInputs{Tree: commitStageInput}),
+		p.treePipeline.Name()),
 	)
 
 	return pipeline
diff --git a/internal/manifest/commit_deployment.go b/internal/manifest/commit_deployment.go
index 928651ae9..03cdf6763 100644
--- a/internal/manifest/commit_deployment.go
+++ b/internal/manifest/commit_deployment.go
@@ -93,17 +93,6 @@ func (p *OSTreeDeployment) serialize() osbuild.Pipeline {
 			OSName: p.osName,
 		},
 	))
-	pipeline.AddStage(osbuild.NewOSTreeConfigStage(
-		&osbuild.OSTreeConfigStageOptions{
-			Repo: repoPath,
-			Config: &osbuild.OSTreeConfig{
-				Sysroot: &osbuild.SysrootOptions{
-					ReadOnly:   common.BoolToPtr(true),
-					Bootloader: "none",
-				},
-			},
-		},
-	))
 	pipeline.AddStage(osbuild.NewMkdirStage(&osbuild.MkdirStageOptions{
 		Paths: []osbuild.Path{
 			{
@@ -156,6 +145,20 @@ func (p *OSTreeDeployment) serialize() osbuild.Pipeline {
 		},
 	))
 
+	configStage := osbuild.NewOSTreeConfigStage(
+		&osbuild.OSTreeConfigStageOptions{
+			Repo: repoPath,
+			Config: &osbuild.OSTreeConfig{
+				Sysroot: &osbuild.SysrootOptions{
+					ReadOnly:   common.BoolToPtr(true),
+					Bootloader: "none",
+				},
+			},
+		},
+	)
+	configStage.MountOSTree(p.osName, p.osTreeRef, 0)
+	pipeline.AddStage(configStage)
+
 	fstabOptions := osbuild.NewFSTabStageOptions(p.PartitionTable)
 	fstabStage := osbuild.NewFSTabStage(fstabOptions)
 	fstabStage.MountOSTree(p.osName, p.osTreeRef, 0)
diff --git a/internal/manifest/efi_boot_tree.go b/internal/manifest/efi_boot_tree.go
new file mode 100644
index 000000000..a87a8a6eb
--- /dev/null
+++ b/internal/manifest/efi_boot_tree.go
@@ -0,0 +1,62 @@
+package manifest
+
+import (
+	"fmt"
+
+	"github.com/osbuild/osbuild-composer/internal/distro"
+	"github.com/osbuild/osbuild-composer/internal/osbuild"
+	"github.com/osbuild/osbuild-composer/internal/platform"
+)
+
+type EFIBootTree struct {
+	Base
+
+	Platform platform.Platform
+
+	anacondaPipeline *Anaconda
+
+	UEFIVendor string
+	ISOLabel   string
+	KSPath     string
+}
+
+func NewEFIBootTree(m *Manifest, buildPipeline *Build, anacondaPipeline *Anaconda) *EFIBootTree {
+	p := &EFIBootTree{
+		Base:             NewBase(m, "efiboot-tree", buildPipeline),
+		anacondaPipeline: anacondaPipeline,
+	}
+	buildPipeline.addDependent(p)
+	m.addPipeline(p)
+	return p
+}
+
+func (p *EFIBootTree) serialize() osbuild.Pipeline {
+	pipeline := p.Base.serialize()
+
+	arch := p.Platform.GetArch().String()
+	var architectures []string
+	if arch == distro.X86_64ArchName {
+		architectures = []string{"X64"}
+	} else if arch == distro.Aarch64ArchName {
+		architectures = []string{"AA64"}
+	} else {
+		panic("unsupported architecture")
+	}
+
+	grubOptions := &osbuild.GrubISOStageOptions{
+		Product: osbuild.Product{
+			Name:    p.anacondaPipeline.product,
+			Version: p.anacondaPipeline.version,
+		},
+		Kernel: osbuild.ISOKernel{
+			Dir:  "/images/pxeboot",
+			Opts: []string{fmt.Sprintf("inst.ks=hd:LABEL=%s:%s", p.ISOLabel, p.KSPath)},
+		},
+		ISOLabel:      p.ISOLabel,
+		Architectures: architectures,
+		Vendor:        p.UEFIVendor,
+	}
+	grub2Stage := osbuild.NewGrubISOStage(grubOptions)
+	pipeline.AddStage(grub2Stage)
+	return pipeline
+}
diff --git a/internal/manifest/iso.go b/internal/manifest/iso.go
index 8fd638670..d4f74e70f 100644
--- a/internal/manifest/iso.go
+++ b/internal/manifest/iso.go
@@ -41,7 +41,7 @@ func (p *ISO) getBuildPackages() []string {
 func (p *ISO) serialize() osbuild.Pipeline {
 	pipeline := p.Base.serialize()
 
-	pipeline.AddStage(osbuild.NewXorrisofsStage(xorrisofsStageOptions(p.Filename, p.treePipeline.isoLabel, p.ISOLinux), osbuild.NewXorrisofsStagePipelineTreeInputs(p.treePipeline.Name())))
+	pipeline.AddStage(osbuild.NewXorrisofsStage(xorrisofsStageOptions(p.Filename, p.treePipeline.isoLabel, p.ISOLinux), p.treePipeline.Name()))
 	pipeline.AddStage(osbuild.NewImplantisomd5Stage(&osbuild.Implantisomd5StageOptions{Filename: p.Filename}))
 
 	return pipeline
diff --git a/internal/manifest/iso_rootfs.go b/internal/manifest/iso_rootfs.go
new file mode 100644
index 000000000..f33ca40ee
--- /dev/null
+++ b/internal/manifest/iso_rootfs.go
@@ -0,0 +1,71 @@
+package manifest
+
+import (
+	"fmt"
+
+	"github.com/osbuild/osbuild-composer/internal/osbuild"
+)
+
+type ISORootfsImg struct {
+	Base
+
+	Size uint64
+
+	anacondaPipeline *Anaconda
+}
+
+func NewISORootfsImg(m *Manifest, buildPipeline *Build, anacondaPipeline *Anaconda) *ISORootfsImg {
+	p := &ISORootfsImg{
+		Base:             NewBase(m, "rootfs-image", buildPipeline),
+		anacondaPipeline: anacondaPipeline,
+	}
+	buildPipeline.addDependent(p)
+	m.addPipeline(p)
+	return p
+}
+
+func (p *ISORootfsImg) serialize() osbuild.Pipeline {
+	pipeline := p.Base.serialize()
+
+	pipeline.AddStage(osbuild.NewMkdirStage(&osbuild.MkdirStageOptions{
+		Paths: []osbuild.Path{
+			{
+				Path: "LiveOS",
+			},
+		},
+	}))
+	pipeline.AddStage(osbuild.NewTruncateStage(&osbuild.TruncateStageOptions{
+		Filename: "LiveOS/rootfs.img",
+		Size:     fmt.Sprintf("%d", p.Size),
+	}))
+
+	mkfsStageOptions := &osbuild.MkfsExt4StageOptions{
+		UUID:  "2fe99653-f7ff-44fd-bea8-fa70107524fb",
+		Label: "Anaconda",
+	}
+	lodevice := osbuild.NewLoopbackDevice(
+		&osbuild.LoopbackDeviceOptions{
+			Filename: "LiveOS/rootfs.img",
+		},
+	)
+
+	devName := "device"
+	devices := osbuild.Devices{devName: *lodevice}
+	mkfsStage := osbuild.NewMkfsExt4Stage(mkfsStageOptions, devices)
+	pipeline.AddStage(mkfsStage)
+
+	inputName := "tree"
+	copyStageOptions := &osbuild.CopyStageOptions{
+		Paths: []osbuild.CopyStagePath{
+			{
+				From: fmt.Sprintf("input://%s/", inputName),
+				To:   fmt.Sprintf("mount://%s/", devName),
+			},
+		},
+	}
+	copyStageInputs := osbuild.NewPipelineTreeInputs(inputName, p.anacondaPipeline.Name())
+	copyStageMounts := &osbuild.Mounts{*osbuild.NewExt4Mount(devName, devName, "/")}
+	copyStage := osbuild.NewCopyStage(copyStageOptions, copyStageInputs, &devices, copyStageMounts)
+	pipeline.AddStage(copyStage)
+	return pipeline
+}
diff --git a/internal/manifest/iso_tree.go b/internal/manifest/iso_tree.go
index a0c75ff02..19b4be769 100644
--- a/internal/manifest/iso_tree.go
+++ b/internal/manifest/iso_tree.go
@@ -4,9 +4,10 @@ import (
 	"fmt"
 	"path"
 
-	"github.com/osbuild/osbuild-composer/internal/blueprint"
-	"github.com/osbuild/osbuild-composer/internal/distro"
+	"github.com/osbuild/osbuild-composer/internal/disk"
 	"github.com/osbuild/osbuild-composer/internal/osbuild"
+	"github.com/osbuild/osbuild-composer/internal/ostree"
+	"github.com/osbuild/osbuild-composer/internal/users"
 )
 
 // An ISOTree represents a tree containing the anaconda installer,
@@ -14,37 +15,46 @@ import (
 // payload to be installed.
 type ISOTree struct {
 	Base
+
 	// TODO: review optional and mandatory fields and their meaning
-	UEFIVendor string
-	OSName     string
-	Release    string
-	Users      []blueprint.UserCustomization
-	Groups     []blueprint.GroupCustomization
+	OSName  string
+	Release string
+	Users   []users.User
+	Groups  []users.Group
+
+	PartitionTable *disk.PartitionTable
 
 	anacondaPipeline *Anaconda
-	isoLabel         string
-	osTreeCommit     string
-	osTreeURL        string
-	osTreeRef        string
+	rootfsPipeline   *ISORootfsImg
+	bootTreePipeline *EFIBootTree
+
+	KSPath   string
+	isoLabel string
+
+	ostree ostree.RequestParams
 }
 
 func NewISOTree(m *Manifest,
 	buildPipeline *Build,
 	anacondaPipeline *Anaconda,
-	osTreeCommit,
-	osTreeURL,
-	osTreeRef,
-	isoLabelTmpl string) *ISOTree {
-	// TODO: replace isoLabelTmpl with more high-level properties
-	isoLabel := fmt.Sprintf(isoLabelTmpl, anacondaPipeline.platform.GetArch())
+	rootfsPipeline *ISORootfsImg,
+	bootTreePipeline *EFIBootTree,
+	ostreeCommit,
+	ostreeURL,
+	ostreeRef,
+	isoLabel string) *ISOTree {
 
 	p := &ISOTree{
 		Base:             NewBase(m, "bootiso-tree", buildPipeline),
 		anacondaPipeline: anacondaPipeline,
+		rootfsPipeline:   rootfsPipeline,
+		bootTreePipeline: bootTreePipeline,
 		isoLabel:         isoLabel,
-		osTreeCommit:     osTreeCommit,
-		osTreeURL:        osTreeURL,
-		osTreeRef:        osTreeRef,
+		ostree: ostree.RequestParams{
+			Parent: ostreeCommit,
+			URL:    ostreeURL,
+			Ref:    ostreeRef,
+		},
 	}
 	buildPipeline.addDependent(p)
 	if anacondaPipeline.Base.manifest != m {
@@ -57,8 +67,8 @@ func NewISOTree(m *Manifest,
 func (p *ISOTree) getOSTreeCommits() []osTreeCommit {
 	return []osTreeCommit{
 		{
-			checksum: p.osTreeCommit,
-			url:      p.osTreeURL,
+			checksum: p.ostree.Parent,
+			url:      p.ostree.URL,
 		},
 	}
 }
@@ -74,80 +84,108 @@ func (p *ISOTree) getBuildPackages() []string {
 func (p *ISOTree) serialize() osbuild.Pipeline {
 	pipeline := p.Base.serialize()
 
-	kspath := "/osbuild.ks"
 	ostreeRepoPath := "/ostree/repo"
+	kernelOpts := fmt.Sprintf("inst.ks=hd:LABEL=%s:%s", p.isoLabel, p.KSPath)
 
-	pipeline.AddStage(osbuild.NewBootISOMonoStage(bootISOMonoStageOptions(p.anacondaPipeline.kernelVer,
-		p.anacondaPipeline.platform.GetArch().String(),
-		p.UEFIVendor,
-		p.anacondaPipeline.product,
-		p.anacondaPipeline.version,
-		p.isoLabel,
-		kspath),
-		osbuild.NewBootISOMonoStagePipelineTreeInputs(p.anacondaPipeline.Name())))
+	pipeline.AddStage(osbuild.NewMkdirStage(&osbuild.MkdirStageOptions{
+		Paths: []osbuild.Path{
+			{
+				Path: "images",
+			},
+			{
+				Path: "images/pxeboot",
+			},
+		},
+	}))
 
-	kickstartOptions, err := osbuild.NewKickstartStageOptions(kspath, "", p.Users, p.Groups, makeISORootPath(ostreeRepoPath), p.osTreeRef, p.OSName)
+	inputName := "tree"
+	copyStageOptions := &osbuild.CopyStageOptions{
+		Paths: []osbuild.CopyStagePath{
+			{
+				From: fmt.Sprintf("input://%s/boot/vmlinuz-%s", inputName, p.anacondaPipeline.kernelVer),
+				To:   "tree:///images/pxeboot/vmlinuz",
+			},
+			{
+				From: fmt.Sprintf("input://%s/boot/initramfs-%s.img", inputName, p.anacondaPipeline.kernelVer),
+				To:   "tree:///images/pxeboot/initrd.img",
+			},
+		},
+	}
+	copyStageInputs := osbuild.NewPipelineTreeInputs(inputName, p.anacondaPipeline.Name())
+	copyStage := osbuild.NewCopyStageSimple(copyStageOptions, copyStageInputs)
+	pipeline.AddStage(copyStage)
+
+	squashfsOptions := osbuild.SquashfsStageOptions{
+		Filename: "images/install.img",
+		Compression: osbuild.FSCompression{
+			Method: "lz4",
+		},
+	}
+	squashfsStage := osbuild.NewSquashfsStage(&squashfsOptions, p.rootfsPipeline.Name())
+	pipeline.AddStage(squashfsStage)
+
+	isoLinuxOptions := &osbuild.ISOLinuxStageOptions{
+		Product: osbuild.ISOLinuxProduct{
+			Name:    p.anacondaPipeline.product,
+			Version: p.anacondaPipeline.version,
+		},
+		Kernel: osbuild.ISOLinuxKernel{
+			Dir:  "/images/pxeboot",
+			Opts: []string{kernelOpts},
+		},
+	}
+	isoLinuxStage := osbuild.NewISOLinuxStage(isoLinuxOptions, p.anacondaPipeline.Name())
+	pipeline.AddStage(isoLinuxStage)
+
+	filename := "images/efiboot.img"
+	pipeline.AddStage(osbuild.NewTruncateStage(&osbuild.TruncateStageOptions{
+		Filename: filename,
+		Size:     "20MB",
+	}))
+
+	efibootDevice := osbuild.NewLoopbackDevice(&osbuild.LoopbackDeviceOptions{Filename: filename})
+	for _, stage := range osbuild.GenMkfsStages(p.PartitionTable, efibootDevice) {
+		pipeline.AddStage(stage)
+	}
+
+	inputName = "root-tree"
+	copyInputs := osbuild.NewPipelineTreeInputs(inputName, p.bootTreePipeline.Name())
+	copyOptions, copyDevices, copyMounts := osbuild.GenCopyFSTreeOptions(inputName, p.bootTreePipeline.Name(), filename, p.PartitionTable)
+	pipeline.AddStage(osbuild.NewCopyStage(copyOptions, copyInputs, copyDevices, copyMounts))
+
+	copyInputs = osbuild.NewPipelineTreeInputs(inputName, p.bootTreePipeline.Name())
+	pipeline.AddStage(osbuild.NewCopyStageSimple(
+		&osbuild.CopyStageOptions{
+			Paths: []osbuild.CopyStagePath{
+				{
+					From: fmt.Sprintf("input://%s/EFI", inputName),
+					To:   "tree:///",
+				},
+			},
+		},
+		copyInputs,
+	))
+
+	kickstartOptions, err := osbuild.NewKickstartStageOptions(p.KSPath, "", p.Users, p.Groups, makeISORootPath(ostreeRepoPath), p.ostree.Ref, p.OSName)
 	if err != nil {
 		panic("password encryption failed")
 	}
 
+	pipeline.AddStage(osbuild.NewOSTreeInitStage(&osbuild.OSTreeInitStageOptions{Path: ostreeRepoPath}))
+	pipeline.AddStage(osbuild.NewOSTreePullStage(
+		&osbuild.OSTreePullStageOptions{Repo: ostreeRepoPath},
+		osbuild.NewOstreePullStageInputs("org.osbuild.source", p.ostree.Parent, p.ostree.Ref),
+	))
+
 	pipeline.AddStage(osbuild.NewKickstartStage(kickstartOptions))
 	pipeline.AddStage(osbuild.NewDiscinfoStage(&osbuild.DiscinfoStageOptions{
 		BaseArch: p.anacondaPipeline.platform.GetArch().String(),
 		Release:  p.Release,
 	}))
 
-	pipeline.AddStage(osbuild.NewOSTreeInitStage(&osbuild.OSTreeInitStageOptions{Path: ostreeRepoPath}))
-	pipeline.AddStage(osbuild.NewOSTreePullStage(
-		&osbuild.OSTreePullStageOptions{Repo: ostreeRepoPath},
-		osbuild.NewOstreePullStageInputs("org.osbuild.source", p.osTreeCommit, p.osTreeRef),
-	))
-
 	return pipeline
 }
 
-func bootISOMonoStageOptions(kernelVer, arch, vendor, product, osVersion, isolabel, kspath string) *osbuild.BootISOMonoStageOptions {
-	comprOptions := new(osbuild.FSCompressionOptions)
-	if bcj := osbuild.BCJOption(arch); bcj != "" {
-		comprOptions.BCJ = bcj
-	}
-	var architectures []string
-
-	if arch == distro.X86_64ArchName {
-		architectures = []string{"X64"}
-	} else if arch == distro.Aarch64ArchName {
-		architectures = []string{"AA64"}
-	} else {
-		panic("unsupported architecture")
-	}
-
-	return &osbuild.BootISOMonoStageOptions{
-		Product: osbuild.Product{
-			Name:    product,
-			Version: osVersion,
-		},
-		ISOLabel:   isolabel,
-		Kernel:     kernelVer,
-		KernelOpts: fmt.Sprintf("inst.ks=hd:LABEL=%s:%s", isolabel, kspath),
-		EFI: osbuild.EFI{
-			Architectures: architectures,
-			Vendor:        vendor,
-		},
-		ISOLinux: osbuild.ISOLinux{
-			Enabled: arch == distro.X86_64ArchName,
-			Debug:   false,
-		},
-		Templates: "99-generic",
-		RootFS: osbuild.RootFS{
-			Size: 9216,
-			Compression: osbuild.FSCompression{
-				Method:  "xz",
-				Options: comprOptions,
-			},
-		},
-	}
-}
-
 //makeISORootPath return a path that can be used to address files and folders in
 //the root of the iso
 func makeISORootPath(p string) string {
diff --git a/internal/manifest/oci_container.go b/internal/manifest/oci_container.go
index 9807f9611..520fb7445 100644
--- a/internal/manifest/oci_container.go
+++ b/internal/manifest/oci_container.go
@@ -43,10 +43,7 @@ func (p *OCIContainer) serialize() osbuild.Pipeline {
 			ExposedPorts: p.ExposedPorts,
 		},
 	}
-	baseInput := new(osbuild.OCIArchiveStageInput)
-	baseInput.Type = "org.osbuild.tree"
-	baseInput.Origin = "org.osbuild.pipeline"
-	baseInput.References = []string{"name:" + p.treePipeline.Name()}
+	baseInput := osbuild.NewTreeInput("name:" + p.treePipeline.Name())
 	inputs := &osbuild.OCIArchiveStageInputs{Base: baseInput}
 	pipeline.AddStage(osbuild.NewOCIArchiveStage(options, inputs))
 
diff --git a/internal/manifest/raw.go b/internal/manifest/raw.go
index f78f7386f..a7b0ad6f3 100644
--- a/internal/manifest/raw.go
+++ b/internal/manifest/raw.go
@@ -48,7 +48,7 @@ func (p *RawImage) serialize() osbuild.Pipeline {
 
 	inputName := "root-tree"
 	copyOptions, copyDevices, copyMounts := osbuild.GenCopyFSTreeOptions(inputName, p.treePipeline.Name(), p.Filename, pt)
-	copyInputs := osbuild.NewCopyStagePipelineTreeInputs(inputName, p.treePipeline.Name())
+	copyInputs := osbuild.NewPipelineTreeInputs(inputName, p.treePipeline.Name())
 	pipeline.AddStage(osbuild.NewCopyStage(copyOptions, copyInputs, copyDevices, copyMounts))
 
 	for _, stage := range osbuild.GenImageFinishStages(pt, p.Filename) {
diff --git a/internal/manifest/raw_ostree.go b/internal/manifest/raw_ostree.go
index f0ab5e398..a64692634 100644
--- a/internal/manifest/raw_ostree.go
+++ b/internal/manifest/raw_ostree.go
@@ -61,7 +61,7 @@ func (p *RawOSTreeImage) serialize() osbuild.Pipeline {
 
 	inputName := "root-tree"
 	copyOptions, copyDevices, copyMounts := osbuild.GenCopyFSTreeOptions(inputName, p.treePipeline.Name(), p.Filename, pt)
-	copyInputs := osbuild.NewCopyStagePipelineTreeInputs(inputName, p.treePipeline.Name())
+	copyInputs := osbuild.NewPipelineTreeInputs(inputName, p.treePipeline.Name())
 	pipeline.AddStage(osbuild.NewCopyStage(copyOptions, copyInputs, copyDevices, copyMounts))
 
 	for _, stage := range osbuild.GenImageFinishStages(pt, p.Filename) {
diff --git a/internal/manifest/tar.go b/internal/manifest/tar.go
index fb2d6d40e..b1fd8006d 100644
--- a/internal/manifest/tar.go
+++ b/internal/manifest/tar.go
@@ -36,12 +36,7 @@ func NewTar(m *Manifest,
 func (p *Tar) serialize() osbuild.Pipeline {
 	pipeline := p.Base.serialize()
 
-	tree := new(osbuild.TarStageInput)
-	tree.Type = "org.osbuild.tree"
-	tree.Origin = "org.osbuild.pipeline"
-	tree.References = []string{"name:" + p.inputPipeline.Name()}
-	tarStage := osbuild.NewTarStage(&osbuild.TarStageOptions{Filename: p.Filename}, &osbuild.TarStageInputs{Tree: tree})
-
+	tarStage := osbuild.NewTarStage(&osbuild.TarStageOptions{Filename: p.Filename}, p.inputPipeline.Name())
 	pipeline.AddStage(tarStage)
 
 	return pipeline
diff --git a/internal/osbuild/bootiso_stage.go b/internal/osbuild/bootiso_stage.go
index e156a9692..73ca861ac 100644
--- a/internal/osbuild/bootiso_stage.go
+++ b/internal/osbuild/bootiso_stage.go
@@ -62,23 +62,12 @@ func BCJOption(arch string) string {
 func (BootISOMonoStageOptions) isStageOptions() {}
 
 type BootISOMonoStageInputs struct {
-	RootFS *BootISOMonoStageInput `json:"rootfs"`
-	Kernel *BootISOMonoStageInput `json:"kernel,omitempty"`
+	RootFS *TreeInput `json:"rootfs"`
+	Kernel *TreeInput `json:"kernel,omitempty"`
 }
 
 func (BootISOMonoStageInputs) isStageInputs() {}
 
-type BootISOMonoStageInput struct {
-	inputCommon
-	References BootISOMonoStageReferences `json:"references"`
-}
-
-func (BootISOMonoStageInput) isStageInput() {}
-
-type BootISOMonoStageReferences []string
-
-func (BootISOMonoStageReferences) isReferences() {}
-
 // Assemble a file system tree for a bootable ISO
 func NewBootISOMonoStage(options *BootISOMonoStageOptions, inputs *BootISOMonoStageInputs) *Stage {
 	return &Stage{
@@ -89,10 +78,7 @@ func NewBootISOMonoStage(options *BootISOMonoStageOptions, inputs *BootISOMonoSt
 }
 
 func NewBootISOMonoStagePipelineTreeInputs(pipeline string) *BootISOMonoStageInputs {
-	rootfsInput := new(BootISOMonoStageInput)
-	rootfsInput.Type = "org.osbuild.tree"
-	rootfsInput.Origin = "org.osbuild.pipeline"
-	rootfsInput.References = BootISOMonoStageReferences{"name:" + pipeline}
+	rootfsInput := NewTreeInput("name:" + pipeline)
 	return &BootISOMonoStageInputs{
 		RootFS: rootfsInput,
 	}
diff --git a/internal/osbuild/copy_stage.go b/internal/osbuild/copy_stage.go
index 3e25c0b4d..2539d0bde 100644
--- a/internal/osbuild/copy_stage.go
+++ b/internal/osbuild/copy_stage.go
@@ -21,59 +21,24 @@ type CopyStagePath struct {
 
 func (CopyStageOptions) isStageOptions() {}
 
-type CopyStageInputs map[string]CopyStageInput
-
-type CopyStageInput struct {
-	inputCommon
-	References CopyStageReferences `json:"references"`
-}
-
-func (CopyStageInputs) isStageInputs() {}
-
-type CopyStageReferences []string
-
-type CopyStageInputsNew interface {
-	isCopyStageInputs()
-}
-
-func (CopyStageInputs) isCopyStageInputs() {}
-
-func (CopyStageReferences) isReferences() {}
-
-func NewCopyStage(options *CopyStageOptions, inputs CopyStageInputsNew, devices *Devices, mounts *Mounts) *Stage {
-	var stageInputs Inputs
-	if inputs != nil {
-		stageInputs = inputs.(Inputs)
-	}
+func NewCopyStage(options *CopyStageOptions, inputs Inputs, devices *Devices, mounts *Mounts) *Stage {
 	return &Stage{
 		Type:    "org.osbuild.copy",
 		Options: options,
-		Inputs:  stageInputs,
+		Inputs:  inputs,
 		Devices: *devices,
 		Mounts:  *mounts,
 	}
 }
 
-func NewCopyStageSimple(options *CopyStageOptions, inputs CopyStageInputsNew) *Stage {
-	var stageInputs Inputs
-	if inputs != nil {
-		stageInputs = inputs.(Inputs)
-	}
+func NewCopyStageSimple(options *CopyStageOptions, inputs Inputs) *Stage {
 	return &Stage{
 		Type:    "org.osbuild.copy",
 		Options: options,
-		Inputs:  stageInputs,
+		Inputs:  inputs,
 	}
 }
 
-func NewCopyStagePipelineTreeInputs(inputName, inputPipeline string) *CopyStageInputs {
-	treeInput := CopyStageInput{}
-	treeInput.Type = "org.osbuild.tree"
-	treeInput.Origin = "org.osbuild.pipeline"
-	treeInput.References = []string{"name:" + inputPipeline}
-	return &CopyStageInputs{inputName: treeInput}
-}
-
 // GenCopyFSTreeOptions creates the options, inputs, devices, and mounts properties
 // for an org.osbuild.copy stage for a given source tree using a partition
 // table description to define the mounts
diff --git a/internal/osbuild/files_input.go b/internal/osbuild/files_input.go
index eb497af6b..360b76b65 100644
--- a/internal/osbuild/files_input.go
+++ b/internal/osbuild/files_input.go
@@ -24,9 +24,6 @@ func NewFilesInputs(references FilesInputReferences) *FilesInputs {
 // inputs accepted by the XZ stage
 func (FilesInputs) isXzStageInputs() {}
 
-// inputs accepted by the Copy stage
-func (FilesInputs) isCopyStageInputs() {}
-
 // SPECIFIC INPUT STRUCTURE
 
 type FilesInput struct {
diff --git a/internal/osbuild/groups_stage.go b/internal/osbuild/groups_stage.go
index 46f5fff5d..4b12ae7e3 100644
--- a/internal/osbuild/groups_stage.go
+++ b/internal/osbuild/groups_stage.go
@@ -1,7 +1,6 @@
 package osbuild
 
 import (
-	"github.com/osbuild/osbuild-composer/internal/blueprint"
 	"github.com/osbuild/osbuild-composer/internal/users"
 )
 
@@ -22,7 +21,7 @@ func NewGroupsStage(options *GroupsStageOptions) *Stage {
 	}
 }
 
-func NewGroupsStageOptions(groups []blueprint.GroupCustomization) *GroupsStageOptions {
+func NewGroupsStageOptions(groups []users.Group) *GroupsStageOptions {
 	options := GroupsStageOptions{
 		Groups: map[string]GroupsStageOptionsGroup{},
 	}
diff --git a/internal/osbuild/isolinux_stage.go b/internal/osbuild/isolinux_stage.go
new file mode 100644
index 000000000..a133beb52
--- /dev/null
+++ b/internal/osbuild/isolinux_stage.go
@@ -0,0 +1,27 @@
+package osbuild
+
+type ISOLinuxStageOptions struct {
+	Product ISOLinuxProduct `json:"product"`
+	Kernel  ISOLinuxKernel  `json:"kernel"`
+}
+
+func (ISOLinuxStageOptions) isStageOptions() {}
+
+type ISOLinuxProduct struct {
+	Name    string `json:"name"`
+	Version string `json:"version"`
+}
+
+type ISOLinuxKernel struct {
+	Dir string `json:"dir"`
+
+	Opts []string `json:"opts,omitempty"`
+}
+
+func NewISOLinuxStage(options *ISOLinuxStageOptions, inputPipeline string) *Stage {
+	return &Stage{
+		Type:    "org.osbuild.isolinux",
+		Options: options,
+		Inputs:  NewPipelineTreeInputs("data", inputPipeline),
+	}
+}
diff --git a/internal/osbuild/kickstart_stage.go b/internal/osbuild/kickstart_stage.go
index ab197ed92..e09bf77fa 100644
--- a/internal/osbuild/kickstart_stage.go
+++ b/internal/osbuild/kickstart_stage.go
@@ -1,8 +1,6 @@
 package osbuild
 
-import (
-	"github.com/osbuild/osbuild-composer/internal/blueprint"
-)
+import "github.com/osbuild/osbuild-composer/internal/users"
 
 type KickstartStageOptions struct {
 	// Where to place the kickstart file
@@ -41,8 +39,8 @@ func NewKickstartStage(options *KickstartStageOptions) *Stage {
 func NewKickstartStageOptions(
 	path string,
 	imageURL string,
-	userCustomizations []blueprint.UserCustomization,
-	groupCustomizations []blueprint.GroupCustomization,
+	userCustomizations []users.User,
+	groupCustomizations []users.Group,
 	ostreeURL string,
 	ostreeRef string,
 	osName string) (*KickstartStageOptions, error) {
diff --git a/internal/osbuild/oci_archive_stage.go b/internal/osbuild/oci_archive_stage.go
index 2b56db86d..53e4c0c6d 100644
--- a/internal/osbuild/oci_archive_stage.go
+++ b/internal/osbuild/oci_archive_stage.go
@@ -34,24 +34,13 @@ func (OCIArchiveStageOptions) isStageOptions() {}
 
 type OCIArchiveStageInputs struct {
 	// Base layer for the container
-	Base *OCIArchiveStageInput `json:"base"`
+	Base *TreeInput `json:"base"`
 	// Additional layers in ascending order
-	Layers []OCIArchiveStageInput `json:",omitempty"`
+	Layers []TreeInput `json:",omitempty"`
 }
 
 func (OCIArchiveStageInputs) isStageInputs() {}
 
-type OCIArchiveStageInput struct {
-	inputCommon
-	References OCIArchiveStageReferences `json:"references"`
-}
-
-func (OCIArchiveStageInput) isStageInput() {}
-
-type OCIArchiveStageReferences []string
-
-func (OCIArchiveStageReferences) isReferences() {}
-
 // A new OCIArchiveStage to to assemble an OCI image archive
 func NewOCIArchiveStage(options *OCIArchiveStageOptions, inputs *OCIArchiveStageInputs) *Stage {
 	return &Stage{
@@ -69,7 +58,7 @@ func (inputs *OCIArchiveStageInputs) MarshalJSON() ([]byte, error) {
 	}
 
 	layers := inputs.Layers
-	inputsMap := make(map[string]OCIArchiveStageInput, len(layers)+1)
+	inputsMap := make(map[string]TreeInput, len(layers)+1)
 	if inputs.Base != nil {
 		inputsMap["base"] = *inputs.Base
 	}
@@ -83,7 +72,7 @@ func (inputs *OCIArchiveStageInputs) MarshalJSON() ([]byte, error) {
 }
 
 // Get the sorted keys that match the pattern "layer.N" (for N > 0)
-func layerKeys(layers map[string]OCIArchiveStageInput) ([]string, error) {
+func layerKeys(layers map[string]TreeInput) ([]string, error) {
 	keys := make([]string, 0, len(layers))
 	for key := range layers {
 		re := regexp.MustCompile(`layer\.[1-9]\d*`)
@@ -110,7 +99,7 @@ func (inputs *OCIArchiveStageInputs) UnmarshalJSON(data []byte) error {
 		inputs = new(OCIArchiveStageInputs)
 	}
 
-	inputsMap := make(map[string]OCIArchiveStageInput)
+	inputsMap := make(map[string]TreeInput)
 
 	dec := json.NewDecoder(bytes.NewReader(data))
 	dec.DisallowUnknownFields()
@@ -130,7 +119,7 @@ func (inputs *OCIArchiveStageInputs) UnmarshalJSON(data []byte) error {
 	if err != nil {
 		return err
 	}
-	inputs.Layers = make([]OCIArchiveStageInput, len(inputsMap)-1)
+	inputs.Layers = make([]TreeInput, len(inputsMap)-1)
 	for idx, key := range keys {
 		inputs.Layers[idx] = inputsMap[key]
 	}
diff --git a/internal/osbuild/ostree_commit_stage.go b/internal/osbuild/ostree_commit_stage.go
index 05e22d077..fbd48db63 100644
--- a/internal/osbuild/ostree_commit_stage.go
+++ b/internal/osbuild/ostree_commit_stage.go
@@ -13,30 +13,13 @@ type OSTreeCommitStageOptions struct {
 
 func (OSTreeCommitStageOptions) isStageOptions() {}
 
-type OSTreeCommitStageInput struct {
-	inputCommon
-	References OSTreeCommitStageReferences `json:"references"`
-}
-
-func (OSTreeCommitStageInput) isStageInput() {}
-
-type OSTreeCommitStageInputs struct {
-	Tree *OSTreeCommitStageInput `json:"tree"`
-}
-
-func (OSTreeCommitStageInputs) isStageInputs() {}
-
-type OSTreeCommitStageReferences []string
-
-func (OSTreeCommitStageReferences) isReferences() {}
-
 // The OSTreeCommitStage (org.osbuild.ostree.commit) describes how to assemble
 // a tree into an OSTree commit.
-func NewOSTreeCommitStage(options *OSTreeCommitStageOptions, inputs *OSTreeCommitStageInputs) *Stage {
+func NewOSTreeCommitStage(options *OSTreeCommitStageOptions, inputPipeline string) *Stage {
 	return &Stage{
 		Type:    "org.osbuild.ostree.commit",
 		Options: options,
-		Inputs:  inputs,
+		Inputs:  NewPipelineTreeInputs("tree", inputPipeline),
 	}
 }
 
diff --git a/internal/osbuild/squashfs_stage.go b/internal/osbuild/squashfs_stage.go
new file mode 100644
index 000000000..8764f74bd
--- /dev/null
+++ b/internal/osbuild/squashfs_stage.go
@@ -0,0 +1,17 @@
+package osbuild
+
+type SquashfsStageOptions struct {
+	Filename string `json:"filename"`
+
+	Compression FSCompression `json:"compression"`
+}
+
+func (SquashfsStageOptions) isStageOptions() {}
+
+func NewSquashfsStage(options *SquashfsStageOptions, inputPipeline string) *Stage {
+	return &Stage{
+		Type:    "org.osbuild.squashfs",
+		Options: options,
+		Inputs:  NewPipelineTreeInputs("tree", inputPipeline),
+	}
+}
diff --git a/internal/osbuild/tar_stage.go b/internal/osbuild/tar_stage.go
index 8676b84f9..f156d5172 100644
--- a/internal/osbuild/tar_stage.go
+++ b/internal/osbuild/tar_stage.go
@@ -84,26 +84,9 @@ func (o TarStageOptions) validate() error {
 	return nil
 }
 
-type TarStageInput struct {
-	inputCommon
-	References TarStageReferences `json:"references"`
-}
-
-func (TarStageInput) isStageInput() {}
-
-type TarStageInputs struct {
-	Tree *TarStageInput `json:"tree"`
-}
-
-func (TarStageInputs) isStageInputs() {}
-
-type TarStageReferences []string
-
-func (TarStageReferences) isReferences() {}
-
 // Assembles a tree into a tar archive. Compression is determined by the suffix
 // (i.e., --auto-compress is used).
-func NewTarStage(options *TarStageOptions, inputs *TarStageInputs) *Stage {
+func NewTarStage(options *TarStageOptions, inputPipeline string) *Stage {
 	if err := options.validate(); err != nil {
 		panic(err)
 	}
@@ -111,16 +94,6 @@ func NewTarStage(options *TarStageOptions, inputs *TarStageInputs) *Stage {
 	return &Stage{
 		Type:    "org.osbuild.tar",
 		Options: options,
-		Inputs:  inputs,
-	}
-}
-
-func NewTarStagePipelineTreeInputs(pipeline string) *TarStageInputs {
-	tree := new(TarStageInput)
-	tree.Type = "org.osbuild.tree"
-	tree.Origin = "org.osbuild.pipeline"
-	tree.References = []string{"name:" + pipeline}
-	return &TarStageInputs{
-		Tree: tree,
+		Inputs:  NewPipelineTreeInputs("tree", inputPipeline),
 	}
 }
diff --git a/internal/osbuild/tree_input.go b/internal/osbuild/tree_input.go
index e9874d722..230b72611 100644
--- a/internal/osbuild/tree_input.go
+++ b/internal/osbuild/tree_input.go
@@ -3,13 +3,28 @@ package osbuild
 // Tree inputs
 type TreeInput struct {
 	inputCommon
+	References []string `json:"references"`
 }
 
 func (TreeInput) isInput() {}
 
-func NewTreeInput() *TreeInput {
+// NewTreeInput creates an org.osbuild.tree input for an osbuild stage.
+// The input is the final tree from a pipeline that should be referenced as
+// 'name:<pipelinename>' in the reference argument.
+func NewTreeInput(reference string) *TreeInput {
 	input := new(TreeInput)
 	input.Type = "org.osbuild.tree"
 	input.Origin = "org.osbuild.pipeline"
+	input.References = []string{reference}
 	return input
 }
+
+type PipelineTreeInputs map[string]TreeInput
+
+func NewPipelineTreeInputs(name, pipeline string) *PipelineTreeInputs {
+	return &PipelineTreeInputs{
+		name: *NewTreeInput("name:" + pipeline),
+	}
+}
+
+func (PipelineTreeInputs) isStageInputs() {}
diff --git a/internal/osbuild/users_stage.go b/internal/osbuild/users_stage.go
index 5d4d40148..70e2ff436 100644
--- a/internal/osbuild/users_stage.go
+++ b/internal/osbuild/users_stage.go
@@ -1,7 +1,6 @@
 package osbuild
 
 import (
-	"github.com/osbuild/osbuild-composer/internal/blueprint"
 	"github.com/osbuild/osbuild-composer/internal/crypt"
 	"github.com/osbuild/osbuild-composer/internal/users"
 )
@@ -30,7 +29,7 @@ func NewUsersStage(options *UsersStageOptions) *Stage {
 	}
 }
 
-func NewUsersStageOptions(userCustomizations []blueprint.UserCustomization, omitKey bool) (*UsersStageOptions, error) {
+func NewUsersStageOptions(userCustomizations []users.User, omitKey bool) (*UsersStageOptions, error) {
 	if len(userCustomizations) == 0 {
 		return nil, nil
 	}
diff --git a/internal/osbuild/xorrisofs_stage.go b/internal/osbuild/xorrisofs_stage.go
index f57ffdec7..3088c0a29 100644
--- a/internal/osbuild/xorrisofs_stage.go
+++ b/internal/osbuild/xorrisofs_stage.go
@@ -30,36 +30,11 @@ type XorrisofsBoot struct {
 
 func (XorrisofsStageOptions) isStageOptions() {}
 
-type XorrisofsStageInputs struct {
-	Tree *XorrisofsStageInput `json:"tree"`
-}
-
-func (XorrisofsStageInputs) isStageInputs() {}
-
-type XorrisofsStageInput struct {
-	inputCommon
-	References XorrisofsStageReferences `json:"references"`
-}
-
-func (XorrisofsStageInput) isStageInput() {}
-
-type XorrisofsStageReferences []string
-
-func (XorrisofsStageReferences) isReferences() {}
-
 // Assembles a Rock Ridge enhanced ISO 9660 filesystem (iso)
-func NewXorrisofsStage(options *XorrisofsStageOptions, inputs *XorrisofsStageInputs) *Stage {
+func NewXorrisofsStage(options *XorrisofsStageOptions, inputPipeline string) *Stage {
 	return &Stage{
 		Type:    "org.osbuild.xorrisofs",
 		Options: options,
-		Inputs:  inputs,
+		Inputs:  NewPipelineTreeInputs("tree", inputPipeline),
 	}
 }
-
-func NewXorrisofsStagePipelineTreeInputs(pipeline string) *XorrisofsStageInputs {
-	input := new(XorrisofsStageInput)
-	input.Type = "org.osbuild.tree"
-	input.Origin = "org.osbuild.pipeline"
-	input.References = XorrisofsStageReferences{"name:" + pipeline}
-	return &XorrisofsStageInputs{Tree: input}
-}
diff --git a/internal/store/store.go b/internal/store/store.go
index 5634ab5f5..400ddfd25 100644
--- a/internal/store/store.go
+++ b/internal/store/store.go
@@ -31,6 +31,14 @@ const StoreDBName = "state"
 
 // A Store contains all the persistent state of osbuild-composer, and is serialized
 // on every change, and deserialized on start.
+//
+// blueprints contain the most recent blueprint, using the name as the key
+//
+// blueprintsCommits contains the order of the commits to a blueprint name as a string of hashes
+// with the most recent one last.
+//
+// blueprintsChanges contains the blueprint change, using the blueprint name string and
+// the hash string from blueprintsCommits
 type Store struct {
 	blueprints        map[string]blueprint.Blueprint
 	workspace         map[string]blueprint.Blueprint
@@ -294,30 +302,37 @@ func (s *Store) TagBlueprint(name string) error {
 			return errors.New("Unknown blueprint")
 		}
 
-		if len(s.blueprintsCommits[name]) == 0 {
+		_, ok = s.blueprintsCommits[name]
+		if !ok || len(s.blueprintsCommits[name]) == 0 {
 			return errors.New("No commits for blueprint")
 		}
 
+		_, ok = s.blueprintsChanges[name]
+		if !ok {
+			return errors.New("No changes for blueprint")
+		}
+
 		latest := s.blueprintsCommits[name][len(s.blueprintsCommits[name])-1]
 		// If the most recent commit already has a revision, don't bump it
 		if s.blueprintsChanges[name][latest].Revision != nil {
 			return nil
 		}
 
-		// Get the latest revision for this blueprint
+		// Get the latest revision for this blueprint (or 0 if there is none)
+		// blueprintsCommits has the most recent commit at the end, so start there
 		var revision int
-		var change blueprint.Change
 		for i := len(s.blueprintsCommits[name]) - 1; i >= 0; i-- {
 			commit := s.blueprintsCommits[name][i]
-			change = s.blueprintsChanges[name][commit]
+			change := s.blueprintsChanges[name][commit]
 			if change.Revision != nil && *change.Revision > revision {
 				revision = *change.Revision
 				break
 			}
 		}
 
-		// Bump the revision (if there was none it will start at 1)
+		// Bump the revision (if there was none it will start at 1) of the latest commit
 		revision++
+		change := s.blueprintsChanges[name][latest]
 		change.Revision = &revision
 		s.blueprintsChanges[name][latest] = change
 		return nil
diff --git a/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go b/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go
index f420acd07..cddbe1bb0 100644
--- a/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go
+++ b/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go
@@ -2551,6 +2551,27 @@ var awsPartition = partition{
 				}: endpoint{
 					Hostname: "appmesh.ca-central-1.api.aws",
 				},
+				endpointKey{
+					Region:  "ca-central-1",
+					Variant: fipsVariant,
+				}: endpoint{
+					Hostname: "appmesh-fips.ca-central-1.amazonaws.com",
+				},
+				endpointKey{
+					Region:  "ca-central-1",
+					Variant: fipsVariant | dualStackVariant,
+				}: endpoint{
+					Hostname: "appmesh-fips.ca-central-1.api.aws",
+				},
+				endpointKey{
+					Region: "ca-central-1-fips",
+				}: endpoint{
+					Hostname: "appmesh-fips.ca-central-1.amazonaws.com",
+					CredentialScope: credentialScope{
+						Region: "ca-central-1",
+					},
+					Deprecated: boxedTrue,
+				},
 				endpointKey{
 					Region: "eu-central-1",
 				}: endpoint{},
@@ -2632,6 +2653,27 @@ var awsPartition = partition{
 				}: endpoint{
 					Hostname: "appmesh.us-east-1.api.aws",
 				},
+				endpointKey{
+					Region:  "us-east-1",
+					Variant: fipsVariant,
+				}: endpoint{
+					Hostname: "appmesh-fips.us-east-1.amazonaws.com",
+				},
+				endpointKey{
+					Region:  "us-east-1",
+					Variant: fipsVariant | dualStackVariant,
+				}: endpoint{
+					Hostname: "appmesh-fips.us-east-1.api.aws",
+				},
+				endpointKey{
+					Region: "us-east-1-fips",
+				}: endpoint{
+					Hostname: "appmesh-fips.us-east-1.amazonaws.com",
+					CredentialScope: credentialScope{
+						Region: "us-east-1",
+					},
+					Deprecated: boxedTrue,
+				},
 				endpointKey{
 					Region: "us-east-2",
 				}: endpoint{},
@@ -2641,6 +2683,27 @@ var awsPartition = partition{
 				}: endpoint{
 					Hostname: "appmesh.us-east-2.api.aws",
 				},
+				endpointKey{
+					Region:  "us-east-2",
+					Variant: fipsVariant,
+				}: endpoint{
+					Hostname: "appmesh-fips.us-east-2.amazonaws.com",
+				},
+				endpointKey{
+					Region:  "us-east-2",
+					Variant: fipsVariant | dualStackVariant,
+				}: endpoint{
+					Hostname: "appmesh-fips.us-east-2.api.aws",
+				},
+				endpointKey{
+					Region: "us-east-2-fips",
+				}: endpoint{
+					Hostname: "appmesh-fips.us-east-2.amazonaws.com",
+					CredentialScope: credentialScope{
+						Region: "us-east-2",
+					},
+					Deprecated: boxedTrue,
+				},
 				endpointKey{
 					Region: "us-west-1",
 				}: endpoint{},
@@ -2650,6 +2713,27 @@ var awsPartition = partition{
 				}: endpoint{
 					Hostname: "appmesh.us-west-1.api.aws",
 				},
+				endpointKey{
+					Region:  "us-west-1",
+					Variant: fipsVariant,
+				}: endpoint{
+					Hostname: "appmesh-fips.us-west-1.amazonaws.com",
+				},
+				endpointKey{
+					Region:  "us-west-1",
+					Variant: fipsVariant | dualStackVariant,
+				}: endpoint{
+					Hostname: "appmesh-fips.us-west-1.api.aws",
+				},
+				endpointKey{
+					Region: "us-west-1-fips",
+				}: endpoint{
+					Hostname: "appmesh-fips.us-west-1.amazonaws.com",
+					CredentialScope: credentialScope{
+						Region: "us-west-1",
+					},
+					Deprecated: boxedTrue,
+				},
 				endpointKey{
 					Region: "us-west-2",
 				}: endpoint{},
@@ -2659,6 +2743,27 @@ var awsPartition = partition{
 				}: endpoint{
 					Hostname: "appmesh.us-west-2.api.aws",
 				},
+				endpointKey{
+					Region:  "us-west-2",
+					Variant: fipsVariant,
+				}: endpoint{
+					Hostname: "appmesh-fips.us-west-2.amazonaws.com",
+				},
+				endpointKey{
+					Region:  "us-west-2",
+					Variant: fipsVariant | dualStackVariant,
+				}: endpoint{
+					Hostname: "appmesh-fips.us-west-2.api.aws",
+				},
+				endpointKey{
+					Region: "us-west-2-fips",
+				}: endpoint{
+					Hostname: "appmesh-fips.us-west-2.amazonaws.com",
+					CredentialScope: credentialScope{
+						Region: "us-west-2",
+					},
+					Deprecated: boxedTrue,
+				},
 			},
 		},
 		"apprunner": service{
@@ -9517,15 +9622,51 @@ var awsPartition = partition{
 				endpointKey{
 					Region: "ap-northeast-1",
 				}: endpoint{},
+				endpointKey{
+					Region: "ap-northeast-2",
+				}: endpoint{},
+				endpointKey{
+					Region: "ap-south-1",
+				}: endpoint{},
 				endpointKey{
 					Region: "ap-southeast-1",
 				}: endpoint{},
+				endpointKey{
+					Region: "ap-southeast-2",
+				}: endpoint{},
+				endpointKey{
+					Region: "ca-central-1",
+				}: endpoint{},
+				endpointKey{
+					Region:  "ca-central-1",
+					Variant: fipsVariant,
+				}: endpoint{
+					Hostname: "emr-serverless-fips.ca-central-1.amazonaws.com",
+				},
 				endpointKey{
 					Region: "eu-central-1",
 				}: endpoint{},
+				endpointKey{
+					Region: "eu-north-1",
+				}: endpoint{},
 				endpointKey{
 					Region: "eu-west-1",
 				}: endpoint{},
+				endpointKey{
+					Region: "eu-west-2",
+				}: endpoint{},
+				endpointKey{
+					Region: "eu-west-3",
+				}: endpoint{},
+				endpointKey{
+					Region: "fips-ca-central-1",
+				}: endpoint{
+					Hostname: "emr-serverless-fips.ca-central-1.amazonaws.com",
+					CredentialScope: credentialScope{
+						Region: "ca-central-1",
+					},
+					Deprecated: boxedTrue,
+				},
 				endpointKey{
 					Region: "fips-us-east-1",
 				}: endpoint{
@@ -9544,6 +9685,15 @@ var awsPartition = partition{
 					},
 					Deprecated: boxedTrue,
 				},
+				endpointKey{
+					Region: "fips-us-west-1",
+				}: endpoint{
+					Hostname: "emr-serverless-fips.us-west-1.amazonaws.com",
+					CredentialScope: credentialScope{
+						Region: "us-west-1",
+					},
+					Deprecated: boxedTrue,
+				},
 				endpointKey{
 					Region: "fips-us-west-2",
 				}: endpoint{
@@ -9553,6 +9703,9 @@ var awsPartition = partition{
 					},
 					Deprecated: boxedTrue,
 				},
+				endpointKey{
+					Region: "sa-east-1",
+				}: endpoint{},
 				endpointKey{
 					Region: "us-east-1",
 				}: endpoint{},
@@ -9571,6 +9724,15 @@ var awsPartition = partition{
 				}: endpoint{
 					Hostname: "emr-serverless-fips.us-east-2.amazonaws.com",
 				},
+				endpointKey{
+					Region: "us-west-1",
+				}: endpoint{},
+				endpointKey{
+					Region:  "us-west-1",
+					Variant: fipsVariant,
+				}: endpoint{
+					Hostname: "emr-serverless-fips.us-west-1.amazonaws.com",
+				},
 				endpointKey{
 					Region: "us-west-2",
 				}: endpoint{},
@@ -12379,6 +12541,16 @@ var awsPartition = partition{
 				},
 			},
 		},
+		"iotfleetwise": service{
+			Endpoints: serviceEndpoints{
+				endpointKey{
+					Region: "eu-central-1",
+				}: endpoint{},
+				endpointKey{
+					Region: "us-east-1",
+				}: endpoint{},
+			},
+		},
 		"iotsecuredtunneling": service{
 			Defaults: endpointDefaults{
 				defaultKey{}: endpoint{},
@@ -12739,6 +12911,18 @@ var awsPartition = partition{
 		},
 		"ivschat": service{
 			Endpoints: serviceEndpoints{
+				endpointKey{
+					Region: "ap-northeast-1",
+				}: endpoint{},
+				endpointKey{
+					Region: "ap-northeast-2",
+				}: endpoint{},
+				endpointKey{
+					Region: "ap-south-1",
+				}: endpoint{},
+				endpointKey{
+					Region: "eu-central-1",
+				}: endpoint{},
 				endpointKey{
 					Region: "eu-west-1",
 				}: endpoint{},
@@ -15491,6 +15675,31 @@ var awsPartition = partition{
 				}: endpoint{},
 			},
 		},
+		"migrationhub-orchestrator": service{
+			Endpoints: serviceEndpoints{
+				endpointKey{
+					Region: "ap-northeast-1",
+				}: endpoint{},
+				endpointKey{
+					Region: "ap-southeast-2",
+				}: endpoint{},
+				endpointKey{
+					Region: "eu-central-1",
+				}: endpoint{},
+				endpointKey{
+					Region: "eu-west-1",
+				}: endpoint{},
+				endpointKey{
+					Region: "eu-west-2",
+				}: endpoint{},
+				endpointKey{
+					Region: "us-east-1",
+				}: endpoint{},
+				endpointKey{
+					Region: "us-west-2",
+				}: endpoint{},
+			},
+		},
 		"migrationhub-strategy": service{
 			Endpoints: serviceEndpoints{
 				endpointKey{
@@ -33643,6 +33852,13 @@ var awsisobPartition = partition{
 				}: endpoint{},
 			},
 		},
+		"resource-groups": service{
+			Endpoints: serviceEndpoints{
+				endpointKey{
+					Region: "us-isob-east-1",
+				}: endpoint{},
+			},
+		},
 		"route53": service{
 			PartitionEndpoint: "aws-iso-b-global",
 			IsRegionalized:    boxedFalse,
diff --git a/vendor/github.com/aws/aws-sdk-go/aws/version.go b/vendor/github.com/aws/aws-sdk-go/aws/version.go
index aba2334c8..5b66e4f42 100644
--- a/vendor/github.com/aws/aws-sdk-go/aws/version.go
+++ b/vendor/github.com/aws/aws-sdk-go/aws/version.go
@@ -5,4 +5,4 @@ package aws
 const SDKName = "aws-sdk-go"
 
 // SDKVersion is the version of this SDK
-const SDKVersion = "1.44.104"
+const SDKVersion = "1.44.108"
diff --git a/vendor/github.com/aws/aws-sdk-go/service/ec2/api.go b/vendor/github.com/aws/aws-sdk-go/service/ec2/api.go
index d26bcc020..0cd8a5948 100644
--- a/vendor/github.com/aws/aws-sdk-go/service/ec2/api.go
+++ b/vendor/github.com/aws/aws-sdk-go/service/ec2/api.go
@@ -107861,6 +107861,10 @@ type FleetLaunchTemplateOverrides struct {
 	// The Availability Zone in which to launch the instances.
 	AvailabilityZone *string `locationName:"availabilityZone" type:"string"`
 
+	// The ID of the AMI. An AMI is required to launch an instance. The AMI ID must
+	// be specified here or in the launch template.
+	ImageId *string `locationName:"imageId" type:"string"`
+
 	// The attributes for the instance types. When you specify instance attributes,
 	// Amazon EC2 will identify instance types with those attributes.
 	//
@@ -107932,6 +107936,12 @@ func (s *FleetLaunchTemplateOverrides) SetAvailabilityZone(v string) *FleetLaunc
 	return s
 }
 
+// SetImageId sets the ImageId field's value.
+func (s *FleetLaunchTemplateOverrides) SetImageId(v string) *FleetLaunchTemplateOverrides {
+	s.ImageId = &v
+	return s
+}
+
 // SetInstanceRequirements sets the InstanceRequirements field's value.
 func (s *FleetLaunchTemplateOverrides) SetInstanceRequirements(v *InstanceRequirements) *FleetLaunchTemplateOverrides {
 	s.InstanceRequirements = v
@@ -107981,6 +107991,10 @@ type FleetLaunchTemplateOverridesRequest struct {
 	// The Availability Zone in which to launch the instances.
 	AvailabilityZone *string `type:"string"`
 
+	// The ID of the AMI. An AMI is required to launch an instance. The AMI ID must
+	// be specified here or in the launch template.
+	ImageId *string `type:"string"`
+
 	// The attributes for the instance types. When you specify instance attributes,
 	// Amazon EC2 will identify instance types with those attributes.
 	//
@@ -108069,6 +108083,12 @@ func (s *FleetLaunchTemplateOverridesRequest) SetAvailabilityZone(v string) *Fle
 	return s
 }
 
+// SetImageId sets the ImageId field's value.
+func (s *FleetLaunchTemplateOverridesRequest) SetImageId(v string) *FleetLaunchTemplateOverridesRequest {
+	s.ImageId = &v
+	return s
+}
+
 // SetInstanceRequirements sets the InstanceRequirements field's value.
 func (s *FleetLaunchTemplateOverridesRequest) SetInstanceRequirements(v *InstanceRequirementsRequest) *FleetLaunchTemplateOverridesRequest {
 	s.InstanceRequirements = v
@@ -170022,6 +170042,9 @@ const (
 
 	// InstanceTypeP4de24xlarge is a InstanceType enum value
 	InstanceTypeP4de24xlarge = "p4de.24xlarge"
+
+	// InstanceTypeU3tb156xlarge is a InstanceType enum value
+	InstanceTypeU3tb156xlarge = "u-3tb1.56xlarge"
 )
 
 // InstanceType_Values returns all elements of the InstanceType enum
@@ -170595,6 +170618,7 @@ func InstanceType_Values() []string {
 		InstanceTypeR6a48xlarge,
 		InstanceTypeR6aMetal,
 		InstanceTypeP4de24xlarge,
+		InstanceTypeU3tb156xlarge,
 	}
 }
 
diff --git a/vendor/google.golang.org/api/internal/version.go b/vendor/google.golang.org/api/internal/version.go
index ce0e80c4e..ab476d113 100644
--- a/vendor/google.golang.org/api/internal/version.go
+++ b/vendor/google.golang.org/api/internal/version.go
@@ -5,4 +5,4 @@
 package internal
 
 // Version is the current tagged release of the library.
-const Version = "0.97.0"
+const Version = "0.98.0"
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 263c3b4a8..eeb0d1f1c 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -73,7 +73,7 @@ github.com/Microsoft/go-winio/pkg/guid
 github.com/VividCortex/ewma
 # github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
 github.com/acarl005/stripansi
-# github.com/aws/aws-sdk-go v1.44.104
+# github.com/aws/aws-sdk-go v1.44.108
 ## explicit
 github.com/aws/aws-sdk-go/aws
 github.com/aws/aws-sdk-go/aws/arn
@@ -721,7 +721,7 @@ golang.org/x/tools/internal/typeparams
 # golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f
 golang.org/x/xerrors
 golang.org/x/xerrors/internal
-# google.golang.org/api v0.97.0
+# google.golang.org/api v0.98.0
 ## explicit
 google.golang.org/api/googleapi
 google.golang.org/api/googleapi/transport


Test patch:
diff --git a/internal/osbuild/copy_stage_test.go b/internal/osbuild/copy_stage_test.go
index 0be79ddb2..3777f7edb 100644
--- a/internal/osbuild/copy_stage_test.go
+++ b/internal/osbuild/copy_stage_test.go
@@ -29,20 +29,20 @@ func TestNewCopyStage(t *testing.T) {
 		*NewBtrfsMount("root", "root", "/"),
 	}
 
-	treeInput := CopyStageInput{}
+	treeInput := TreeInput{}
 	treeInput.Type = "org.osbuild.tree"
 	treeInput.Origin = "org.osbuild.pipeline"
 	treeInput.References = []string{"name:input-pipeline"}
 	expectedStage := &Stage{
 		Type:    "org.osbuild.copy",
 		Options: &CopyStageOptions{paths},
-		Inputs:  &CopyStageInputs{"tree-input": treeInput},
+		Inputs:  &PipelineTreeInputs{"tree-input": treeInput},
 		Devices: devices,
 		Mounts:  mounts,
 	}
 	// convert to alias types
 	stageMounts := Mounts(mounts)
 	stageDevices := Devices(devices)
-	actualStage := NewCopyStage(&CopyStageOptions{paths}, &CopyStageInputs{"tree-input": treeInput}, &stageDevices, &stageMounts)
+	actualStage := NewCopyStage(&CopyStageOptions{paths}, NewPipelineTreeInputs("tree-input", "input-pipeline"), &stageDevices, &stageMounts)
 	assert.Equal(t, expectedStage, actualStage)
 }
diff --git a/internal/osbuild/oci_archive_stage_test.go b/internal/osbuild/oci_archive_stage_test.go
index c4395ec7d..ac3af14bd 100644
--- a/internal/osbuild/oci_archive_stage_test.go
+++ b/internal/osbuild/oci_archive_stage_test.go
@@ -36,7 +36,7 @@ func TestOCIArchiveInputs(t *testing.T) {
 		}
 	}`
 	inputs := new(OCIArchiveStageInputs)
-	base := &OCIArchiveStageInput{
+	base := &TreeInput{
 		References: []string{
 			"name:container-tree",
 		},
@@ -44,14 +44,14 @@ func TestOCIArchiveInputs(t *testing.T) {
 	base.Type = "org.osbuild.oci-archive"
 	base.Origin = "org.osbuild.pipeline"
 
-	layer1 := OCIArchiveStageInput{
+	layer1 := TreeInput{
 		References: []string{
 			"name:container-ostree",
 		},
 	}
 	layer1.Type = "org.osbuild.tree"
 	layer1.Origin = "org.osbuild.pipeline"
-	layer2 := OCIArchiveStageInput{
+	layer2 := TreeInput{
 		References: []string{
 			"name:container-ostree2",
 		},
@@ -60,7 +60,7 @@ func TestOCIArchiveInputs(t *testing.T) {
 	layer2.Origin = "org.osbuild.pipeline"
 
 	inputs.Base = base
-	inputs.Layers = []OCIArchiveStageInput{layer1, layer2}
+	inputs.Layers = []TreeInput{layer1, layer2}
 
 	data, err := json.Marshal(inputs)
 	assert.NoError(t, err)
diff --git a/internal/osbuild/tar_stage_test.go b/internal/osbuild/tar_stage_test.go
index c2eb8a6fb..24888ac49 100644
--- a/internal/osbuild/tar_stage_test.go
+++ b/internal/osbuild/tar_stage_test.go
@@ -8,13 +8,22 @@ import (
 
 func TestNewTarStage(t *testing.T) {
 	stageOptions := &TarStageOptions{Filename: "archive.tar.xz"}
-	stageInputs := &TarStageInputs{}
+	stageInputs := &PipelineTreeInputs{"tree": TreeInput{
+		inputCommon: inputCommon{
+			Type:   "org.osbuild.tree",
+			Origin: "org.osbuild.pipeline",
+		},
+		References: []string{
+			"name:pipeline33",
+		},
+	},
+	}
 	expectedStage := &Stage{
 		Type:    "org.osbuild.tar",
 		Options: stageOptions,
 		Inputs:  stageInputs,
 	}
-	actualStage := NewTarStage(stageOptions, stageInputs)
+	actualStage := NewTarStage(stageOptions, "pipeline33")
 	assert.Equal(t, expectedStage, actualStage)
 }
 
@@ -59,10 +68,10 @@ func TestTarStageOptionsValidate(t *testing.T) {
 		t.Run(tt.name, func(t *testing.T) {
 			if tt.err {
 				assert.Errorf(t, tt.options.validate(), "%q didn't return an error [idx: %d]", tt.name, idx)
-				assert.Panics(t, func() { NewTarStage(&tt.options, &TarStageInputs{}) })
+				assert.Panics(t, func() { NewTarStage(&tt.options, "") })
 			} else {
 				assert.NoErrorf(t, tt.options.validate(), "%q returned an error [idx: %d]", tt.name, idx)
-				assert.NotPanics(t, func() { NewTarStage(&tt.options, &TarStageInputs{}) })
+				assert.NotPanics(t, func() { NewTarStage(&tt.options, "") })
 			}
 		})
 	}
diff --git a/internal/osbuild/users_stage_test.go b/internal/osbuild/users_stage_test.go
index ec858bc73..5e0aa161c 100644
--- a/internal/osbuild/users_stage_test.go
+++ b/internal/osbuild/users_stage_test.go
@@ -4,8 +4,7 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/osbuild/osbuild-composer/internal/blueprint"
-
+	"github.com/osbuild/osbuild-composer/internal/users"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
@@ -24,20 +23,20 @@ func TestNewUsersStageOptionsPassword(t *testing.T) {
 	EmptyPass := ""
 	CryptPass := "$6$RWdHzrPfoM6BMuIP$gKYlBXQuJgP.G2j2twbOyxYjFDPUQw8Jp.gWe1WD/obX0RMyfgw5vt.Mn/tLLX4mQjaklSiIzoAW3HrVQRg4Q." // #nosec G101
 
-	users := []blueprint.UserCustomization{
-		blueprint.UserCustomization{
+	users := []users.User{
+		{
 			Name:     "bart",
 			Password: &Pass,
 		},
-		blueprint.UserCustomization{
+		{
 			Name:     "lisa",
 			Password: &CryptPass,
 		},
-		blueprint.UserCustomization{
+		{
 			Name:     "maggie",
 			Password: &EmptyPass,
 		},
-		blueprint.UserCustomization{
+		{
 			Name: "homer",
 		},
 	}
diff --git a/internal/store/store_test.go b/internal/store/store_test.go
index 8736551ce..96cd2fceb 100644
--- a/internal/store/store_test.go
+++ b/internal/store/store_test.go
@@ -23,8 +23,9 @@ type storeTest struct {
 	myStore          *Store
 	myCustomizations blueprint.Customizations
 	myBP             blueprint.Blueprint
-	CommitHash       string
-	myChange         blueprint.Change
+	myBPv3           blueprint.Blueprint
+	CommitHash       []string
+	myChange         []blueprint.Change
 	myTarget         *target.Target
 	mySources        map[string]osbuild.Source
 	myCompose        Compose
@@ -90,13 +91,34 @@ func (suite *storeTest) SetupSuite() {
 			{Name: "test3"}},
 		Customizations: &suite.myCustomizations,
 	}
-	suite.CommitHash = "firstCommit"
-	suite.myChange = blueprint.Change{
-		Commit:    "firstCommit",
-		Message:   "firstCommitMessage",
-		Revision:  nil,
-		Timestamp: "now",
-		Blueprint: suite.myBP,
+	suite.myBPv3 = blueprint.Blueprint{
+		Name:        "testBP",
+		Description: "Testing tagging testBP blueprint",
+		Version:     "3.0.0",
+		Packages: []blueprint.Package{
+			{Name: "test4", Version: "*"}},
+		Modules: []blueprint.Package{
+			{Name: "test5", Version: "*"}},
+		Groups: []blueprint.Group{
+			{Name: "test6"}},
+		Customizations: &suite.myCustomizations,
+	}
+	suite.CommitHash = []string{"firstCommit", "secondCommit"}
+	suite.myChange = []blueprint.Change{
+		blueprint.Change{
+			Commit:    "firstCommit",
+			Message:   "firstCommitMessage",
+			Revision:  nil,
+			Timestamp: "now",
+			Blueprint: suite.myBP,
+		},
+		blueprint.Change{
+			Commit:    "secondCommit",
+			Message:   "secondCommitMessage",
+			Revision:  nil,
+			Timestamp: "now",
+			Blueprint: suite.myBPv3,
+		},
 	}
 	suite.myTarget = &target.Target{
 		Uuid:      uuid.New(),
@@ -190,17 +212,18 @@ func (suite *storeTest) TestGetBlueprintChanges() {
 
 func (suite *storeTest) TestGetBlueprintChange() {
 	Commit := make(map[string]blueprint.Change)
-	Commit[suite.CommitHash] = suite.myChange
-	suite.myStore.blueprintsCommits["testBP"] = []string{suite.CommitHash}
+	Commit[suite.CommitHash[0]] = suite.myChange[0]
+	Commit[suite.CommitHash[1]] = suite.myChange[1]
+	suite.myStore.blueprintsCommits["testBP"] = suite.CommitHash
 	suite.myStore.blueprintsChanges["testBP"] = Commit
 
-	actualChange, err := suite.myStore.GetBlueprintChange("testBP", suite.CommitHash)
+	actualChange, err := suite.myStore.GetBlueprintChange("testBP", suite.CommitHash[0])
 	suite.NoError(err)
-	expectedChange := suite.myChange
+	expectedChange := suite.myChange[0]
 	suite.Equal(&expectedChange, actualChange)
 
 	//Try to get non existing BP
-	actualChange, err = suite.myStore.GetBlueprintChange("Non_existing_BP", suite.CommitHash)
+	actualChange, err = suite.myStore.GetBlueprintChange("Non_existing_BP", suite.CommitHash[0])
 	suite.Nil(actualChange)
 	suite.EqualError(err, "Unknown blueprint")
 
@@ -212,20 +235,44 @@ func (suite *storeTest) TestGetBlueprintChange() {
 
 func (suite *storeTest) TestTagBlueprint() {
 	Commit := make(map[string]blueprint.Change)
-	Commit[suite.CommitHash] = suite.myChange
-	suite.myStore.blueprints["testBP"] = suite.myBP
-	suite.myStore.blueprintsCommits["testBP"] = []string{suite.CommitHash}
+	Commit[suite.CommitHash[0]] = suite.myChange[0]
+	Commit[suite.CommitHash[1]] = suite.myChange[1]
+	suite.myStore.blueprintsCommits["testBP"] = suite.CommitHash
 	suite.myStore.blueprintsChanges["testBP"] = Commit
+	suite.myStore.blueprints["testBP"] = suite.myBPv3
+
+	//Check that the blueprint changes have no revisions
+	suite.Nil(suite.myStore.blueprintsChanges["testBP"][suite.CommitHash[0]].Revision)
+	suite.Nil(suite.myStore.blueprintsChanges["testBP"][suite.CommitHash[1]].Revision)
 
-	//Check that the blueprints change has no revision
-	suite.Nil(suite.myStore.blueprintsChanges["testBP"][suite.CommitHash].Revision)
+	// This should tag the most recent commit
 	suite.NoError(suite.myStore.TagBlueprint("testBP"))
-	//The blueprints change should have a revision now
-	actualRevision := suite.myStore.blueprintsChanges["testBP"][suite.CommitHash].Revision
+
+	actualRevision := suite.myStore.blueprintsChanges["testBP"][suite.CommitHash[0]].Revision
+	suite.Nil(actualRevision)
+
+	actualRevision = suite.myStore.blueprintsChanges["testBP"][suite.CommitHash[1]].Revision
+	suite.Require().NotNil(actualRevision)
 	suite.Equal(1, *actualRevision)
+
+	// Check the blueprints to make sure they have not been changed
+	actualBP := suite.myStore.blueprintsChanges["testBP"][suite.CommitHash[0]].Blueprint
+	suite.Equal(suite.myBP, actualBP)
+
+	actualBP = suite.myStore.blueprintsChanges["testBP"][suite.CommitHash[1]].Blueprint
+	suite.Equal(suite.myBPv3, actualBP)
+	suite.Equal(suite.myBPv3, suite.myStore.blueprints["testBP"])
+
 	//Try to tag it again (should not change)
 	suite.NoError(suite.myStore.TagBlueprint("testBP"))
+
+	actualRevision = suite.myStore.blueprintsChanges["testBP"][suite.CommitHash[0]].Revision
+	suite.Nil(actualRevision)
+
+	actualRevision = suite.myStore.blueprintsChanges["testBP"][suite.CommitHash[1]].Revision
+	suite.Require().NotNil(actualRevision)
 	suite.Equal(1, *actualRevision)
+
 	//Try to tag a non existing BNP
 	suite.EqualError(suite.myStore.TagBlueprint("Non_existing_BP"), "Unknown blueprint")
 	//Remove commits from a blueprint and try to tag it
diff --git a/test/cases/azure.sh b/test/cases/azure.sh
index 5cfeac2c2..7bf33b244 100755
--- a/test/cases/azure.sh
+++ b/test/cases/azure.sh
@@ -59,30 +59,6 @@ else
 fi
 az version
 
-# We need terraform to provision the vm in azure and then destroy it
-if [ "$ID" == "rhel" ] || [ "$ID" == "centos" ]
-then
-    release="RHEL"
-elif [ "$ID" == "fedora" ]
-then
-    release="fedora"
-else
-    echo "Test is not running on neither Fedora, RHEL or CentOS, terminating!"
-    exit 1
-fi
-
-sudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/$release/hashicorp.repo
-# set $releasever to 8 when running on RHEL-9 because there is no hashicorp repo for it yet
-if [[ $ID == rhel || $ID == centos ]] && [[ ${VERSION_ID%.*} == 9 ]]; then
-    # shellcheck disable=SC2016
-    sudo sed -i 's/$releasever/8/g' /etc/yum.repos.d/hashicorp.repo
-# set $releasever to 36 when running on Fedora 37 because there is no hashicorp repo for it yet
-elif [[ $ID == fedora ]] && [[ ${VERSION_ID%.*} == 37 ]]; then
-    # shellcheck disable=SC2016
-    sudo sed -i 's/$releasever/36/g' /etc/yum.repos.d/hashicorp.repo
-fi
-sudo dnf install -y terraform
-
 ARCH=$(uname -m)
 TEST_ID="$DISTRO_CODE-$ARCH-$BRANCH_NAME-$BUILD_ID"
 IMAGE_KEY=image-${TEST_ID}
@@ -95,16 +71,6 @@ BLUEPRINT_FILE=${TEMPDIR}/blueprint.toml
 COMPOSE_START=${TEMPDIR}/compose-start-${IMAGE_KEY}.json
 COMPOSE_INFO=${TEMPDIR}/compose-info-${IMAGE_KEY}.json
 
-# Check for the smoke test file on the Azure instance that we start.
-smoke_test_check () {
-    SMOKE_TEST=$(sudo ssh -i key.rsa redhat@"${1}" -o StrictHostKeyChecking=no 'cat /etc/smoke-test.txt')
-    if [[ $SMOKE_TEST == smoke-test ]]; then
-        echo 1
-    else
-        echo 0
-    fi
-}
-
 # Get the compose log.
 get_compose_log () {
     COMPOSE_ID=$1
@@ -226,53 +192,42 @@ if [[ $COMPOSE_STATUS != FINISHED ]]; then
     exit 1
 fi
 
-# Set up necessary variables for terraform
-export TF_VAR_RESOURCE_GROUP="$AZURE_RESOURCE_GROUP"
-export TF_VAR_STORAGE_ACCOUNT="$AZURE_STORAGE_ACCOUNT"
-export TF_VAR_CONTAINER_NAME="$AZURE_CONTAINER_NAME"
-export TF_VAR_BLOB_NAME="$IMAGE_KEY".vhd
-export TF_VAR_TEST_ID="$TEST_ID"
-# https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/image#argument-reference
-export TF_VAR_HYPER_V_GEN="${HYPER_V_GEN}"
 export BLOB_URL="https://$AZURE_STORAGE_ACCOUNT.blob.core.windows.net/$AZURE_CONTAINER_NAME/$IMAGE_KEY.vhd"
-export ARM_CLIENT_ID="$V2_AZURE_CLIENT_ID" > /dev/null
-export ARM_CLIENT_SECRET="$V2_AZURE_CLIENT_SECRET" > /dev/null
-export ARM_SUBSCRIPTION_ID="$AZURE_SUBSCRIPTION_ID" > /dev/null
-export ARM_TENANT_ID="$AZURE_TENANT_ID" > /dev/null
-
-SSH_DATA_DIR=$(/usr/libexec/osbuild-composer-test/gen-ssh.sh)
 
-# Copy terraform main file and cloud-init to current working directory
-cp /usr/share/tests/osbuild-composer/azure/main.tf .
-cp "${SSH_DATA_DIR}"/user-data .
+greenprint "Pulling cloud-image-val container"
 
-# Initialize terraform
-terraform init
+CONTAINER_CLOUD_IMAGE_VAL="quay.io/cloudexperience/cloud-image-val-test:prod"
 
-# Import the uploaded page blob to terraform
-terraform import azurerm_storage_blob.testBlob "$BLOB_URL"
+sudo ${CONTAINER_RUNTIME} pull ${CONTAINER_CLOUD_IMAGE_VAL}
 
-# Apply the configuration
-terraform apply -auto-approve
+greenprint "Running cloud-image-val on generated image"
 
-PUBLIC_IP=$(terraform output -raw public_IP)
-terraform output -raw tls_private_key > key.rsa
-chmod 400 key.rsa
-
-# Check for our smoke test file.
-greenprint "🛃 Checking for smoke test file"
-for _ in {0..10}; do
-    RESULTS="$(smoke_test_check "$PUBLIC_IP")"
-    if [[ $RESULTS == 1 ]]; then
-        echo "Smoke test passed! 🥳"
-        break
-    fi
-    echo "Machine is not ready yet, retrying connection."
-    sleep 5
-done
+tee "${TEMPDIR}/resource-file.json" <<EOF
+{
+  "subscription_id": "${AZURE_SUBSCRIPTION_ID}",
+  "resource_group": "${AZURE_RESOURCE_GROUP}",
+  "provider": "azure",
+  "instances": [
+    {
+      "vhd_uri": "${BLOB_URL}",
+      "location": "${AZURE_LOCATION}",
+      "name": "${IMAGE_KEY}"
+    }
+  ]
+}
+EOF
 
-# Clean up resources in Azure
-terraform destroy -auto-approve
+sudo ${CONTAINER_RUNTIME} run \
+    -a stdout -a stderr \
+    -e ARM_CLIENT_ID="${V2_AZURE_CLIENT_ID}" \
+    -e ARM_CLIENT_SECRET="${V2_AZURE_CLIENT_SECRET}" \
+    -e ARM_SUBSCRIPTION_ID="${AZURE_SUBSCRIPTION_ID}" \
+    -e ARM_TENANT_ID="${AZURE_TENANT_ID}" \
+    -v "${TEMPDIR}":/tmp:Z \
+    ${CONTAINER_CLOUD_IMAGE_VAL} \
+    python cloud-image-val.py -r /tmp/resource-file.json -d -o /tmp/report.xml -m 'not pub' && RESULTS=1 || RESULTS=0
+
+mv "${TEMPDIR}"/report.html "${ARTIFACTS}"
 
 # Also delete the compose so we don't run out of disk space
 sudo composer-cli compose delete "${COMPOSE_ID}" > /dev/null
diff --git a/test/cases/ostree-ng.sh b/test/cases/ostree-ng.sh
index f512ac4bc..c2244c34a 100755
--- a/test/cases/ostree-ng.sh
+++ b/test/cases/ostree-ng.sh
@@ -559,6 +559,15 @@ sudo rm -f "$LIBVIRT_BIOS_IMAGE_PATH"
 ## Install, upgrade and test Edge image on UEFI VM
 ##
 ##################################################
+
+# Since virt-install 4.0.0, loader attribute can't be configured here,
+# otherwise it'll report "loader attribute 'readonly' cannot be specified
+# when firmware autoselection is enabled"
+if nvrGreaterOrEqual "virt-install" "4.0.0"; then
+    BOOT_UEFI_ARGS="uefi"
+else
+    BOOT_UEFI_ARGS="uefi,loader_ro=yes,loader_type=pflash,nvram_template=/usr/share/edk2/ovmf/OVMF_VARS.fd,loader_secure=no"
+fi
 # Install ostree image via anaconda.
 greenprint "💿 Install ostree image via installer(ISO) on UEFI VM"
 sudo virt-install  --name="${IMAGE_KEY}-uefi"\
@@ -569,7 +578,7 @@ sudo virt-install  --name="${IMAGE_KEY}-uefi"\
                    --os-type linux \
                    --os-variant ${OS_VARIANT} \
                    --cdrom "/var/lib/libvirt/images/${ISO_FILENAME}" \
-                   --boot uefi,loader_ro=yes,loader_type=pflash,nvram_template=/usr/share/edk2/ovmf/OVMF_VARS.fd,loader_secure=no \
+                   --boot "$BOOT_UEFI_ARGS" \
                    --nographics \
                    --noautoconsole \
                    --wait=-1 \
diff --git a/test/data/ansible/check_ostree.yaml b/test/data/ansible/check_ostree.yaml
index de48f1ff5..40e8d1069 100644
--- a/test/data/ansible/check_ostree.yaml
+++ b/test/data/ansible/check_ostree.yaml
@@ -250,6 +250,25 @@
         - name: failed count + 1
           set_fact:
             failed_counter: "{{ failed_counter | int + 1 }}"
+      when: (ansible_facts['distribution'] == 'Fedora' and ansible_facts['distribution_version'] is version('37', '<')) or
+            (ansible_facts['distribution'] == 'CentOS') or (ansible_facts['distribution'] == 'RedHat')
+
+    # https://fedoraproject.org/wiki/Changes/Silverblue_Kinoite_readonly_sysroot
+    - name: /sysroot should be mount with ro permission since Fedora 37
+      block:
+        - assert:
+            that:
+              - result_sysroot_mount_status.stdout == "ro"
+            fail_msg: "/sysroot is not mounted with ro permission"
+            success_msg: "/sysroot is mounted with ro permission"
+      always:
+        - set_fact:
+            total_counter: "{{ total_counter | int + 1 }}"
+      rescue:
+        - name: failed count + 1
+          set_fact:
+            failed_counter: "{{ failed_counter | int + 1 }}"
+      when: ansible_facts['distribution'] == 'Fedora' and ansible_facts['distribution_version'] is version('37', '>=')
 
     # case: check /var mount point
     - name: check /var mount point
@@ -382,6 +401,8 @@
         - name: failed count + 1
           set_fact:
             failed_counter: "{{ failed_counter | int + 1 }}"
+      when: (ansible_facts['distribution'] == 'Fedora' and ansible_facts['distribution_version'] is version('37', '<')) or
+            (ansible_facts['distribution'] == 'CentOS') or (ansible_facts['distribution'] == 'RedHat')
 
     # case: check dmesg error and failed log
     - name: check dmesg output
@@ -514,8 +535,6 @@
         - name: failed count + 1
           set_fact:
             failed_counter: "{{ failed_counter | int + 1 }}"
-      when: (ansible_facts['distribution'] == 'RedHat' and ansible_facts['distribution_version'] is version('8.6', '>=')) or
-            (ansible_facts['distribution'] == 'CentOS')
       when: (ansible_facts['distribution'] == 'RedHat' and ansible_facts['distribution_version'] is version('8.6', '>=')) or
             (ansible_facts['distribution'] == 'CentOS') or
             (ansible_facts['distribution'] == 'Fedora' and ansible_facts['distribution_version'] is version('36', '>='))
diff --git a/test/data/manifests/fedora_35-aarch64-iot_installer-boot.json b/test/data/manifests/fedora_35-aarch64-iot_installer-boot.json
index d23689ba0..14120ed39 100644
--- a/test/data/manifests/fedora_35-aarch64-iot_installer-boot.json
+++ b/test/data/manifests/fedora_35-aarch64-iot_installer-boot.json
@@ -9990,13 +9990,45 @@
         ]
       },
       {
-        "name": "bootiso-tree",
+        "name": "rootfs-image",
         "build": "name:build",
         "stages": [
           {
-            "type": "org.osbuild.bootiso.mono",
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "LiveOS"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "LiveOS/rootfs.img",
+              "size": "4294967296"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.ext4",
+            "options": {
+              "uuid": "2fe99653-f7ff-44fd-bea8-fa70107524fb",
+              "label": "Anaconda"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
             "inputs": {
-              "rootfs": {
+              "tree": {
                 "type": "org.osbuild.tree",
                 "origin": "org.osbuild.pipeline",
                 "references": [
@@ -10004,52 +10036,220 @@
                 ]
               }
             },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/",
+                  "to": "mount://device/"
+                }
+              ]
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "device",
+                "type": "org.osbuild.ext4",
+                "source": "device",
+                "target": "/"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "efiboot-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.grub2.iso",
             "options": {
               "product": {
                 "name": "Fedora",
                 "version": "35"
               },
-              "kernel": "5.15.13-200.fc35.aarch64",
-              "isolabel": "Fedora-35-BaseOS-aarch64",
-              "efi": {
-                "architectures": [
-                  "AA64"
-                ],
-                "vendor": "fedora"
-              },
-              "isolinux": {
-                "enabled": false
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-35-BaseOS-aarch64:/ostree.ks"
+                ]
               },
-              "kernel_opts": "inst.ks=hd:LABEL=Fedora-35-BaseOS-aarch64:/osbuild.ks",
-              "templates": "99-generic",
-              "rootfs": {
-                "compression": {
-                  "method": "xz",
-                  "options": {
-                    "bcj": "arm"
-                  }
+              "isolabel": "Fedora-35-BaseOS-aarch64",
+              "architectures": [
+                "AA64"
+              ],
+              "vendor": "fedora"
+            }
+          }
+        ]
+      },
+      {
+        "name": "bootiso-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "images"
                 },
-                "size": 9216
+                {
+                  "path": "images/pxeboot"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
               }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/boot/vmlinuz-5.15.13-200.fc35.aarch64",
+                  "to": "tree:///images/pxeboot/vmlinuz"
+                },
+                {
+                  "from": "input://tree/boot/initramfs-5.15.13-200.fc35.aarch64.img",
+                  "to": "tree:///images/pxeboot/initrd.img"
+                }
+              ]
             }
           },
           {
-            "type": "org.osbuild.kickstart",
+            "type": "org.osbuild.squashfs",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:rootfs-image"
+                ]
+              }
+            },
             "options": {
-              "path": "/osbuild.ks",
-              "ostree": {
-                "osname": "fedora",
-                "url": "file:///run/install/repo/ostree/repo",
-                "ref": "test/iot",
-                "gpg": false
+              "filename": "images/install.img",
+              "compression": {
+                "method": "lz4"
               }
             }
           },
           {
-            "type": "org.osbuild.discinfo",
+            "type": "org.osbuild.isolinux",
+            "inputs": {
+              "data": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
+              }
+            },
             "options": {
-              "basearch": "aarch64",
-              "release": "202010217.n.0"
+              "product": {
+                "name": "Fedora",
+                "version": "35"
+              },
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-35-BaseOS-aarch64:/ostree.ks"
+                ]
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "images/efiboot.img",
+              "size": "20MB"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.fat",
+            "options": {
+              "volid": "0194fdc2"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960,
+                  "lock": true
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/",
+                  "to": "mount://root/"
+                }
+              ]
+            },
+            "devices": {
+              "root": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "root",
+                "type": "org.osbuild.fat",
+                "source": "root",
+                "target": "/"
+              }
+            ]
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/EFI",
+                  "to": "tree:///"
+                }
+              ]
             }
           },
           {
@@ -10074,6 +10274,25 @@
             "options": {
               "repo": "/ostree/repo"
             }
+          },
+          {
+            "type": "org.osbuild.kickstart",
+            "options": {
+              "path": "/ostree.ks",
+              "ostree": {
+                "osname": "fedora",
+                "url": "file:///run/install/repo/ostree/repo",
+                "ref": "test/iot",
+                "gpg": false
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.discinfo",
+            "options": {
+              "basearch": "aarch64",
+              "release": "202010217.n.0"
+            }
           }
         ]
       },
diff --git a/test/data/manifests/fedora_35-aarch64-iot_installer_with_users-boot.json b/test/data/manifests/fedora_35-aarch64-iot_installer_with_users-boot.json
index bb9b32288..ef375c217 100644
--- a/test/data/manifests/fedora_35-aarch64-iot_installer_with_users-boot.json
+++ b/test/data/manifests/fedora_35-aarch64-iot_installer_with_users-boot.json
@@ -10020,13 +10020,45 @@
         ]
       },
       {
-        "name": "bootiso-tree",
+        "name": "rootfs-image",
         "build": "name:build",
         "stages": [
           {
-            "type": "org.osbuild.bootiso.mono",
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "LiveOS"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "LiveOS/rootfs.img",
+              "size": "4294967296"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.ext4",
+            "options": {
+              "uuid": "2fe99653-f7ff-44fd-bea8-fa70107524fb",
+              "label": "Anaconda"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
             "inputs": {
-              "rootfs": {
+              "tree": {
                 "type": "org.osbuild.tree",
                 "origin": "org.osbuild.pipeline",
                 "references": [
@@ -10034,39 +10066,249 @@
                 ]
               }
             },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/",
+                  "to": "mount://device/"
+                }
+              ]
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "device",
+                "type": "org.osbuild.ext4",
+                "source": "device",
+                "target": "/"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "efiboot-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.grub2.iso",
             "options": {
               "product": {
                 "name": "Fedora",
                 "version": "35"
               },
-              "kernel": "5.15.13-200.fc35.aarch64",
-              "isolabel": "Fedora-35-BaseOS-aarch64",
-              "efi": {
-                "architectures": [
-                  "AA64"
-                ],
-                "vendor": "fedora"
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-35-BaseOS-aarch64:/ostree.ks"
+                ]
               },
-              "isolinux": {
-                "enabled": false
+              "isolabel": "Fedora-35-BaseOS-aarch64",
+              "architectures": [
+                "AA64"
+              ],
+              "vendor": "fedora"
+            }
+          }
+        ]
+      },
+      {
+        "name": "bootiso-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "images"
+                },
+                {
+                  "path": "images/pxeboot"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/boot/vmlinuz-5.15.13-200.fc35.aarch64",
+                  "to": "tree:///images/pxeboot/vmlinuz"
+                },
+                {
+                  "from": "input://tree/boot/initramfs-5.15.13-200.fc35.aarch64.img",
+                  "to": "tree:///images/pxeboot/initrd.img"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.squashfs",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:rootfs-image"
+                ]
+              }
+            },
+            "options": {
+              "filename": "images/install.img",
+              "compression": {
+                "method": "lz4"
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.isolinux",
+            "inputs": {
+              "data": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
+              }
+            },
+            "options": {
+              "product": {
+                "name": "Fedora",
+                "version": "35"
               },
-              "kernel_opts": "inst.ks=hd:LABEL=Fedora-35-BaseOS-aarch64:/osbuild.ks",
-              "templates": "99-generic",
-              "rootfs": {
-                "compression": {
-                  "method": "xz",
-                  "options": {
-                    "bcj": "arm"
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-35-BaseOS-aarch64:/ostree.ks"
+                ]
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "images/efiboot.img",
+              "size": "20MB"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.fat",
+            "options": {
+              "volid": "0194fdc2"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960,
+                  "lock": true
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/",
+                  "to": "mount://root/"
+                }
+              ]
+            },
+            "devices": {
+              "root": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "root",
+                "type": "org.osbuild.fat",
+                "source": "root",
+                "target": "/"
+              }
+            ]
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/EFI",
+                  "to": "tree:///"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.ostree.init",
+            "options": {
+              "path": "/ostree/repo"
+            }
+          },
+          {
+            "type": "org.osbuild.ostree.pull",
+            "inputs": {
+              "commits": {
+                "type": "org.osbuild.ostree",
+                "origin": "org.osbuild.source",
+                "references": {
+                  "test/iot": {
+                    "ref": "test/iot"
                   }
-                },
-                "size": 9216
+                }
               }
+            },
+            "options": {
+              "repo": "/ostree/repo"
             }
           },
           {
             "type": "org.osbuild.kickstart",
             "options": {
-              "path": "/osbuild.ks",
+              "path": "/ostree.ks",
               "ostree": {
                 "osname": "fedora",
                 "url": "file:///run/install/repo/ostree/repo",
@@ -10103,29 +10345,6 @@
               "basearch": "aarch64",
               "release": "202010217.n.0"
             }
-          },
-          {
-            "type": "org.osbuild.ostree.init",
-            "options": {
-              "path": "/ostree/repo"
-            }
-          },
-          {
-            "type": "org.osbuild.ostree.pull",
-            "inputs": {
-              "commits": {
-                "type": "org.osbuild.ostree",
-                "origin": "org.osbuild.source",
-                "references": {
-                  "test/iot": {
-                    "ref": "test/iot"
-                  }
-                }
-              }
-            },
-            "options": {
-              "repo": "/ostree/repo"
-            }
           }
         ]
       },
diff --git a/test/data/manifests/fedora_35-aarch64-iot_raw_image-boot.json b/test/data/manifests/fedora_35-aarch64-iot_raw_image-boot.json
index ca64e9638..db4076980 100644
--- a/test/data/manifests/fedora_35-aarch64-iot_raw_image-boot.json
+++ b/test/data/manifests/fedora_35-aarch64-iot_raw_image-boot.json
@@ -1795,18 +1795,6 @@
               "osname": "fedora-iot"
             }
           },
-          {
-            "type": "org.osbuild.ostree.config",
-            "options": {
-              "repo": "/ostree/repo",
-              "config": {
-                "sysroot": {
-                  "readonly": true,
-                  "bootloader": "none"
-                }
-              }
-            }
-          },
           {
             "type": "org.osbuild.mkdir",
             "options": {
@@ -1832,7 +1820,8 @@
                 "label": "root"
               },
               "kernel_opts": [
-                "modprobe.blacklist=vc4"
+                "modprobe.blacklist=vc4",
+                "rw"
               ]
             }
           },
@@ -1861,6 +1850,31 @@
               }
             }
           },
+          {
+            "type": "org.osbuild.ostree.config",
+            "options": {
+              "repo": "/ostree/repo",
+              "config": {
+                "sysroot": {
+                  "readonly": true,
+                  "bootloader": "none"
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "ostree-test/fedora/iot",
+                "type": "org.osbuild.ostree.deployment",
+                "options": {
+                  "deployment": {
+                    "osname": "fedora-iot",
+                    "ref": "test/fedora/iot",
+                    "serial": 0
+                  }
+                }
+              }
+            ]
+          },
           {
             "type": "org.osbuild.fstab",
             "options": {
@@ -1970,7 +1984,7 @@
             "options": {
               "root_fs_uuid": "6e4ff95f-f662-45ee-a82a-bdf44a2d0b75",
               "boot_fs_uuid": "0194fdc2-fa2f-4cc0-81d3-ff12045b73c8",
-              "kernel_opts": "modprobe.blacklist=vc4",
+              "kernel_opts": "modprobe.blacklist=vc4,rw",
               "uefi": {
                 "vendor": "fedora",
                 "install": true,
diff --git a/test/data/manifests/fedora_35-x86_64-iot_installer-boot.json b/test/data/manifests/fedora_35-x86_64-iot_installer-boot.json
index 3f233c790..b9c29f8f2 100644
--- a/test/data/manifests/fedora_35-x86_64-iot_installer-boot.json
+++ b/test/data/manifests/fedora_35-x86_64-iot_installer-boot.json
@@ -10183,13 +10183,45 @@
         ]
       },
       {
-        "name": "bootiso-tree",
+        "name": "rootfs-image",
         "build": "name:build",
         "stages": [
           {
-            "type": "org.osbuild.bootiso.mono",
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "LiveOS"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "LiveOS/rootfs.img",
+              "size": "4294967296"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.ext4",
+            "options": {
+              "uuid": "2fe99653-f7ff-44fd-bea8-fa70107524fb",
+              "label": "Anaconda"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
             "inputs": {
-              "rootfs": {
+              "tree": {
                 "type": "org.osbuild.tree",
                 "origin": "org.osbuild.pipeline",
                 "references": [
@@ -10197,52 +10229,220 @@
                 ]
               }
             },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/",
+                  "to": "mount://device/"
+                }
+              ]
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "device",
+                "type": "org.osbuild.ext4",
+                "source": "device",
+                "target": "/"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "efiboot-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.grub2.iso",
             "options": {
               "product": {
                 "name": "Fedora",
                 "version": "35"
               },
-              "kernel": "5.15.13-200.fc35.x86_64",
-              "isolabel": "Fedora-35-BaseOS-x86_64",
-              "efi": {
-                "architectures": [
-                  "X64"
-                ],
-                "vendor": "fedora"
-              },
-              "isolinux": {
-                "enabled": true
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-35-BaseOS-x86_64:/ostree.ks"
+                ]
               },
-              "kernel_opts": "inst.ks=hd:LABEL=Fedora-35-BaseOS-x86_64:/osbuild.ks",
-              "templates": "99-generic",
-              "rootfs": {
-                "compression": {
-                  "method": "xz",
-                  "options": {
-                    "bcj": "x86"
-                  }
+              "isolabel": "Fedora-35-BaseOS-x86_64",
+              "architectures": [
+                "X64"
+              ],
+              "vendor": "fedora"
+            }
+          }
+        ]
+      },
+      {
+        "name": "bootiso-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "images"
                 },
-                "size": 9216
+                {
+                  "path": "images/pxeboot"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
               }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/boot/vmlinuz-5.15.13-200.fc35.x86_64",
+                  "to": "tree:///images/pxeboot/vmlinuz"
+                },
+                {
+                  "from": "input://tree/boot/initramfs-5.15.13-200.fc35.x86_64.img",
+                  "to": "tree:///images/pxeboot/initrd.img"
+                }
+              ]
             }
           },
           {
-            "type": "org.osbuild.kickstart",
+            "type": "org.osbuild.squashfs",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:rootfs-image"
+                ]
+              }
+            },
             "options": {
-              "path": "/osbuild.ks",
-              "ostree": {
-                "osname": "fedora",
-                "url": "file:///run/install/repo/ostree/repo",
-                "ref": "test/iot",
-                "gpg": false
+              "filename": "images/install.img",
+              "compression": {
+                "method": "lz4"
               }
             }
           },
           {
-            "type": "org.osbuild.discinfo",
+            "type": "org.osbuild.isolinux",
+            "inputs": {
+              "data": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
+              }
+            },
             "options": {
-              "basearch": "x86_64",
-              "release": "202010217.n.0"
+              "product": {
+                "name": "Fedora",
+                "version": "35"
+              },
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-35-BaseOS-x86_64:/ostree.ks"
+                ]
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "images/efiboot.img",
+              "size": "20MB"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.fat",
+            "options": {
+              "volid": "0194fdc2"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960,
+                  "lock": true
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/",
+                  "to": "mount://root/"
+                }
+              ]
+            },
+            "devices": {
+              "root": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "root",
+                "type": "org.osbuild.fat",
+                "source": "root",
+                "target": "/"
+              }
+            ]
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/EFI",
+                  "to": "tree:///"
+                }
+              ]
             }
           },
           {
@@ -10267,6 +10467,25 @@
             "options": {
               "repo": "/ostree/repo"
             }
+          },
+          {
+            "type": "org.osbuild.kickstart",
+            "options": {
+              "path": "/ostree.ks",
+              "ostree": {
+                "osname": "fedora",
+                "url": "file:///run/install/repo/ostree/repo",
+                "ref": "test/iot",
+                "gpg": false
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.discinfo",
+            "options": {
+              "basearch": "x86_64",
+              "release": "202010217.n.0"
+            }
           }
         ]
       },
diff --git a/test/data/manifests/fedora_35-x86_64-iot_installer_with_users-boot.json b/test/data/manifests/fedora_35-x86_64-iot_installer_with_users-boot.json
index d411fa490..1a2a8891f 100644
--- a/test/data/manifests/fedora_35-x86_64-iot_installer_with_users-boot.json
+++ b/test/data/manifests/fedora_35-x86_64-iot_installer_with_users-boot.json
@@ -10213,13 +10213,45 @@
         ]
       },
       {
-        "name": "bootiso-tree",
+        "name": "rootfs-image",
         "build": "name:build",
         "stages": [
           {
-            "type": "org.osbuild.bootiso.mono",
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "LiveOS"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "LiveOS/rootfs.img",
+              "size": "4294967296"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.ext4",
+            "options": {
+              "uuid": "2fe99653-f7ff-44fd-bea8-fa70107524fb",
+              "label": "Anaconda"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
             "inputs": {
-              "rootfs": {
+              "tree": {
                 "type": "org.osbuild.tree",
                 "origin": "org.osbuild.pipeline",
                 "references": [
@@ -10227,39 +10259,249 @@
                 ]
               }
             },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/",
+                  "to": "mount://device/"
+                }
+              ]
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "device",
+                "type": "org.osbuild.ext4",
+                "source": "device",
+                "target": "/"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "efiboot-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.grub2.iso",
             "options": {
               "product": {
                 "name": "Fedora",
                 "version": "35"
               },
-              "kernel": "5.15.13-200.fc35.x86_64",
-              "isolabel": "Fedora-35-BaseOS-x86_64",
-              "efi": {
-                "architectures": [
-                  "X64"
-                ],
-                "vendor": "fedora"
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-35-BaseOS-x86_64:/ostree.ks"
+                ]
               },
-              "isolinux": {
-                "enabled": true
+              "isolabel": "Fedora-35-BaseOS-x86_64",
+              "architectures": [
+                "X64"
+              ],
+              "vendor": "fedora"
+            }
+          }
+        ]
+      },
+      {
+        "name": "bootiso-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "images"
+                },
+                {
+                  "path": "images/pxeboot"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/boot/vmlinuz-5.15.13-200.fc35.x86_64",
+                  "to": "tree:///images/pxeboot/vmlinuz"
+                },
+                {
+                  "from": "input://tree/boot/initramfs-5.15.13-200.fc35.x86_64.img",
+                  "to": "tree:///images/pxeboot/initrd.img"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.squashfs",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:rootfs-image"
+                ]
+              }
+            },
+            "options": {
+              "filename": "images/install.img",
+              "compression": {
+                "method": "lz4"
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.isolinux",
+            "inputs": {
+              "data": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
+              }
+            },
+            "options": {
+              "product": {
+                "name": "Fedora",
+                "version": "35"
               },
-              "kernel_opts": "inst.ks=hd:LABEL=Fedora-35-BaseOS-x86_64:/osbuild.ks",
-              "templates": "99-generic",
-              "rootfs": {
-                "compression": {
-                  "method": "xz",
-                  "options": {
-                    "bcj": "x86"
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-35-BaseOS-x86_64:/ostree.ks"
+                ]
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "images/efiboot.img",
+              "size": "20MB"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.fat",
+            "options": {
+              "volid": "0194fdc2"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960,
+                  "lock": true
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/",
+                  "to": "mount://root/"
+                }
+              ]
+            },
+            "devices": {
+              "root": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "root",
+                "type": "org.osbuild.fat",
+                "source": "root",
+                "target": "/"
+              }
+            ]
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/EFI",
+                  "to": "tree:///"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.ostree.init",
+            "options": {
+              "path": "/ostree/repo"
+            }
+          },
+          {
+            "type": "org.osbuild.ostree.pull",
+            "inputs": {
+              "commits": {
+                "type": "org.osbuild.ostree",
+                "origin": "org.osbuild.source",
+                "references": {
+                  "test/iot": {
+                    "ref": "test/iot"
                   }
-                },
-                "size": 9216
+                }
               }
+            },
+            "options": {
+              "repo": "/ostree/repo"
             }
           },
           {
             "type": "org.osbuild.kickstart",
             "options": {
-              "path": "/osbuild.ks",
+              "path": "/ostree.ks",
               "ostree": {
                 "osname": "fedora",
                 "url": "file:///run/install/repo/ostree/repo",
@@ -10296,29 +10538,6 @@
               "basearch": "x86_64",
               "release": "202010217.n.0"
             }
-          },
-          {
-            "type": "org.osbuild.ostree.init",
-            "options": {
-              "path": "/ostree/repo"
-            }
-          },
-          {
-            "type": "org.osbuild.ostree.pull",
-            "inputs": {
-              "commits": {
-                "type": "org.osbuild.ostree",
-                "origin": "org.osbuild.source",
-                "references": {
-                  "test/iot": {
-                    "ref": "test/iot"
-                  }
-                }
-              }
-            },
-            "options": {
-              "repo": "/ostree/repo"
-            }
           }
         ]
       },
diff --git a/test/data/manifests/fedora_35-x86_64-iot_raw_image-boot.json b/test/data/manifests/fedora_35-x86_64-iot_raw_image-boot.json
index fe439755c..bae7fe27e 100644
--- a/test/data/manifests/fedora_35-x86_64-iot_raw_image-boot.json
+++ b/test/data/manifests/fedora_35-x86_64-iot_raw_image-boot.json
@@ -1819,18 +1819,6 @@
               "osname": "fedora-iot"
             }
           },
-          {
-            "type": "org.osbuild.ostree.config",
-            "options": {
-              "repo": "/ostree/repo",
-              "config": {
-                "sysroot": {
-                  "readonly": true,
-                  "bootloader": "none"
-                }
-              }
-            }
-          },
           {
             "type": "org.osbuild.mkdir",
             "options": {
@@ -1856,7 +1844,8 @@
                 "label": "root"
               },
               "kernel_opts": [
-                "modprobe.blacklist=vc4"
+                "modprobe.blacklist=vc4",
+                "rw"
               ]
             }
           },
@@ -1885,6 +1874,31 @@
               }
             }
           },
+          {
+            "type": "org.osbuild.ostree.config",
+            "options": {
+              "repo": "/ostree/repo",
+              "config": {
+                "sysroot": {
+                  "readonly": true,
+                  "bootloader": "none"
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "ostree-test/fedora/iot",
+                "type": "org.osbuild.ostree.deployment",
+                "options": {
+                  "deployment": {
+                    "osname": "fedora-iot",
+                    "ref": "test/fedora/iot",
+                    "serial": 0
+                  }
+                }
+              }
+            ]
+          },
           {
             "type": "org.osbuild.fstab",
             "options": {
@@ -1994,7 +2008,7 @@
             "options": {
               "root_fs_uuid": "6e4ff95f-f662-45ee-a82a-bdf44a2d0b75",
               "boot_fs_uuid": "0194fdc2-fa2f-4cc0-81d3-ff12045b73c8",
-              "kernel_opts": "modprobe.blacklist=vc4",
+              "kernel_opts": "modprobe.blacklist=vc4,rw",
               "legacy": "i386-pc",
               "uefi": {
                 "vendor": "fedora",
diff --git a/test/data/manifests/fedora_36-aarch64-iot_installer-boot.json b/test/data/manifests/fedora_36-aarch64-iot_installer-boot.json
index 472727e0e..da58bc53a 100644
--- a/test/data/manifests/fedora_36-aarch64-iot_installer-boot.json
+++ b/test/data/manifests/fedora_36-aarch64-iot_installer-boot.json
@@ -10510,13 +10510,45 @@
         ]
       },
       {
-        "name": "bootiso-tree",
+        "name": "rootfs-image",
         "build": "name:build",
         "stages": [
           {
-            "type": "org.osbuild.bootiso.mono",
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "LiveOS"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "LiveOS/rootfs.img",
+              "size": "4294967296"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.ext4",
+            "options": {
+              "uuid": "2fe99653-f7ff-44fd-bea8-fa70107524fb",
+              "label": "Anaconda"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
             "inputs": {
-              "rootfs": {
+              "tree": {
                 "type": "org.osbuild.tree",
                 "origin": "org.osbuild.pipeline",
                 "references": [
@@ -10524,52 +10556,220 @@
                 ]
               }
             },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/",
+                  "to": "mount://device/"
+                }
+              ]
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "device",
+                "type": "org.osbuild.ext4",
+                "source": "device",
+                "target": "/"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "efiboot-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.grub2.iso",
             "options": {
               "product": {
                 "name": "Fedora",
                 "version": "36"
               },
-              "kernel": "5.18.9-200.fc36.aarch64",
-              "isolabel": "Fedora-36-BaseOS-aarch64",
-              "efi": {
-                "architectures": [
-                  "AA64"
-                ],
-                "vendor": "fedora"
-              },
-              "isolinux": {
-                "enabled": false
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-36-BaseOS-aarch64:/ostree.ks"
+                ]
               },
-              "kernel_opts": "inst.ks=hd:LABEL=Fedora-36-BaseOS-aarch64:/osbuild.ks",
-              "templates": "99-generic",
-              "rootfs": {
-                "compression": {
-                  "method": "xz",
-                  "options": {
-                    "bcj": "arm"
-                  }
+              "isolabel": "Fedora-36-BaseOS-aarch64",
+              "architectures": [
+                "AA64"
+              ],
+              "vendor": "fedora"
+            }
+          }
+        ]
+      },
+      {
+        "name": "bootiso-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "images"
                 },
-                "size": 9216
+                {
+                  "path": "images/pxeboot"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
               }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/boot/vmlinuz-5.18.9-200.fc36.aarch64",
+                  "to": "tree:///images/pxeboot/vmlinuz"
+                },
+                {
+                  "from": "input://tree/boot/initramfs-5.18.9-200.fc36.aarch64.img",
+                  "to": "tree:///images/pxeboot/initrd.img"
+                }
+              ]
             }
           },
           {
-            "type": "org.osbuild.kickstart",
+            "type": "org.osbuild.squashfs",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:rootfs-image"
+                ]
+              }
+            },
             "options": {
-              "path": "/osbuild.ks",
-              "ostree": {
-                "osname": "fedora",
-                "url": "file:///run/install/repo/ostree/repo",
-                "ref": "test/iot",
-                "gpg": false
+              "filename": "images/install.img",
+              "compression": {
+                "method": "lz4"
               }
             }
           },
           {
-            "type": "org.osbuild.discinfo",
+            "type": "org.osbuild.isolinux",
+            "inputs": {
+              "data": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
+              }
+            },
             "options": {
-              "basearch": "aarch64",
-              "release": "202010217.n.0"
+              "product": {
+                "name": "Fedora",
+                "version": "36"
+              },
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-36-BaseOS-aarch64:/ostree.ks"
+                ]
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "images/efiboot.img",
+              "size": "20MB"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.fat",
+            "options": {
+              "volid": "0194fdc2"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960,
+                  "lock": true
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/",
+                  "to": "mount://root/"
+                }
+              ]
+            },
+            "devices": {
+              "root": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "root",
+                "type": "org.osbuild.fat",
+                "source": "root",
+                "target": "/"
+              }
+            ]
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/EFI",
+                  "to": "tree:///"
+                }
+              ]
             }
           },
           {
@@ -10594,6 +10794,25 @@
             "options": {
               "repo": "/ostree/repo"
             }
+          },
+          {
+            "type": "org.osbuild.kickstart",
+            "options": {
+              "path": "/ostree.ks",
+              "ostree": {
+                "osname": "fedora",
+                "url": "file:///run/install/repo/ostree/repo",
+                "ref": "test/iot",
+                "gpg": false
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.discinfo",
+            "options": {
+              "basearch": "aarch64",
+              "release": "202010217.n.0"
+            }
           }
         ]
       },
diff --git a/test/data/manifests/fedora_36-aarch64-iot_installer_with_users-boot.json b/test/data/manifests/fedora_36-aarch64-iot_installer_with_users-boot.json
index 26d0d4745..d02412b5d 100644
--- a/test/data/manifests/fedora_36-aarch64-iot_installer_with_users-boot.json
+++ b/test/data/manifests/fedora_36-aarch64-iot_installer_with_users-boot.json
@@ -10540,13 +10540,45 @@
         ]
       },
       {
-        "name": "bootiso-tree",
+        "name": "rootfs-image",
         "build": "name:build",
         "stages": [
           {
-            "type": "org.osbuild.bootiso.mono",
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "LiveOS"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "LiveOS/rootfs.img",
+              "size": "4294967296"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.ext4",
+            "options": {
+              "uuid": "2fe99653-f7ff-44fd-bea8-fa70107524fb",
+              "label": "Anaconda"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
             "inputs": {
-              "rootfs": {
+              "tree": {
                 "type": "org.osbuild.tree",
                 "origin": "org.osbuild.pipeline",
                 "references": [
@@ -10554,39 +10586,249 @@
                 ]
               }
             },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/",
+                  "to": "mount://device/"
+                }
+              ]
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "device",
+                "type": "org.osbuild.ext4",
+                "source": "device",
+                "target": "/"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "efiboot-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.grub2.iso",
             "options": {
               "product": {
                 "name": "Fedora",
                 "version": "36"
               },
-              "kernel": "5.18.9-200.fc36.aarch64",
-              "isolabel": "Fedora-36-BaseOS-aarch64",
-              "efi": {
-                "architectures": [
-                  "AA64"
-                ],
-                "vendor": "fedora"
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-36-BaseOS-aarch64:/ostree.ks"
+                ]
               },
-              "isolinux": {
-                "enabled": false
+              "isolabel": "Fedora-36-BaseOS-aarch64",
+              "architectures": [
+                "AA64"
+              ],
+              "vendor": "fedora"
+            }
+          }
+        ]
+      },
+      {
+        "name": "bootiso-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "images"
+                },
+                {
+                  "path": "images/pxeboot"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/boot/vmlinuz-5.18.9-200.fc36.aarch64",
+                  "to": "tree:///images/pxeboot/vmlinuz"
+                },
+                {
+                  "from": "input://tree/boot/initramfs-5.18.9-200.fc36.aarch64.img",
+                  "to": "tree:///images/pxeboot/initrd.img"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.squashfs",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:rootfs-image"
+                ]
+              }
+            },
+            "options": {
+              "filename": "images/install.img",
+              "compression": {
+                "method": "lz4"
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.isolinux",
+            "inputs": {
+              "data": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
+              }
+            },
+            "options": {
+              "product": {
+                "name": "Fedora",
+                "version": "36"
               },
-              "kernel_opts": "inst.ks=hd:LABEL=Fedora-36-BaseOS-aarch64:/osbuild.ks",
-              "templates": "99-generic",
-              "rootfs": {
-                "compression": {
-                  "method": "xz",
-                  "options": {
-                    "bcj": "arm"
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-36-BaseOS-aarch64:/ostree.ks"
+                ]
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "images/efiboot.img",
+              "size": "20MB"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.fat",
+            "options": {
+              "volid": "0194fdc2"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960,
+                  "lock": true
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/",
+                  "to": "mount://root/"
+                }
+              ]
+            },
+            "devices": {
+              "root": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "root",
+                "type": "org.osbuild.fat",
+                "source": "root",
+                "target": "/"
+              }
+            ]
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/EFI",
+                  "to": "tree:///"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.ostree.init",
+            "options": {
+              "path": "/ostree/repo"
+            }
+          },
+          {
+            "type": "org.osbuild.ostree.pull",
+            "inputs": {
+              "commits": {
+                "type": "org.osbuild.ostree",
+                "origin": "org.osbuild.source",
+                "references": {
+                  "test/iot": {
+                    "ref": "test/iot"
                   }
-                },
-                "size": 9216
+                }
               }
+            },
+            "options": {
+              "repo": "/ostree/repo"
             }
           },
           {
             "type": "org.osbuild.kickstart",
             "options": {
-              "path": "/osbuild.ks",
+              "path": "/ostree.ks",
               "ostree": {
                 "osname": "fedora",
                 "url": "file:///run/install/repo/ostree/repo",
@@ -10623,29 +10865,6 @@
               "basearch": "aarch64",
               "release": "202010217.n.0"
             }
-          },
-          {
-            "type": "org.osbuild.ostree.init",
-            "options": {
-              "path": "/ostree/repo"
-            }
-          },
-          {
-            "type": "org.osbuild.ostree.pull",
-            "inputs": {
-              "commits": {
-                "type": "org.osbuild.ostree",
-                "origin": "org.osbuild.source",
-                "references": {
-                  "test/iot": {
-                    "ref": "test/iot"
-                  }
-                }
-              }
-            },
-            "options": {
-              "repo": "/ostree/repo"
-            }
           }
         ]
       },
diff --git a/test/data/manifests/fedora_36-aarch64-iot_raw_image-boot.json b/test/data/manifests/fedora_36-aarch64-iot_raw_image-boot.json
index 88e495ec0..6f34e467c 100644
--- a/test/data/manifests/fedora_36-aarch64-iot_raw_image-boot.json
+++ b/test/data/manifests/fedora_36-aarch64-iot_raw_image-boot.json
@@ -2043,18 +2043,6 @@
               "osname": "fedora-iot"
             }
           },
-          {
-            "type": "org.osbuild.ostree.config",
-            "options": {
-              "repo": "/ostree/repo",
-              "config": {
-                "sysroot": {
-                  "readonly": true,
-                  "bootloader": "none"
-                }
-              }
-            }
-          },
           {
             "type": "org.osbuild.mkdir",
             "options": {
@@ -2080,7 +2068,8 @@
                 "label": "root"
               },
               "kernel_opts": [
-                "modprobe.blacklist=vc4"
+                "modprobe.blacklist=vc4",
+                "rw"
               ]
             }
           },
@@ -2109,6 +2098,31 @@
               }
             }
           },
+          {
+            "type": "org.osbuild.ostree.config",
+            "options": {
+              "repo": "/ostree/repo",
+              "config": {
+                "sysroot": {
+                  "readonly": true,
+                  "bootloader": "none"
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "ostree-test/fedora/iot",
+                "type": "org.osbuild.ostree.deployment",
+                "options": {
+                  "deployment": {
+                    "osname": "fedora-iot",
+                    "ref": "test/fedora/iot",
+                    "serial": 0
+                  }
+                }
+              }
+            ]
+          },
           {
             "type": "org.osbuild.fstab",
             "options": {
@@ -2218,7 +2232,7 @@
             "options": {
               "root_fs_uuid": "6e4ff95f-f662-45ee-a82a-bdf44a2d0b75",
               "boot_fs_uuid": "0194fdc2-fa2f-4cc0-81d3-ff12045b73c8",
-              "kernel_opts": "modprobe.blacklist=vc4",
+              "kernel_opts": "modprobe.blacklist=vc4,rw",
               "uefi": {
                 "vendor": "fedora",
                 "install": true,
diff --git a/test/data/manifests/fedora_36-x86_64-iot_installer-boot.json b/test/data/manifests/fedora_36-x86_64-iot_installer-boot.json
index 6e0cc5790..a3d9f6870 100644
--- a/test/data/manifests/fedora_36-x86_64-iot_installer-boot.json
+++ b/test/data/manifests/fedora_36-x86_64-iot_installer-boot.json
@@ -10695,13 +10695,45 @@
         ]
       },
       {
-        "name": "bootiso-tree",
+        "name": "rootfs-image",
         "build": "name:build",
         "stages": [
           {
-            "type": "org.osbuild.bootiso.mono",
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "LiveOS"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "LiveOS/rootfs.img",
+              "size": "4294967296"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.ext4",
+            "options": {
+              "uuid": "2fe99653-f7ff-44fd-bea8-fa70107524fb",
+              "label": "Anaconda"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
             "inputs": {
-              "rootfs": {
+              "tree": {
                 "type": "org.osbuild.tree",
                 "origin": "org.osbuild.pipeline",
                 "references": [
@@ -10709,52 +10741,220 @@
                 ]
               }
             },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/",
+                  "to": "mount://device/"
+                }
+              ]
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "device",
+                "type": "org.osbuild.ext4",
+                "source": "device",
+                "target": "/"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "efiboot-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.grub2.iso",
             "options": {
               "product": {
                 "name": "Fedora",
                 "version": "36"
               },
-              "kernel": "5.18.9-200.fc36.x86_64",
-              "isolabel": "Fedora-36-BaseOS-x86_64",
-              "efi": {
-                "architectures": [
-                  "X64"
-                ],
-                "vendor": "fedora"
-              },
-              "isolinux": {
-                "enabled": true
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-36-BaseOS-x86_64:/ostree.ks"
+                ]
               },
-              "kernel_opts": "inst.ks=hd:LABEL=Fedora-36-BaseOS-x86_64:/osbuild.ks",
-              "templates": "99-generic",
-              "rootfs": {
-                "compression": {
-                  "method": "xz",
-                  "options": {
-                    "bcj": "x86"
-                  }
+              "isolabel": "Fedora-36-BaseOS-x86_64",
+              "architectures": [
+                "X64"
+              ],
+              "vendor": "fedora"
+            }
+          }
+        ]
+      },
+      {
+        "name": "bootiso-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "images"
                 },
-                "size": 9216
+                {
+                  "path": "images/pxeboot"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
               }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/boot/vmlinuz-5.18.9-200.fc36.x86_64",
+                  "to": "tree:///images/pxeboot/vmlinuz"
+                },
+                {
+                  "from": "input://tree/boot/initramfs-5.18.9-200.fc36.x86_64.img",
+                  "to": "tree:///images/pxeboot/initrd.img"
+                }
+              ]
             }
           },
           {
-            "type": "org.osbuild.kickstart",
+            "type": "org.osbuild.squashfs",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:rootfs-image"
+                ]
+              }
+            },
             "options": {
-              "path": "/osbuild.ks",
-              "ostree": {
-                "osname": "fedora",
-                "url": "file:///run/install/repo/ostree/repo",
-                "ref": "test/iot",
-                "gpg": false
+              "filename": "images/install.img",
+              "compression": {
+                "method": "lz4"
               }
             }
           },
           {
-            "type": "org.osbuild.discinfo",
+            "type": "org.osbuild.isolinux",
+            "inputs": {
+              "data": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
+              }
+            },
             "options": {
-              "basearch": "x86_64",
-              "release": "202010217.n.0"
+              "product": {
+                "name": "Fedora",
+                "version": "36"
+              },
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-36-BaseOS-x86_64:/ostree.ks"
+                ]
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "images/efiboot.img",
+              "size": "20MB"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.fat",
+            "options": {
+              "volid": "0194fdc2"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960,
+                  "lock": true
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/",
+                  "to": "mount://root/"
+                }
+              ]
+            },
+            "devices": {
+              "root": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "root",
+                "type": "org.osbuild.fat",
+                "source": "root",
+                "target": "/"
+              }
+            ]
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/EFI",
+                  "to": "tree:///"
+                }
+              ]
             }
           },
           {
@@ -10779,6 +10979,25 @@
             "options": {
               "repo": "/ostree/repo"
             }
+          },
+          {
+            "type": "org.osbuild.kickstart",
+            "options": {
+              "path": "/ostree.ks",
+              "ostree": {
+                "osname": "fedora",
+                "url": "file:///run/install/repo/ostree/repo",
+                "ref": "test/iot",
+                "gpg": false
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.discinfo",
+            "options": {
+              "basearch": "x86_64",
+              "release": "202010217.n.0"
+            }
           }
         ]
       },
diff --git a/test/data/manifests/fedora_36-x86_64-iot_installer_with_users-boot.json b/test/data/manifests/fedora_36-x86_64-iot_installer_with_users-boot.json
index 21038babf..032218760 100644
--- a/test/data/manifests/fedora_36-x86_64-iot_installer_with_users-boot.json
+++ b/test/data/manifests/fedora_36-x86_64-iot_installer_with_users-boot.json
@@ -10725,13 +10725,45 @@
         ]
       },
       {
-        "name": "bootiso-tree",
+        "name": "rootfs-image",
         "build": "name:build",
         "stages": [
           {
-            "type": "org.osbuild.bootiso.mono",
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "LiveOS"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "LiveOS/rootfs.img",
+              "size": "4294967296"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.ext4",
+            "options": {
+              "uuid": "2fe99653-f7ff-44fd-bea8-fa70107524fb",
+              "label": "Anaconda"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
             "inputs": {
-              "rootfs": {
+              "tree": {
                 "type": "org.osbuild.tree",
                 "origin": "org.osbuild.pipeline",
                 "references": [
@@ -10739,39 +10771,249 @@
                 ]
               }
             },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/",
+                  "to": "mount://device/"
+                }
+              ]
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "device",
+                "type": "org.osbuild.ext4",
+                "source": "device",
+                "target": "/"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "efiboot-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.grub2.iso",
             "options": {
               "product": {
                 "name": "Fedora",
                 "version": "36"
               },
-              "kernel": "5.18.9-200.fc36.x86_64",
-              "isolabel": "Fedora-36-BaseOS-x86_64",
-              "efi": {
-                "architectures": [
-                  "X64"
-                ],
-                "vendor": "fedora"
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-36-BaseOS-x86_64:/ostree.ks"
+                ]
               },
-              "isolinux": {
-                "enabled": true
+              "isolabel": "Fedora-36-BaseOS-x86_64",
+              "architectures": [
+                "X64"
+              ],
+              "vendor": "fedora"
+            }
+          }
+        ]
+      },
+      {
+        "name": "bootiso-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "images"
+                },
+                {
+                  "path": "images/pxeboot"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/boot/vmlinuz-5.18.9-200.fc36.x86_64",
+                  "to": "tree:///images/pxeboot/vmlinuz"
+                },
+                {
+                  "from": "input://tree/boot/initramfs-5.18.9-200.fc36.x86_64.img",
+                  "to": "tree:///images/pxeboot/initrd.img"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.squashfs",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:rootfs-image"
+                ]
+              }
+            },
+            "options": {
+              "filename": "images/install.img",
+              "compression": {
+                "method": "lz4"
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.isolinux",
+            "inputs": {
+              "data": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
+              }
+            },
+            "options": {
+              "product": {
+                "name": "Fedora",
+                "version": "36"
               },
-              "kernel_opts": "inst.ks=hd:LABEL=Fedora-36-BaseOS-x86_64:/osbuild.ks",
-              "templates": "99-generic",
-              "rootfs": {
-                "compression": {
-                  "method": "xz",
-                  "options": {
-                    "bcj": "x86"
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-36-BaseOS-x86_64:/ostree.ks"
+                ]
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "images/efiboot.img",
+              "size": "20MB"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.fat",
+            "options": {
+              "volid": "0194fdc2"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960,
+                  "lock": true
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/",
+                  "to": "mount://root/"
+                }
+              ]
+            },
+            "devices": {
+              "root": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "root",
+                "type": "org.osbuild.fat",
+                "source": "root",
+                "target": "/"
+              }
+            ]
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/EFI",
+                  "to": "tree:///"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.ostree.init",
+            "options": {
+              "path": "/ostree/repo"
+            }
+          },
+          {
+            "type": "org.osbuild.ostree.pull",
+            "inputs": {
+              "commits": {
+                "type": "org.osbuild.ostree",
+                "origin": "org.osbuild.source",
+                "references": {
+                  "test/iot": {
+                    "ref": "test/iot"
                   }
-                },
-                "size": 9216
+                }
               }
+            },
+            "options": {
+              "repo": "/ostree/repo"
             }
           },
           {
             "type": "org.osbuild.kickstart",
             "options": {
-              "path": "/osbuild.ks",
+              "path": "/ostree.ks",
               "ostree": {
                 "osname": "fedora",
                 "url": "file:///run/install/repo/ostree/repo",
@@ -10808,29 +11050,6 @@
               "basearch": "x86_64",
               "release": "202010217.n.0"
             }
-          },
-          {
-            "type": "org.osbuild.ostree.init",
-            "options": {
-              "path": "/ostree/repo"
-            }
-          },
-          {
-            "type": "org.osbuild.ostree.pull",
-            "inputs": {
-              "commits": {
-                "type": "org.osbuild.ostree",
-                "origin": "org.osbuild.source",
-                "references": {
-                  "test/iot": {
-                    "ref": "test/iot"
-                  }
-                }
-              }
-            },
-            "options": {
-              "repo": "/ostree/repo"
-            }
           }
         ]
       },
diff --git a/test/data/manifests/fedora_36-x86_64-iot_raw_image-boot.json b/test/data/manifests/fedora_36-x86_64-iot_raw_image-boot.json
index 6c6b48aea..b2c8241ec 100644
--- a/test/data/manifests/fedora_36-x86_64-iot_raw_image-boot.json
+++ b/test/data/manifests/fedora_36-x86_64-iot_raw_image-boot.json
@@ -2067,18 +2067,6 @@
               "osname": "fedora-iot"
             }
           },
-          {
-            "type": "org.osbuild.ostree.config",
-            "options": {
-              "repo": "/ostree/repo",
-              "config": {
-                "sysroot": {
-                  "readonly": true,
-                  "bootloader": "none"
-                }
-              }
-            }
-          },
           {
             "type": "org.osbuild.mkdir",
             "options": {
@@ -2104,7 +2092,8 @@
                 "label": "root"
               },
               "kernel_opts": [
-                "modprobe.blacklist=vc4"
+                "modprobe.blacklist=vc4",
+                "rw"
               ]
             }
           },
@@ -2133,6 +2122,31 @@
               }
             }
           },
+          {
+            "type": "org.osbuild.ostree.config",
+            "options": {
+              "repo": "/ostree/repo",
+              "config": {
+                "sysroot": {
+                  "readonly": true,
+                  "bootloader": "none"
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "ostree-test/fedora/iot",
+                "type": "org.osbuild.ostree.deployment",
+                "options": {
+                  "deployment": {
+                    "osname": "fedora-iot",
+                    "ref": "test/fedora/iot",
+                    "serial": 0
+                  }
+                }
+              }
+            ]
+          },
           {
             "type": "org.osbuild.fstab",
             "options": {
@@ -2242,7 +2256,7 @@
             "options": {
               "root_fs_uuid": "6e4ff95f-f662-45ee-a82a-bdf44a2d0b75",
               "boot_fs_uuid": "0194fdc2-fa2f-4cc0-81d3-ff12045b73c8",
-              "kernel_opts": "modprobe.blacklist=vc4",
+              "kernel_opts": "modprobe.blacklist=vc4,rw",
               "legacy": "i386-pc",
               "uefi": {
                 "vendor": "fedora",
diff --git a/test/data/manifests/fedora_37-aarch64-iot_installer-boot.json b/test/data/manifests/fedora_37-aarch64-iot_installer-boot.json
index 73b678763..d35d22ac7 100644
--- a/test/data/manifests/fedora_37-aarch64-iot_installer-boot.json
+++ b/test/data/manifests/fedora_37-aarch64-iot_installer-boot.json
@@ -10620,13 +10620,45 @@
         ]
       },
       {
-        "name": "bootiso-tree",
+        "name": "rootfs-image",
         "build": "name:build",
         "stages": [
           {
-            "type": "org.osbuild.bootiso.mono",
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "LiveOS"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "LiveOS/rootfs.img",
+              "size": "4294967296"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.ext4",
+            "options": {
+              "uuid": "2fe99653-f7ff-44fd-bea8-fa70107524fb",
+              "label": "Anaconda"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
             "inputs": {
-              "rootfs": {
+              "tree": {
                 "type": "org.osbuild.tree",
                 "origin": "org.osbuild.pipeline",
                 "references": [
@@ -10634,52 +10666,220 @@
                 ]
               }
             },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/",
+                  "to": "mount://device/"
+                }
+              ]
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "device",
+                "type": "org.osbuild.ext4",
+                "source": "device",
+                "target": "/"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "efiboot-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.grub2.iso",
             "options": {
               "product": {
                 "name": "Fedora",
                 "version": "37"
               },
-              "kernel": "5.19.3-300.fc37.aarch64",
-              "isolabel": "Fedora-37-BaseOS-aarch64",
-              "efi": {
-                "architectures": [
-                  "AA64"
-                ],
-                "vendor": "fedora"
-              },
-              "isolinux": {
-                "enabled": false
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-37-BaseOS-aarch64:/ostree.ks"
+                ]
               },
-              "kernel_opts": "inst.ks=hd:LABEL=Fedora-37-BaseOS-aarch64:/osbuild.ks",
-              "templates": "99-generic",
-              "rootfs": {
-                "compression": {
-                  "method": "xz",
-                  "options": {
-                    "bcj": "arm"
-                  }
+              "isolabel": "Fedora-37-BaseOS-aarch64",
+              "architectures": [
+                "AA64"
+              ],
+              "vendor": "fedora"
+            }
+          }
+        ]
+      },
+      {
+        "name": "bootiso-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "images"
                 },
-                "size": 9216
+                {
+                  "path": "images/pxeboot"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
               }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/boot/vmlinuz-5.19.3-300.fc37.aarch64",
+                  "to": "tree:///images/pxeboot/vmlinuz"
+                },
+                {
+                  "from": "input://tree/boot/initramfs-5.19.3-300.fc37.aarch64.img",
+                  "to": "tree:///images/pxeboot/initrd.img"
+                }
+              ]
             }
           },
           {
-            "type": "org.osbuild.kickstart",
+            "type": "org.osbuild.squashfs",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:rootfs-image"
+                ]
+              }
+            },
             "options": {
-              "path": "/osbuild.ks",
-              "ostree": {
-                "osname": "fedora",
-                "url": "file:///run/install/repo/ostree/repo",
-                "ref": "test/iot",
-                "gpg": false
+              "filename": "images/install.img",
+              "compression": {
+                "method": "lz4"
               }
             }
           },
           {
-            "type": "org.osbuild.discinfo",
+            "type": "org.osbuild.isolinux",
+            "inputs": {
+              "data": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
+              }
+            },
             "options": {
-              "basearch": "aarch64",
-              "release": "202010217.n.0"
+              "product": {
+                "name": "Fedora",
+                "version": "37"
+              },
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-37-BaseOS-aarch64:/ostree.ks"
+                ]
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "images/efiboot.img",
+              "size": "20MB"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.fat",
+            "options": {
+              "volid": "0194fdc2"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960,
+                  "lock": true
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/",
+                  "to": "mount://root/"
+                }
+              ]
+            },
+            "devices": {
+              "root": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "root",
+                "type": "org.osbuild.fat",
+                "source": "root",
+                "target": "/"
+              }
+            ]
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/EFI",
+                  "to": "tree:///"
+                }
+              ]
             }
           },
           {
@@ -10704,6 +10904,25 @@
             "options": {
               "repo": "/ostree/repo"
             }
+          },
+          {
+            "type": "org.osbuild.kickstart",
+            "options": {
+              "path": "/ostree.ks",
+              "ostree": {
+                "osname": "fedora",
+                "url": "file:///run/install/repo/ostree/repo",
+                "ref": "test/iot",
+                "gpg": false
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.discinfo",
+            "options": {
+              "basearch": "aarch64",
+              "release": "202010217.n.0"
+            }
           }
         ]
       },
diff --git a/test/data/manifests/fedora_37-aarch64-iot_installer_with_users-boot.json b/test/data/manifests/fedora_37-aarch64-iot_installer_with_users-boot.json
index ad3fed41e..aa8b25fde 100644
--- a/test/data/manifests/fedora_37-aarch64-iot_installer_with_users-boot.json
+++ b/test/data/manifests/fedora_37-aarch64-iot_installer_with_users-boot.json
@@ -10650,13 +10650,45 @@
         ]
       },
       {
-        "name": "bootiso-tree",
+        "name": "rootfs-image",
         "build": "name:build",
         "stages": [
           {
-            "type": "org.osbuild.bootiso.mono",
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "LiveOS"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "LiveOS/rootfs.img",
+              "size": "4294967296"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.ext4",
+            "options": {
+              "uuid": "2fe99653-f7ff-44fd-bea8-fa70107524fb",
+              "label": "Anaconda"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
             "inputs": {
-              "rootfs": {
+              "tree": {
                 "type": "org.osbuild.tree",
                 "origin": "org.osbuild.pipeline",
                 "references": [
@@ -10664,39 +10696,249 @@
                 ]
               }
             },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/",
+                  "to": "mount://device/"
+                }
+              ]
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "device",
+                "type": "org.osbuild.ext4",
+                "source": "device",
+                "target": "/"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "efiboot-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.grub2.iso",
             "options": {
               "product": {
                 "name": "Fedora",
                 "version": "37"
               },
-              "kernel": "5.19.3-300.fc37.aarch64",
-              "isolabel": "Fedora-37-BaseOS-aarch64",
-              "efi": {
-                "architectures": [
-                  "AA64"
-                ],
-                "vendor": "fedora"
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-37-BaseOS-aarch64:/ostree.ks"
+                ]
               },
-              "isolinux": {
-                "enabled": false
+              "isolabel": "Fedora-37-BaseOS-aarch64",
+              "architectures": [
+                "AA64"
+              ],
+              "vendor": "fedora"
+            }
+          }
+        ]
+      },
+      {
+        "name": "bootiso-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "images"
+                },
+                {
+                  "path": "images/pxeboot"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/boot/vmlinuz-5.19.3-300.fc37.aarch64",
+                  "to": "tree:///images/pxeboot/vmlinuz"
+                },
+                {
+                  "from": "input://tree/boot/initramfs-5.19.3-300.fc37.aarch64.img",
+                  "to": "tree:///images/pxeboot/initrd.img"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.squashfs",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:rootfs-image"
+                ]
+              }
+            },
+            "options": {
+              "filename": "images/install.img",
+              "compression": {
+                "method": "lz4"
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.isolinux",
+            "inputs": {
+              "data": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
+              }
+            },
+            "options": {
+              "product": {
+                "name": "Fedora",
+                "version": "37"
               },
-              "kernel_opts": "inst.ks=hd:LABEL=Fedora-37-BaseOS-aarch64:/osbuild.ks",
-              "templates": "99-generic",
-              "rootfs": {
-                "compression": {
-                  "method": "xz",
-                  "options": {
-                    "bcj": "arm"
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-37-BaseOS-aarch64:/ostree.ks"
+                ]
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "images/efiboot.img",
+              "size": "20MB"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.fat",
+            "options": {
+              "volid": "0194fdc2"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960,
+                  "lock": true
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/",
+                  "to": "mount://root/"
+                }
+              ]
+            },
+            "devices": {
+              "root": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "root",
+                "type": "org.osbuild.fat",
+                "source": "root",
+                "target": "/"
+              }
+            ]
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/EFI",
+                  "to": "tree:///"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.ostree.init",
+            "options": {
+              "path": "/ostree/repo"
+            }
+          },
+          {
+            "type": "org.osbuild.ostree.pull",
+            "inputs": {
+              "commits": {
+                "type": "org.osbuild.ostree",
+                "origin": "org.osbuild.source",
+                "references": {
+                  "test/iot": {
+                    "ref": "test/iot"
                   }
-                },
-                "size": 9216
+                }
               }
+            },
+            "options": {
+              "repo": "/ostree/repo"
             }
           },
           {
             "type": "org.osbuild.kickstart",
             "options": {
-              "path": "/osbuild.ks",
+              "path": "/ostree.ks",
               "ostree": {
                 "osname": "fedora",
                 "url": "file:///run/install/repo/ostree/repo",
@@ -10733,29 +10975,6 @@
               "basearch": "aarch64",
               "release": "202010217.n.0"
             }
-          },
-          {
-            "type": "org.osbuild.ostree.init",
-            "options": {
-              "path": "/ostree/repo"
-            }
-          },
-          {
-            "type": "org.osbuild.ostree.pull",
-            "inputs": {
-              "commits": {
-                "type": "org.osbuild.ostree",
-                "origin": "org.osbuild.source",
-                "references": {
-                  "test/iot": {
-                    "ref": "test/iot"
-                  }
-                }
-              }
-            },
-            "options": {
-              "repo": "/ostree/repo"
-            }
           }
         ]
       },
diff --git a/test/data/manifests/fedora_37-aarch64-iot_raw_image-boot.json b/test/data/manifests/fedora_37-aarch64-iot_raw_image-boot.json
index f8ab10432..2cf46964e 100644
--- a/test/data/manifests/fedora_37-aarch64-iot_raw_image-boot.json
+++ b/test/data/manifests/fedora_37-aarch64-iot_raw_image-boot.json
@@ -2051,18 +2051,6 @@
               "osname": "fedora-iot"
             }
           },
-          {
-            "type": "org.osbuild.ostree.config",
-            "options": {
-              "repo": "/ostree/repo",
-              "config": {
-                "sysroot": {
-                  "readonly": true,
-                  "bootloader": "none"
-                }
-              }
-            }
-          },
           {
             "type": "org.osbuild.mkdir",
             "options": {
@@ -2088,7 +2076,8 @@
                 "label": "root"
               },
               "kernel_opts": [
-                "modprobe.blacklist=vc4"
+                "modprobe.blacklist=vc4",
+                "rw"
               ]
             }
           },
@@ -2117,6 +2106,31 @@
               }
             }
           },
+          {
+            "type": "org.osbuild.ostree.config",
+            "options": {
+              "repo": "/ostree/repo",
+              "config": {
+                "sysroot": {
+                  "readonly": true,
+                  "bootloader": "none"
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "ostree-test/fedora/iot",
+                "type": "org.osbuild.ostree.deployment",
+                "options": {
+                  "deployment": {
+                    "osname": "fedora-iot",
+                    "ref": "test/fedora/iot",
+                    "serial": 0
+                  }
+                }
+              }
+            ]
+          },
           {
             "type": "org.osbuild.fstab",
             "options": {
@@ -2226,7 +2240,7 @@
             "options": {
               "root_fs_uuid": "6e4ff95f-f662-45ee-a82a-bdf44a2d0b75",
               "boot_fs_uuid": "0194fdc2-fa2f-4cc0-81d3-ff12045b73c8",
-              "kernel_opts": "modprobe.blacklist=vc4",
+              "kernel_opts": "modprobe.blacklist=vc4,rw",
               "uefi": {
                 "vendor": "fedora",
                 "install": true,
diff --git a/test/data/manifests/fedora_37-x86_64-iot_installer-boot.json b/test/data/manifests/fedora_37-x86_64-iot_installer-boot.json
index ff7ce8303..6ebf39879 100644
--- a/test/data/manifests/fedora_37-x86_64-iot_installer-boot.json
+++ b/test/data/manifests/fedora_37-x86_64-iot_installer-boot.json
@@ -10813,13 +10813,45 @@
         ]
       },
       {
-        "name": "bootiso-tree",
+        "name": "rootfs-image",
         "build": "name:build",
         "stages": [
           {
-            "type": "org.osbuild.bootiso.mono",
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "LiveOS"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "LiveOS/rootfs.img",
+              "size": "4294967296"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.ext4",
+            "options": {
+              "uuid": "2fe99653-f7ff-44fd-bea8-fa70107524fb",
+              "label": "Anaconda"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
             "inputs": {
-              "rootfs": {
+              "tree": {
                 "type": "org.osbuild.tree",
                 "origin": "org.osbuild.pipeline",
                 "references": [
@@ -10827,52 +10859,220 @@
                 ]
               }
             },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/",
+                  "to": "mount://device/"
+                }
+              ]
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "device",
+                "type": "org.osbuild.ext4",
+                "source": "device",
+                "target": "/"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "efiboot-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.grub2.iso",
             "options": {
               "product": {
                 "name": "Fedora",
                 "version": "37"
               },
-              "kernel": "5.19.3-300.fc37.x86_64",
-              "isolabel": "Fedora-37-BaseOS-x86_64",
-              "efi": {
-                "architectures": [
-                  "X64"
-                ],
-                "vendor": "fedora"
-              },
-              "isolinux": {
-                "enabled": true
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-37-BaseOS-x86_64:/ostree.ks"
+                ]
               },
-              "kernel_opts": "inst.ks=hd:LABEL=Fedora-37-BaseOS-x86_64:/osbuild.ks",
-              "templates": "99-generic",
-              "rootfs": {
-                "compression": {
-                  "method": "xz",
-                  "options": {
-                    "bcj": "x86"
-                  }
+              "isolabel": "Fedora-37-BaseOS-x86_64",
+              "architectures": [
+                "X64"
+              ],
+              "vendor": "fedora"
+            }
+          }
+        ]
+      },
+      {
+        "name": "bootiso-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "images"
                 },
-                "size": 9216
+                {
+                  "path": "images/pxeboot"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
               }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/boot/vmlinuz-5.19.3-300.fc37.x86_64",
+                  "to": "tree:///images/pxeboot/vmlinuz"
+                },
+                {
+                  "from": "input://tree/boot/initramfs-5.19.3-300.fc37.x86_64.img",
+                  "to": "tree:///images/pxeboot/initrd.img"
+                }
+              ]
             }
           },
           {
-            "type": "org.osbuild.kickstart",
+            "type": "org.osbuild.squashfs",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:rootfs-image"
+                ]
+              }
+            },
             "options": {
-              "path": "/osbuild.ks",
-              "ostree": {
-                "osname": "fedora",
-                "url": "file:///run/install/repo/ostree/repo",
-                "ref": "test/iot",
-                "gpg": false
+              "filename": "images/install.img",
+              "compression": {
+                "method": "lz4"
               }
             }
           },
           {
-            "type": "org.osbuild.discinfo",
+            "type": "org.osbuild.isolinux",
+            "inputs": {
+              "data": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
+              }
+            },
             "options": {
-              "basearch": "x86_64",
-              "release": "202010217.n.0"
+              "product": {
+                "name": "Fedora",
+                "version": "37"
+              },
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-37-BaseOS-x86_64:/ostree.ks"
+                ]
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "images/efiboot.img",
+              "size": "20MB"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.fat",
+            "options": {
+              "volid": "0194fdc2"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960,
+                  "lock": true
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/",
+                  "to": "mount://root/"
+                }
+              ]
+            },
+            "devices": {
+              "root": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "root",
+                "type": "org.osbuild.fat",
+                "source": "root",
+                "target": "/"
+              }
+            ]
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/EFI",
+                  "to": "tree:///"
+                }
+              ]
             }
           },
           {
@@ -10897,6 +11097,25 @@
             "options": {
               "repo": "/ostree/repo"
             }
+          },
+          {
+            "type": "org.osbuild.kickstart",
+            "options": {
+              "path": "/ostree.ks",
+              "ostree": {
+                "osname": "fedora",
+                "url": "file:///run/install/repo/ostree/repo",
+                "ref": "test/iot",
+                "gpg": false
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.discinfo",
+            "options": {
+              "basearch": "x86_64",
+              "release": "202010217.n.0"
+            }
           }
         ]
       },
diff --git a/test/data/manifests/fedora_37-x86_64-iot_installer_with_users-boot.json b/test/data/manifests/fedora_37-x86_64-iot_installer_with_users-boot.json
index 1deb4d6e2..a3067192f 100644
--- a/test/data/manifests/fedora_37-x86_64-iot_installer_with_users-boot.json
+++ b/test/data/manifests/fedora_37-x86_64-iot_installer_with_users-boot.json
@@ -10843,13 +10843,45 @@
         ]
       },
       {
-        "name": "bootiso-tree",
+        "name": "rootfs-image",
         "build": "name:build",
         "stages": [
           {
-            "type": "org.osbuild.bootiso.mono",
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "LiveOS"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "LiveOS/rootfs.img",
+              "size": "4294967296"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.ext4",
+            "options": {
+              "uuid": "2fe99653-f7ff-44fd-bea8-fa70107524fb",
+              "label": "Anaconda"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
             "inputs": {
-              "rootfs": {
+              "tree": {
                 "type": "org.osbuild.tree",
                 "origin": "org.osbuild.pipeline",
                 "references": [
@@ -10857,39 +10889,249 @@
                 ]
               }
             },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/",
+                  "to": "mount://device/"
+                }
+              ]
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "device",
+                "type": "org.osbuild.ext4",
+                "source": "device",
+                "target": "/"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "efiboot-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.grub2.iso",
             "options": {
               "product": {
                 "name": "Fedora",
                 "version": "37"
               },
-              "kernel": "5.19.3-300.fc37.x86_64",
-              "isolabel": "Fedora-37-BaseOS-x86_64",
-              "efi": {
-                "architectures": [
-                  "X64"
-                ],
-                "vendor": "fedora"
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-37-BaseOS-x86_64:/ostree.ks"
+                ]
               },
-              "isolinux": {
-                "enabled": true
+              "isolabel": "Fedora-37-BaseOS-x86_64",
+              "architectures": [
+                "X64"
+              ],
+              "vendor": "fedora"
+            }
+          }
+        ]
+      },
+      {
+        "name": "bootiso-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "images"
+                },
+                {
+                  "path": "images/pxeboot"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/boot/vmlinuz-5.19.3-300.fc37.x86_64",
+                  "to": "tree:///images/pxeboot/vmlinuz"
+                },
+                {
+                  "from": "input://tree/boot/initramfs-5.19.3-300.fc37.x86_64.img",
+                  "to": "tree:///images/pxeboot/initrd.img"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.squashfs",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:rootfs-image"
+                ]
+              }
+            },
+            "options": {
+              "filename": "images/install.img",
+              "compression": {
+                "method": "lz4"
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.isolinux",
+            "inputs": {
+              "data": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
+              }
+            },
+            "options": {
+              "product": {
+                "name": "Fedora",
+                "version": "37"
               },
-              "kernel_opts": "inst.ks=hd:LABEL=Fedora-37-BaseOS-x86_64:/osbuild.ks",
-              "templates": "99-generic",
-              "rootfs": {
-                "compression": {
-                  "method": "xz",
-                  "options": {
-                    "bcj": "x86"
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-37-BaseOS-x86_64:/ostree.ks"
+                ]
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "images/efiboot.img",
+              "size": "20MB"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.fat",
+            "options": {
+              "volid": "0194fdc2"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960,
+                  "lock": true
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/",
+                  "to": "mount://root/"
+                }
+              ]
+            },
+            "devices": {
+              "root": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "root",
+                "type": "org.osbuild.fat",
+                "source": "root",
+                "target": "/"
+              }
+            ]
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/EFI",
+                  "to": "tree:///"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.ostree.init",
+            "options": {
+              "path": "/ostree/repo"
+            }
+          },
+          {
+            "type": "org.osbuild.ostree.pull",
+            "inputs": {
+              "commits": {
+                "type": "org.osbuild.ostree",
+                "origin": "org.osbuild.source",
+                "references": {
+                  "test/iot": {
+                    "ref": "test/iot"
                   }
-                },
-                "size": 9216
+                }
               }
+            },
+            "options": {
+              "repo": "/ostree/repo"
             }
           },
           {
             "type": "org.osbuild.kickstart",
             "options": {
-              "path": "/osbuild.ks",
+              "path": "/ostree.ks",
               "ostree": {
                 "osname": "fedora",
                 "url": "file:///run/install/repo/ostree/repo",
@@ -10926,29 +11168,6 @@
               "basearch": "x86_64",
               "release": "202010217.n.0"
             }
-          },
-          {
-            "type": "org.osbuild.ostree.init",
-            "options": {
-              "path": "/ostree/repo"
-            }
-          },
-          {
-            "type": "org.osbuild.ostree.pull",
-            "inputs": {
-              "commits": {
-                "type": "org.osbuild.ostree",
-                "origin": "org.osbuild.source",
-                "references": {
-                  "test/iot": {
-                    "ref": "test/iot"
-                  }
-                }
-              }
-            },
-            "options": {
-              "repo": "/ostree/repo"
-            }
           }
         ]
       },
diff --git a/test/data/manifests/fedora_37-x86_64-iot_raw_image-boot.json b/test/data/manifests/fedora_37-x86_64-iot_raw_image-boot.json
index 16d363241..f69fdb185 100644
--- a/test/data/manifests/fedora_37-x86_64-iot_raw_image-boot.json
+++ b/test/data/manifests/fedora_37-x86_64-iot_raw_image-boot.json
@@ -2075,18 +2075,6 @@
               "osname": "fedora-iot"
             }
           },
-          {
-            "type": "org.osbuild.ostree.config",
-            "options": {
-              "repo": "/ostree/repo",
-              "config": {
-                "sysroot": {
-                  "readonly": true,
-                  "bootloader": "none"
-                }
-              }
-            }
-          },
           {
             "type": "org.osbuild.mkdir",
             "options": {
@@ -2112,7 +2100,8 @@
                 "label": "root"
               },
               "kernel_opts": [
-                "modprobe.blacklist=vc4"
+                "modprobe.blacklist=vc4",
+                "rw"
               ]
             }
           },
@@ -2141,6 +2130,31 @@
               }
             }
           },
+          {
+            "type": "org.osbuild.ostree.config",
+            "options": {
+              "repo": "/ostree/repo",
+              "config": {
+                "sysroot": {
+                  "readonly": true,
+                  "bootloader": "none"
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "ostree-test/fedora/iot",
+                "type": "org.osbuild.ostree.deployment",
+                "options": {
+                  "deployment": {
+                    "osname": "fedora-iot",
+                    "ref": "test/fedora/iot",
+                    "serial": 0
+                  }
+                }
+              }
+            ]
+          },
           {
             "type": "org.osbuild.fstab",
             "options": {
@@ -2250,7 +2264,7 @@
             "options": {
               "root_fs_uuid": "6e4ff95f-f662-45ee-a82a-bdf44a2d0b75",
               "boot_fs_uuid": "0194fdc2-fa2f-4cc0-81d3-ff12045b73c8",
-              "kernel_opts": "modprobe.blacklist=vc4",
+              "kernel_opts": "modprobe.blacklist=vc4,rw",
               "legacy": "i386-pc",
               "uefi": {
                 "vendor": "fedora",
diff --git a/test/data/manifests/fedora_38-aarch64-iot_installer-boot.json b/test/data/manifests/fedora_38-aarch64-iot_installer-boot.json
index b195f29ec..d68295f68 100644
--- a/test/data/manifests/fedora_38-aarch64-iot_installer-boot.json
+++ b/test/data/manifests/fedora_38-aarch64-iot_installer-boot.json
@@ -10604,13 +10604,45 @@
         ]
       },
       {
-        "name": "bootiso-tree",
+        "name": "rootfs-image",
         "build": "name:build",
         "stages": [
           {
-            "type": "org.osbuild.bootiso.mono",
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "LiveOS"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "LiveOS/rootfs.img",
+              "size": "4294967296"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.ext4",
+            "options": {
+              "uuid": "2fe99653-f7ff-44fd-bea8-fa70107524fb",
+              "label": "Anaconda"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
             "inputs": {
-              "rootfs": {
+              "tree": {
                 "type": "org.osbuild.tree",
                 "origin": "org.osbuild.pipeline",
                 "references": [
@@ -10618,52 +10650,220 @@
                 ]
               }
             },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/",
+                  "to": "mount://device/"
+                }
+              ]
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "device",
+                "type": "org.osbuild.ext4",
+                "source": "device",
+                "target": "/"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "efiboot-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.grub2.iso",
             "options": {
               "product": {
                 "name": "Fedora",
                 "version": "38"
               },
-              "kernel": "6.0.0-0.rc1.13.fc38.aarch64",
-              "isolabel": "Fedora-38-BaseOS-aarch64",
-              "efi": {
-                "architectures": [
-                  "AA64"
-                ],
-                "vendor": "fedora"
-              },
-              "isolinux": {
-                "enabled": false
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-38-BaseOS-aarch64:/ostree.ks"
+                ]
               },
-              "kernel_opts": "inst.ks=hd:LABEL=Fedora-38-BaseOS-aarch64:/osbuild.ks",
-              "templates": "99-generic",
-              "rootfs": {
-                "compression": {
-                  "method": "xz",
-                  "options": {
-                    "bcj": "arm"
-                  }
+              "isolabel": "Fedora-38-BaseOS-aarch64",
+              "architectures": [
+                "AA64"
+              ],
+              "vendor": "fedora"
+            }
+          }
+        ]
+      },
+      {
+        "name": "bootiso-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "images"
                 },
-                "size": 9216
+                {
+                  "path": "images/pxeboot"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
               }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/boot/vmlinuz-6.0.0-0.rc1.13.fc38.aarch64",
+                  "to": "tree:///images/pxeboot/vmlinuz"
+                },
+                {
+                  "from": "input://tree/boot/initramfs-6.0.0-0.rc1.13.fc38.aarch64.img",
+                  "to": "tree:///images/pxeboot/initrd.img"
+                }
+              ]
             }
           },
           {
-            "type": "org.osbuild.kickstart",
+            "type": "org.osbuild.squashfs",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:rootfs-image"
+                ]
+              }
+            },
             "options": {
-              "path": "/osbuild.ks",
-              "ostree": {
-                "osname": "fedora",
-                "url": "file:///run/install/repo/ostree/repo",
-                "ref": "test/iot",
-                "gpg": false
+              "filename": "images/install.img",
+              "compression": {
+                "method": "lz4"
               }
             }
           },
           {
-            "type": "org.osbuild.discinfo",
+            "type": "org.osbuild.isolinux",
+            "inputs": {
+              "data": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
+              }
+            },
             "options": {
-              "basearch": "aarch64",
-              "release": "202010217.n.0"
+              "product": {
+                "name": "Fedora",
+                "version": "38"
+              },
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-38-BaseOS-aarch64:/ostree.ks"
+                ]
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "images/efiboot.img",
+              "size": "20MB"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.fat",
+            "options": {
+              "volid": "0194fdc2"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960,
+                  "lock": true
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/",
+                  "to": "mount://root/"
+                }
+              ]
+            },
+            "devices": {
+              "root": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "root",
+                "type": "org.osbuild.fat",
+                "source": "root",
+                "target": "/"
+              }
+            ]
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/EFI",
+                  "to": "tree:///"
+                }
+              ]
             }
           },
           {
@@ -10688,6 +10888,25 @@
             "options": {
               "repo": "/ostree/repo"
             }
+          },
+          {
+            "type": "org.osbuild.kickstart",
+            "options": {
+              "path": "/ostree.ks",
+              "ostree": {
+                "osname": "fedora",
+                "url": "file:///run/install/repo/ostree/repo",
+                "ref": "test/iot",
+                "gpg": false
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.discinfo",
+            "options": {
+              "basearch": "aarch64",
+              "release": "202010217.n.0"
+            }
           }
         ]
       },
diff --git a/test/data/manifests/fedora_38-aarch64-iot_installer_with_users-boot.json b/test/data/manifests/fedora_38-aarch64-iot_installer_with_users-boot.json
index 03d9963b3..df822fc1a 100644
--- a/test/data/manifests/fedora_38-aarch64-iot_installer_with_users-boot.json
+++ b/test/data/manifests/fedora_38-aarch64-iot_installer_with_users-boot.json
@@ -10634,13 +10634,45 @@
         ]
       },
       {
-        "name": "bootiso-tree",
+        "name": "rootfs-image",
         "build": "name:build",
         "stages": [
           {
-            "type": "org.osbuild.bootiso.mono",
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "LiveOS"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "LiveOS/rootfs.img",
+              "size": "4294967296"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.ext4",
+            "options": {
+              "uuid": "2fe99653-f7ff-44fd-bea8-fa70107524fb",
+              "label": "Anaconda"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
             "inputs": {
-              "rootfs": {
+              "tree": {
                 "type": "org.osbuild.tree",
                 "origin": "org.osbuild.pipeline",
                 "references": [
@@ -10648,39 +10680,249 @@
                 ]
               }
             },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/",
+                  "to": "mount://device/"
+                }
+              ]
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "device",
+                "type": "org.osbuild.ext4",
+                "source": "device",
+                "target": "/"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "efiboot-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.grub2.iso",
             "options": {
               "product": {
                 "name": "Fedora",
                 "version": "38"
               },
-              "kernel": "6.0.0-0.rc1.13.fc38.aarch64",
-              "isolabel": "Fedora-38-BaseOS-aarch64",
-              "efi": {
-                "architectures": [
-                  "AA64"
-                ],
-                "vendor": "fedora"
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-38-BaseOS-aarch64:/ostree.ks"
+                ]
               },
-              "isolinux": {
-                "enabled": false
+              "isolabel": "Fedora-38-BaseOS-aarch64",
+              "architectures": [
+                "AA64"
+              ],
+              "vendor": "fedora"
+            }
+          }
+        ]
+      },
+      {
+        "name": "bootiso-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "images"
+                },
+                {
+                  "path": "images/pxeboot"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/boot/vmlinuz-6.0.0-0.rc1.13.fc38.aarch64",
+                  "to": "tree:///images/pxeboot/vmlinuz"
+                },
+                {
+                  "from": "input://tree/boot/initramfs-6.0.0-0.rc1.13.fc38.aarch64.img",
+                  "to": "tree:///images/pxeboot/initrd.img"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.squashfs",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:rootfs-image"
+                ]
+              }
+            },
+            "options": {
+              "filename": "images/install.img",
+              "compression": {
+                "method": "lz4"
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.isolinux",
+            "inputs": {
+              "data": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
+              }
+            },
+            "options": {
+              "product": {
+                "name": "Fedora",
+                "version": "38"
               },
-              "kernel_opts": "inst.ks=hd:LABEL=Fedora-38-BaseOS-aarch64:/osbuild.ks",
-              "templates": "99-generic",
-              "rootfs": {
-                "compression": {
-                  "method": "xz",
-                  "options": {
-                    "bcj": "arm"
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-38-BaseOS-aarch64:/ostree.ks"
+                ]
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "images/efiboot.img",
+              "size": "20MB"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.fat",
+            "options": {
+              "volid": "0194fdc2"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960,
+                  "lock": true
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/",
+                  "to": "mount://root/"
+                }
+              ]
+            },
+            "devices": {
+              "root": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "root",
+                "type": "org.osbuild.fat",
+                "source": "root",
+                "target": "/"
+              }
+            ]
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/EFI",
+                  "to": "tree:///"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.ostree.init",
+            "options": {
+              "path": "/ostree/repo"
+            }
+          },
+          {
+            "type": "org.osbuild.ostree.pull",
+            "inputs": {
+              "commits": {
+                "type": "org.osbuild.ostree",
+                "origin": "org.osbuild.source",
+                "references": {
+                  "test/iot": {
+                    "ref": "test/iot"
                   }
-                },
-                "size": 9216
+                }
               }
+            },
+            "options": {
+              "repo": "/ostree/repo"
             }
           },
           {
             "type": "org.osbuild.kickstart",
             "options": {
-              "path": "/osbuild.ks",
+              "path": "/ostree.ks",
               "ostree": {
                 "osname": "fedora",
                 "url": "file:///run/install/repo/ostree/repo",
@@ -10717,29 +10959,6 @@
               "basearch": "aarch64",
               "release": "202010217.n.0"
             }
-          },
-          {
-            "type": "org.osbuild.ostree.init",
-            "options": {
-              "path": "/ostree/repo"
-            }
-          },
-          {
-            "type": "org.osbuild.ostree.pull",
-            "inputs": {
-              "commits": {
-                "type": "org.osbuild.ostree",
-                "origin": "org.osbuild.source",
-                "references": {
-                  "test/iot": {
-                    "ref": "test/iot"
-                  }
-                }
-              }
-            },
-            "options": {
-              "repo": "/ostree/repo"
-            }
           }
         ]
       },
diff --git a/test/data/manifests/fedora_38-aarch64-iot_raw_image-boot.json b/test/data/manifests/fedora_38-aarch64-iot_raw_image-boot.json
index baddae70a..d9972a68f 100644
--- a/test/data/manifests/fedora_38-aarch64-iot_raw_image-boot.json
+++ b/test/data/manifests/fedora_38-aarch64-iot_raw_image-boot.json
@@ -2043,18 +2043,6 @@
               "osname": "fedora-iot"
             }
           },
-          {
-            "type": "org.osbuild.ostree.config",
-            "options": {
-              "repo": "/ostree/repo",
-              "config": {
-                "sysroot": {
-                  "readonly": true,
-                  "bootloader": "none"
-                }
-              }
-            }
-          },
           {
             "type": "org.osbuild.mkdir",
             "options": {
@@ -2080,7 +2068,8 @@
                 "label": "root"
               },
               "kernel_opts": [
-                "modprobe.blacklist=vc4"
+                "modprobe.blacklist=vc4",
+                "rw"
               ]
             }
           },
@@ -2109,6 +2098,31 @@
               }
             }
           },
+          {
+            "type": "org.osbuild.ostree.config",
+            "options": {
+              "repo": "/ostree/repo",
+              "config": {
+                "sysroot": {
+                  "readonly": true,
+                  "bootloader": "none"
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "ostree-test/fedora/iot",
+                "type": "org.osbuild.ostree.deployment",
+                "options": {
+                  "deployment": {
+                    "osname": "fedora-iot",
+                    "ref": "test/fedora/iot",
+                    "serial": 0
+                  }
+                }
+              }
+            ]
+          },
           {
             "type": "org.osbuild.fstab",
             "options": {
@@ -2218,7 +2232,7 @@
             "options": {
               "root_fs_uuid": "6e4ff95f-f662-45ee-a82a-bdf44a2d0b75",
               "boot_fs_uuid": "0194fdc2-fa2f-4cc0-81d3-ff12045b73c8",
-              "kernel_opts": "modprobe.blacklist=vc4",
+              "kernel_opts": "modprobe.blacklist=vc4,rw",
               "uefi": {
                 "vendor": "fedora",
                 "install": true,
diff --git a/test/data/manifests/fedora_38-x86_64-iot_installer-boot.json b/test/data/manifests/fedora_38-x86_64-iot_installer-boot.json
index 1b3c8f8af..7d6739400 100644
--- a/test/data/manifests/fedora_38-x86_64-iot_installer-boot.json
+++ b/test/data/manifests/fedora_38-x86_64-iot_installer-boot.json
@@ -10789,13 +10789,45 @@
         ]
       },
       {
-        "name": "bootiso-tree",
+        "name": "rootfs-image",
         "build": "name:build",
         "stages": [
           {
-            "type": "org.osbuild.bootiso.mono",
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "LiveOS"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "LiveOS/rootfs.img",
+              "size": "4294967296"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.ext4",
+            "options": {
+              "uuid": "2fe99653-f7ff-44fd-bea8-fa70107524fb",
+              "label": "Anaconda"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
             "inputs": {
-              "rootfs": {
+              "tree": {
                 "type": "org.osbuild.tree",
                 "origin": "org.osbuild.pipeline",
                 "references": [
@@ -10803,52 +10835,220 @@
                 ]
               }
             },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/",
+                  "to": "mount://device/"
+                }
+              ]
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "device",
+                "type": "org.osbuild.ext4",
+                "source": "device",
+                "target": "/"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "efiboot-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.grub2.iso",
             "options": {
               "product": {
                 "name": "Fedora",
                 "version": "38"
               },
-              "kernel": "6.0.0-0.rc1.13.fc38.x86_64",
-              "isolabel": "Fedora-38-BaseOS-x86_64",
-              "efi": {
-                "architectures": [
-                  "X64"
-                ],
-                "vendor": "fedora"
-              },
-              "isolinux": {
-                "enabled": true
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-38-BaseOS-x86_64:/ostree.ks"
+                ]
               },
-              "kernel_opts": "inst.ks=hd:LABEL=Fedora-38-BaseOS-x86_64:/osbuild.ks",
-              "templates": "99-generic",
-              "rootfs": {
-                "compression": {
-                  "method": "xz",
-                  "options": {
-                    "bcj": "x86"
-                  }
+              "isolabel": "Fedora-38-BaseOS-x86_64",
+              "architectures": [
+                "X64"
+              ],
+              "vendor": "fedora"
+            }
+          }
+        ]
+      },
+      {
+        "name": "bootiso-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "images"
                 },
-                "size": 9216
+                {
+                  "path": "images/pxeboot"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
               }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/boot/vmlinuz-6.0.0-0.rc1.13.fc38.x86_64",
+                  "to": "tree:///images/pxeboot/vmlinuz"
+                },
+                {
+                  "from": "input://tree/boot/initramfs-6.0.0-0.rc1.13.fc38.x86_64.img",
+                  "to": "tree:///images/pxeboot/initrd.img"
+                }
+              ]
             }
           },
           {
-            "type": "org.osbuild.kickstart",
+            "type": "org.osbuild.squashfs",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:rootfs-image"
+                ]
+              }
+            },
             "options": {
-              "path": "/osbuild.ks",
-              "ostree": {
-                "osname": "fedora",
-                "url": "file:///run/install/repo/ostree/repo",
-                "ref": "test/iot",
-                "gpg": false
+              "filename": "images/install.img",
+              "compression": {
+                "method": "lz4"
               }
             }
           },
           {
-            "type": "org.osbuild.discinfo",
+            "type": "org.osbuild.isolinux",
+            "inputs": {
+              "data": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
+              }
+            },
             "options": {
-              "basearch": "x86_64",
-              "release": "202010217.n.0"
+              "product": {
+                "name": "Fedora",
+                "version": "38"
+              },
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-38-BaseOS-x86_64:/ostree.ks"
+                ]
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "images/efiboot.img",
+              "size": "20MB"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.fat",
+            "options": {
+              "volid": "0194fdc2"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960,
+                  "lock": true
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/",
+                  "to": "mount://root/"
+                }
+              ]
+            },
+            "devices": {
+              "root": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "root",
+                "type": "org.osbuild.fat",
+                "source": "root",
+                "target": "/"
+              }
+            ]
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/EFI",
+                  "to": "tree:///"
+                }
+              ]
             }
           },
           {
@@ -10873,6 +11073,25 @@
             "options": {
               "repo": "/ostree/repo"
             }
+          },
+          {
+            "type": "org.osbuild.kickstart",
+            "options": {
+              "path": "/ostree.ks",
+              "ostree": {
+                "osname": "fedora",
+                "url": "file:///run/install/repo/ostree/repo",
+                "ref": "test/iot",
+                "gpg": false
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.discinfo",
+            "options": {
+              "basearch": "x86_64",
+              "release": "202010217.n.0"
+            }
           }
         ]
       },
diff --git a/test/data/manifests/fedora_38-x86_64-iot_installer_with_users-boot.json b/test/data/manifests/fedora_38-x86_64-iot_installer_with_users-boot.json
index 67130cc86..d5f1e8ba8 100644
--- a/test/data/manifests/fedora_38-x86_64-iot_installer_with_users-boot.json
+++ b/test/data/manifests/fedora_38-x86_64-iot_installer_with_users-boot.json
@@ -10819,13 +10819,45 @@
         ]
       },
       {
-        "name": "bootiso-tree",
+        "name": "rootfs-image",
         "build": "name:build",
         "stages": [
           {
-            "type": "org.osbuild.bootiso.mono",
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "LiveOS"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "LiveOS/rootfs.img",
+              "size": "4294967296"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.ext4",
+            "options": {
+              "uuid": "2fe99653-f7ff-44fd-bea8-fa70107524fb",
+              "label": "Anaconda"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
             "inputs": {
-              "rootfs": {
+              "tree": {
                 "type": "org.osbuild.tree",
                 "origin": "org.osbuild.pipeline",
                 "references": [
@@ -10833,39 +10865,249 @@
                 ]
               }
             },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/",
+                  "to": "mount://device/"
+                }
+              ]
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "LiveOS/rootfs.img"
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "device",
+                "type": "org.osbuild.ext4",
+                "source": "device",
+                "target": "/"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "efiboot-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.grub2.iso",
             "options": {
               "product": {
                 "name": "Fedora",
                 "version": "38"
               },
-              "kernel": "6.0.0-0.rc1.13.fc38.x86_64",
-              "isolabel": "Fedora-38-BaseOS-x86_64",
-              "efi": {
-                "architectures": [
-                  "X64"
-                ],
-                "vendor": "fedora"
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-38-BaseOS-x86_64:/ostree.ks"
+                ]
               },
-              "isolinux": {
-                "enabled": true
+              "isolabel": "Fedora-38-BaseOS-x86_64",
+              "architectures": [
+                "X64"
+              ],
+              "vendor": "fedora"
+            }
+          }
+        ]
+      },
+      {
+        "name": "bootiso-tree",
+        "build": "name:build",
+        "stages": [
+          {
+            "type": "org.osbuild.mkdir",
+            "options": {
+              "paths": [
+                {
+                  "path": "images"
+                },
+                {
+                  "path": "images/pxeboot"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://tree/boot/vmlinuz-6.0.0-0.rc1.13.fc38.x86_64",
+                  "to": "tree:///images/pxeboot/vmlinuz"
+                },
+                {
+                  "from": "input://tree/boot/initramfs-6.0.0-0.rc1.13.fc38.x86_64.img",
+                  "to": "tree:///images/pxeboot/initrd.img"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.squashfs",
+            "inputs": {
+              "tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:rootfs-image"
+                ]
+              }
+            },
+            "options": {
+              "filename": "images/install.img",
+              "compression": {
+                "method": "lz4"
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.isolinux",
+            "inputs": {
+              "data": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:anaconda-tree"
+                ]
+              }
+            },
+            "options": {
+              "product": {
+                "name": "Fedora",
+                "version": "38"
               },
-              "kernel_opts": "inst.ks=hd:LABEL=Fedora-38-BaseOS-x86_64:/osbuild.ks",
-              "templates": "99-generic",
-              "rootfs": {
-                "compression": {
-                  "method": "xz",
-                  "options": {
-                    "bcj": "x86"
+              "kernel": {
+                "dir": "/images/pxeboot",
+                "opts": [
+                  "inst.ks=hd:LABEL=Fedora-38-BaseOS-x86_64:/ostree.ks"
+                ]
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.truncate",
+            "options": {
+              "filename": "images/efiboot.img",
+              "size": "20MB"
+            }
+          },
+          {
+            "type": "org.osbuild.mkfs.fat",
+            "options": {
+              "volid": "0194fdc2"
+            },
+            "devices": {
+              "device": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960,
+                  "lock": true
+                }
+              }
+            }
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/",
+                  "to": "mount://root/"
+                }
+              ]
+            },
+            "devices": {
+              "root": {
+                "type": "org.osbuild.loopback",
+                "options": {
+                  "filename": "images/efiboot.img",
+                  "size": 40960
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "root",
+                "type": "org.osbuild.fat",
+                "source": "root",
+                "target": "/"
+              }
+            ]
+          },
+          {
+            "type": "org.osbuild.copy",
+            "inputs": {
+              "root-tree": {
+                "type": "org.osbuild.tree",
+                "origin": "org.osbuild.pipeline",
+                "references": [
+                  "name:efiboot-tree"
+                ]
+              }
+            },
+            "options": {
+              "paths": [
+                {
+                  "from": "input://root-tree/EFI",
+                  "to": "tree:///"
+                }
+              ]
+            }
+          },
+          {
+            "type": "org.osbuild.ostree.init",
+            "options": {
+              "path": "/ostree/repo"
+            }
+          },
+          {
+            "type": "org.osbuild.ostree.pull",
+            "inputs": {
+              "commits": {
+                "type": "org.osbuild.ostree",
+                "origin": "org.osbuild.source",
+                "references": {
+                  "test/iot": {
+                    "ref": "test/iot"
                   }
-                },
-                "size": 9216
+                }
               }
+            },
+            "options": {
+              "repo": "/ostree/repo"
             }
           },
           {
             "type": "org.osbuild.kickstart",
             "options": {
-              "path": "/osbuild.ks",
+              "path": "/ostree.ks",
               "ostree": {
                 "osname": "fedora",
                 "url": "file:///run/install/repo/ostree/repo",
@@ -10902,29 +11144,6 @@
               "basearch": "x86_64",
               "release": "202010217.n.0"
             }
-          },
-          {
-            "type": "org.osbuild.ostree.init",
-            "options": {
-              "path": "/ostree/repo"
-            }
-          },
-          {
-            "type": "org.osbuild.ostree.pull",
-            "inputs": {
-              "commits": {
-                "type": "org.osbuild.ostree",
-                "origin": "org.osbuild.source",
-                "references": {
-                  "test/iot": {
-                    "ref": "test/iot"
-                  }
-                }
-              }
-            },
-            "options": {
-              "repo": "/ostree/repo"
-            }
           }
         ]
       },
diff --git a/test/data/manifests/fedora_38-x86_64-iot_raw_image-boot.json b/test/data/manifests/fedora_38-x86_64-iot_raw_image-boot.json
index 194b1fdde..6b61eafda 100644
--- a/test/data/manifests/fedora_38-x86_64-iot_raw_image-boot.json
+++ b/test/data/manifests/fedora_38-x86_64-iot_raw_image-boot.json
@@ -2067,18 +2067,6 @@
               "osname": "fedora-iot"
             }
           },
-          {
-            "type": "org.osbuild.ostree.config",
-            "options": {
-              "repo": "/ostree/repo",
-              "config": {
-                "sysroot": {
-                  "readonly": true,
-                  "bootloader": "none"
-                }
-              }
-            }
-          },
           {
             "type": "org.osbuild.mkdir",
             "options": {
@@ -2104,7 +2092,8 @@
                 "label": "root"
               },
               "kernel_opts": [
-                "modprobe.blacklist=vc4"
+                "modprobe.blacklist=vc4",
+                "rw"
               ]
             }
           },
@@ -2133,6 +2122,31 @@
               }
             }
           },
+          {
+            "type": "org.osbuild.ostree.config",
+            "options": {
+              "repo": "/ostree/repo",
+              "config": {
+                "sysroot": {
+                  "readonly": true,
+                  "bootloader": "none"
+                }
+              }
+            },
+            "mounts": [
+              {
+                "name": "ostree-test/fedora/iot",
+                "type": "org.osbuild.ostree.deployment",
+                "options": {
+                  "deployment": {
+                    "osname": "fedora-iot",
+                    "ref": "test/fedora/iot",
+                    "serial": 0
+                  }
+                }
+              }
+            ]
+          },
           {
             "type": "org.osbuild.fstab",
             "options": {
@@ -2242,7 +2256,7 @@
             "options": {
               "root_fs_uuid": "6e4ff95f-f662-45ee-a82a-bdf44a2d0b75",
               "boot_fs_uuid": "0194fdc2-fa2f-4cc0-81d3-ff12045b73c8",
-              "kernel_opts": "modprobe.blacklist=vc4",
+              "kernel_opts": "modprobe.blacklist=vc4,rw",
               "legacy": "i386-pc",
               "uefi": {
                 "vendor": "fedora",