diff --git a/roles/fetch-javascript-tarball/README.rst b/roles/fetch-javascript-tarball/README.rst
index 10df25868..a09046acf 100644
--- a/roles/fetch-javascript-tarball/README.rst
+++ b/roles/fetch-javascript-tarball/README.rst
@@ -6,3 +6,11 @@ Fetch a Javascript tarball back to be published.
    :default: {{ zuul.project.src_dir }}
 
    Directory to run npm in.
+
+.. zuul:rolevar:: zuul_use_fetch_output
+   :default: false
+
+   Whether to synchronize files to the executor work dir, or to copy them
+   on the test instance.
+   When set to false, the role synchronizes the file to the executor.
+   When set to true, the job needs to use the fetch-output role later.
diff --git a/roles/fetch-javascript-tarball/defaults/main.yaml b/roles/fetch-javascript-tarball/defaults/main.yaml
index 9739eb171..3de4191ec 100644
--- a/roles/fetch-javascript-tarball/defaults/main.yaml
+++ b/roles/fetch-javascript-tarball/defaults/main.yaml
@@ -1 +1,3 @@
 zuul_work_dir: "{{ zuul.project.src_dir }}"
+zuul_output_dir: "{{ ansible_user_dir }}/zuul-output"
+zuul_use_fetch_output: "{{ zuul_site_use_fetch_output|default(false) }}"
diff --git a/roles/fetch-javascript-tarball/tasks/main.yaml b/roles/fetch-javascript-tarball/tasks/main.yaml
index 20bafcc6e..4906115ce 100644
--- a/roles/fetch-javascript-tarball/tasks/main.yaml
+++ b/roles/fetch-javascript-tarball/tasks/main.yaml
@@ -1,23 +1,25 @@
 - name: Rename tarball for uploading
   shell: |
-    mkdir -p dist
-    cp *.tgz dist/{{ zuul.project.short_name }}-{{ project_ver }}.tar.gz
-    cp *.tgz dist/{{ zuul.project.short_name }}-latest.tar.gz
+    mkdir -p {{ zuul_output_dir }}/artifacts
+    cp *.tgz {{ zuul_output_dir }}/artifacts/{{ zuul.project.short_name }}-{{ project_ver }}.tar.gz
+    cp *.tgz {{ zuul_output_dir }}/artifacts/{{ zuul.project.short_name }}-latest.tar.gz
   args:
     chdir: "{{ zuul_work_dir }}"
   tags:
     # Ignore ANSIBLE0007: No need to use file module instead of mkdir
     - skip_ansible_lint
 
-- name: Ensure artifacts directory exists
-  file:
-    path: "{{ zuul.executor.work_root }}/artifacts"
-    state: directory
-  delegate_to: localhost
+- block:
+    - name: Ensure artifacts directory exists
+      file:
+        path: "{{ zuul.executor.work_root }}/artifacts"
+        state: directory
+      delegate_to: localhost
 
-- name: Collect artifacts
-  synchronize:
-    dest: "{{ zuul.executor.work_root }}/artifacts/"
-    mode: pull
-    src: "{{ zuul_work_dir }}/dist/"
-    verify_host: true
+    - name: Collect artifacts
+      synchronize:
+        dest: "{{ zuul.executor.work_root }}/artifacts/"
+        mode: pull
+        src: "{{ zuul_output_dir }}/artifacts/"
+        verify_host: true
+  when: not zuul_use_fetch_output
diff --git a/test-playbooks/javascripts/fetch-javascript-tarball.yaml b/test-playbooks/javascripts/fetch-javascript-tarball.yaml
new file mode 100644
index 000000000..c05cc4d3c
--- /dev/null
+++ b/test-playbooks/javascripts/fetch-javascript-tarball.yaml
@@ -0,0 +1,26 @@
+- hosts: all
+  pre_tasks:
+    - name: Create fake tarball
+      shell: |
+        mkdir -p {{ zuul.project.src_dir }}
+        tar czf {{ zuul.project.src_dir }}/dist.tgz /etc/os-release
+  tasks:
+    - import_role:
+        name: fetch-javascript-tarball
+      vars:
+        project_ver: master
+    - import_role:
+        name: fetch-output
+      when: zuul_use_fetch_output
+  post_tasks:
+    - name: Check for artifact on the test instance
+      stat:
+        path: "{{ ansible_user_dir }}/zuul-output/artifacts/zuul-jobs-latest.tar.gz"
+      register: _test_artifact
+      failed_when: not _test_artifact.stat.exists
+    - name: Check for artifact on the executor
+      stat:
+        path: "{{ zuul.executor.work_root }}/artifacts/zuul-jobs-latest.tar.gz"
+      delegate_to: localhost
+      register: _executor_artifact
+      failed_when: not _executor_artifact.stat.exists
diff --git a/zuul-tests.d/js-roles-jobs.yaml b/zuul-tests.d/js-roles-jobs.yaml
index a6cfd14ba..a6a7d71d3 100644
--- a/zuul-tests.d/js-roles-jobs.yaml
+++ b/zuul-tests.d/js-roles-jobs.yaml
@@ -7,6 +7,26 @@
     vars:
       role_name: install-nodejs
 
+
+- job:
+    name: zuul-jobs-test-fetch-javascript-tarball
+    description: Test the fetch-javascript-tarball
+    files:
+      - roles/fetch-javascript-tarball/.*
+      - roles/fetch-output/.*
+    run: test-playbooks/javascripts/fetch-javascript-tarball.yaml
+    vars:
+      zuul_use_fetch_output: true
+
+- job:
+    name: zuul-jobs-test-fetch-javascript-tarball-synchronize
+    description: Test the fetch-javascript-tarball without fetch-output
+    files:
+      - roles/fetch-javascript-tarball/.*
+    run: test-playbooks/javascripts/fetch-javascript-tarball.yaml
+    vars:
+      zuul_use_fetch_output: false
+
 # -* AUTOGENERATED *-
 #  The following project section is autogenerated by
 #    tools/update-test-platforms.py
@@ -16,5 +36,7 @@
     check:
       jobs: &id001
         - zuul-jobs-test-install-nodejs
+        - zuul-jobs-test-fetch-javascript-tarball
+        - zuul-jobs-test-fetch-javascript-tarball-synchronize
     gate:
       jobs: *id001