diff --git a/.ddev/homeadditions/bin/generate-composer-json b/.ddev/homeadditions/bin/generate-composer-json
index 8762732db5683a28dc66a780729d90f0fe194b9f..554d18dc45e5f96de1f7dcc0a5f19ceb646be2c8 100755
--- a/.ddev/homeadditions/bin/generate-composer-json
+++ b/.ddev/homeadditions/bin/generate-composer-json
@@ -16,7 +16,7 @@ $data = array_merge_recursive(
 );
 
 // If in a CI environment, make all path repository URLs absolute.
-if (getenv('CI')) {
+if (getenv('CI') || getenv('TUGBOAT_PREVIEW')) {
   array_walk($data['repositories'], function (array &$repository): void {
     if ($repository['type'] === 'path') {
       $repository['url'] = getcwd() . '/' . $repository['url'];
diff --git a/.tugboat/config.yml b/.tugboat/config.yml
index b04e1db8258c02b053bd9c08585c7996363cef46..ddef083f35bc3cd01ef03b8d5327ed38ec868579 100644
--- a/.tugboat/config.yml
+++ b/.tugboat/config.yml
@@ -9,17 +9,19 @@ services:
         - docker-php-ext-install opcache
         - a2enmod headers rewrite
       update:
-        # There is currently an issue with Composer 2.8.1, so we install 2.8.0.
-        # TODO: remove this line when the upstream Composer issue is resolved.
-        - composer self-update 2.8.0
-        # Add the components as globally available path repositories, simulating Packagist.
-        - 'sed "s|\"url\": \"|\"url\": \"$TUGBOAT_ROOT/|g" components.composer.json > $(composer config --global home)/config.json'
         # Add the project template as a globally available path repository, simulating Packagist.
         - composer config --global repositories.template path $TUGBOAT_ROOT/project_template
         # Clean up from any previous build.
         - rm -Rf $TUGBOAT_ROOT/project
         # Create the project.
-        - composer create-project drupal/cms $TUGBOAT_ROOT/project --stability=dev
+        - composer create-project drupal/cms $TUGBOAT_ROOT/project --stability=dev --no-install
+        # Generate `composer.json` by merging our dev requirements into the project template.
+        - .ddev/homeadditions/bin/generate-composer-json > $TUGBOAT_ROOT/project/composer.json
+        # TODO: remove this when https://www.drupal.org/project/drupal_cms/issues/3497781 lands
+        # Temporary workaround to bypass scaffolding the XB demo
+        - composer config --working-dir=$TUGBOAT_ROOT/project --unset extra.drupal-scaffold.file-mapping
+        # Run composer install with the new composer.json
+        - composer install --working-dir=$TUGBOAT_ROOT/project --optimize-autoloader
         # Symlink the Drupal root to the Apache web root.
         - ln -snf $TUGBOAT_ROOT/project/web $DOCROOT
         # Create the files directory and settings.php files with appropriate perms.
@@ -32,7 +34,6 @@ services:
         - chmod 2775 $TUGBOAT_ROOT/project_template
         - chgrp -R www-data $TUGBOAT_ROOT/project_template
         - chmod g+w $TUGBOAT_ROOT/project_template/web
-        - chmod g+w $TUGBOAT_ROOT/project_template/vendor
         # Disable PHP memory limit for Drush.
         - |
           echo "if (PHP_SAPI === 'cli') {