diff --git a/modules/jenkins_jobs/files/modules/builders.py b/modules/jenkins_jobs/files/modules/builders.py
index 5a19cbca73..61110df6d7 100644
--- a/modules/jenkins_jobs/files/modules/builders.py
+++ b/modules/jenkins_jobs/files/modules/builders.py
@@ -100,6 +100,9 @@ for f in `find . -iname *.erb` ; do
 done
 """)
 
+    def _builder_selenium(self, xml_parent):
+        self._add_script(xml_parent, '/usr/local/jenkins/slave_scripts/run-selenium.sh')
+
     def _builder_shell(self, xml_parent, data):
         self._add_script(xml_parent, data)
 
diff --git a/modules/jenkins_jobs/files/projects/openstack/horizon.yml b/modules/jenkins_jobs/files/projects/openstack/horizon.yml
index 69c597b1f8..4f1fbdcf83 100644
--- a/modules/jenkins_jobs/files/projects/openstack/horizon.yml
+++ b/modules/jenkins_jobs/files/projects/openstack/horizon.yml
@@ -21,3 +21,31 @@ values:
   tarball_project: 'horizon'
   doc_publisher_site: 'docs.openstack.org'
   node: 'precise'
+
+---
+# gate-horizon-selenium
+main:
+  name: 'gate-horizon-selenium'
+  review_site: 'review.openstack.org'
+  github_org: 'openstack'
+  project: 'horizon'
+  concurrent: 'true'
+
+logrotate:
+  daysToKeep: 28
+  numToKeep: -1
+  artifactDaysToKeep: -1
+  artifactNumToKeep: -1
+
+triggers:
+  - zuul
+
+builders:
+  - gerrit_git_prep
+  - selenium
+
+scm:
+  scm: 'false'
+
+assignednode:
+  node: 'precise'
diff --git a/modules/jenkins_slave/files/slave_scripts/run-selenium.sh b/modules/jenkins_slave/files/slave_scripts/run-selenium.sh
new file mode 100755
index 0000000000..7bd248c2b6
--- /dev/null
+++ b/modules/jenkins_slave/files/slave_scripts/run-selenium.sh
@@ -0,0 +1,28 @@
+#!/bin/bash -xe
+
+# If a bundle file is present, call tox with the jenkins version of
+# the test environment so it is used.  Otherwise, use the normal
+# (non-bundle) test environment.  Also, run pip freeze on the
+# resulting environment at the end so that we have a record of exactly
+# what packages we ended up testing.
+#
+
+venv=venv
+
+VDISPLAY=99
+DIMENSIONS='1280x1024x24'
+/usr/bin/Xvfb :${VDISPLAY} -screen 0 ${DIMENSIONS} 2>&1 > /dev/null &
+
+set +e
+DISPLAY=:${VDISPLAY} tox -e$venv -- /bin/bash run_tests.sh -N --with-selenium
+result=$?
+
+pkill Xvfb 2>&1 > /dev/null
+set -e
+
+echo "Begin pip freeze output from test virtualenv:"
+echo "======================================================================"
+.tox/$venv/bin/pip freeze
+echo "======================================================================"
+
+exit $result
diff --git a/modules/jenkins_slave/manifests/init.pp b/modules/jenkins_slave/manifests/init.pp
index 952a040af6..9c47d7f549 100644
--- a/modules/jenkins_slave/manifests/init.pp
+++ b/modules/jenkins_slave/manifests/init.pp
@@ -32,6 +32,7 @@ class jenkins_slave($ssh_key, $sudo = false, $bare = false, $user = true) {
                  "docbook5-xml", # for building openstack docs
                  "docbook-xsl", # for building openstack docs
                  "ebtables",
+                 "firefox", # for selenium tests
                  "gawk",
                  "graphviz",
                  "iptables",
@@ -73,6 +74,7 @@ class jenkins_slave($ssh_key, $sudo = false, $bare = false, $user = true) {
                  "vlan",
                  "wget",
                  "xsltproc", # for building openstack docs
+                 "xvfb", # for selenium tests
                  "pyflakes"]
 
     if ($bare == false) {
diff --git a/modules/openstack_project/files/zuul/layout.yaml b/modules/openstack_project/files/zuul/layout.yaml
index 6caec503f0..cc90851e90 100644
--- a/modules/openstack_project/files/zuul/layout.yaml
+++ b/modules/openstack_project/files/zuul/layout.yaml
@@ -169,6 +169,7 @@ projects:
         - gate-horizon-pep8
         - gate-horizon-python26
         - gate-horizon-python27
+        - gate-horizon-selenium
         - gate-tempest-devstack-vm
     gate:
       - gate-horizon-merge: