diff --git a/README b/README
index b45058da..51243974 100644
--- a/README
+++ b/README
@@ -423,6 +423,17 @@ HostGroupFlags_<group>
 DebianPreseed
    Text to add to the debian-installer preseed file.  Optional.
 
+FreeBSDDist
+   Path to the folder that contains the FreeBSD install image and
+   the FreeBSD compressed install sets, together with the MANIFEST
+   file that holds the checksums. This is required in order to run
+   a FreeBSD host install if no previous FreeBSD buildjob is
+   available (ie: for example when running in standalone mode).
+
+FreeBSDVersion
+   Numeric value holding the major FreeBSD version of the media
+   provided in FreeBSDDist (ie: 12).
+
 ========================================
 
 Config settings relevant only to standalone mode
diff --git a/ap-common b/ap-common
index cbb815ce..d4fa7aef 100644
--- a/ap-common
+++ b/ap-common
@@ -37,6 +37,10 @@
 : ${PUSH_TREE_XTF:=$XENBITS:/home/xen/git/xtf.git}
 : ${BASE_TREE_XTF:=git://xenbits.xen.org/xtf.git}
 
+: ${TREE_FREEBSD:=git://github.com/freebsd/freebsd.git}
+: ${PUSH_TREE_FREEBSD:=$XENBITS:/home/xen/git/freebsd.git}
+: ${BASE_TREE_FREEBSD:=git://xenbits.xen.org/freebsd.git}
+
 : ${TREE_LIBVIRT:=git://libvirt.org/libvirt.git}
 : ${PUSH_TREE_LIBVIRT:=$XENBITS:/home/xen/git/libvirt.git}
 : ${BASE_TREE_LIBVIRT:=git://xenbits.xen.org/libvirt.git}
diff --git a/cri-getconfig b/cri-getconfig
index b2c91ac7..b7f7ae0c 100644
--- a/cri-getconfig
+++ b/cri-getconfig
@@ -25,6 +25,43 @@ getconfig () {
         '
 }
 
+# Get the path to a stashed file from another job.
+#
+# $1: current flight
+# $2: current job
+# $3: name of the stashed file
+# $4: flight (optional) and job where to fetch the stashed file from
+#     (in $flight.$job format or $job).
+getstashed() {
+	perl -e '
+		use Osstest;
+		use Osstest::TestSupport;
+		csreadconfig();
+		$flight = "'$1'";
+		$job = "'$2'";
+		print get_stashed("'$3'", "'$4'") or die $!;
+	'
+}
+
+# Get a runvar from another job.
+#
+# $1: current flight
+# $2: current job
+# $3: name of the runvar
+# $4: flight (optional) and job where to fetch the runvar from
+#     (in $flight.$job format or $job).
+
+getrunvar() {
+	perl -e '
+		use Osstest;
+		use Osstest::TestSupport;
+		csreadconfig();
+		$flight = "'$1'";
+		$job = "'$2'";
+		print get_runvar("'$3'", "'$4'") or die $!;
+	'
+}
+
 getconfig_TftpDiVersion_suite () {
 	perl -e '
 		use Osstest;
diff --git a/mfi-common b/mfi-common
index ec31e2ef..5b7d1887 100644
--- a/mfi-common
+++ b/mfi-common
@@ -96,6 +96,51 @@ set_hostos_runvars () {
   esac
 }
 
+set_freebsd_build_job_flags () {
+    arch=$1
+    job="build-$arch-freebsd"
+
+    # Figure out where are the installer binaries. The order is the following:
+    #
+    # 1. Env variable FREEBSD_BUILDJOB: use the output from a previous
+    # build-<arch>-freebsd.
+    #
+    # 2. Env variables FREEBSD_DIST, FREEBSD_VERSION: set
+    # before calling into make-flight, provide the path to the installer image,
+    # the sets to install and the version being installed.
+    #
+    # 3. Config file FreeBSDDist, FreeBSDVersion: same as 2. except that they
+    # are set on the config file.
+    #
+    # This is slightly convoluted because in order to fetch a runvar or stashed
+    # file osstest needs a current flight and job, so the FreeBSD build flight
+    # is created with missing runvars, then this newly created job is used to
+    # fetch runvars from other jobs if needed, and finally the missing runvars
+    # in the newly created flight are set using cs-adjust-flight.
+    if [ -n "$FREEBSD_BUILDJOB" ]; then
+        distpath=`getstashed $flight $job "path_freebsddist" $FREEBSD_BUILDJOB`
+        version=`getrunvar $flight $job "freebsd_version" $FREEBSD_BUILDJOB`
+        ./cs-adjust-flight $flight runvar-set $job freebsdbuildjob \
+                           $FREEBSD_BUILDJOB
+    elif [ -n "$FREEBSD_DIST" ] && [ -n "$FREEBSD_VERSION" ]; then
+        distpath="$FREEBSD_DIST/install.img"
+        version=$FREEBSD_VERSION
+        ./cs-adjust-flight $flight runvar-set $job freebsd_distpath $distpath
+    else
+        distpath=`getconfig "FreeBSDDist"`
+        version=`getconfig "FreeBSDVersion"`
+        ./cs-adjust-flight $flight runvar-set $job freebsd_distpath $distpath
+    fi
+
+    image="$distpath/install.img"
+    hash=`sha256sum $image | head -c 16`
+    ./cs-adjust-flight $flight runvar-set $job                          \
+        host_hostflags                                                  \
+        share-build-freebsd-$arch-$hash,arch-$arch,freebsd-$version,purpose-build
+
+    echo $version
+}
+
 create_build_jobs () {
 
   local arch
@@ -325,6 +370,26 @@ create_build_jobs () {
 
     fi
 
+    case $arch in
+    amd64)
+    job_create_build build-$arch-freebsd build-freebsd                       \
+                arch=$arch                                                   \
+                $RUNVARS $BUILD_RUNVARS $BUILD_FREEBSD_RUNVARS $arch_runvars \
+                tree_freebsd=$TREE_FREEBSD                                   \
+                revision_freebsd=$REVISION_FREEBSD
+    freebsd_version=`set_freebsd_build_job_flags $arch`
+    # Pass new hostflags, now the buildhost must not be shared, since we are
+    # testing the newly built image
+    job_create_build build-$arch-freebsd-again build-freebsd                 \
+                arch=$arch                                                   \
+                $RUNVARS $BUILD_RUNVARS $BUILD_FREEBSD_RUNVARS $arch_runvars \
+                host_hostflags=arch-$arch,freebsd-$freebsd_version,purpose-build \
+                tree_freebsd=$TREE_FREEBSD                                   \
+                revision_freebsd=$REVISION_FREEBSD                           \
+                freebsdbuildjob=build-$arch-freebsd
+    ;;
+    esac
+
     case "$arch" in
     arm*) continue;; # don't do any other kernel builds
     esac
diff --git a/sg-run-job b/sg-run-job
index ceb79800..c480a02b 100755
--- a/sg-run-job
+++ b/sg-run-job
@@ -53,12 +53,15 @@ proc run-job {job} {
     set skip_globs     [jobdb::read-runvar $flight $job skip_testids]
 
     set nh [need-hosts/$jobinfo(recipe)]
-    if {![string compare $nh BUILD]} {
+    if {![string compare $nh BUILD_LINUX]} {
         set need_xen_hosts {}
-        set need_build_host 1
+        set need_build_host LINUX
+    } elseif {![string compare $nh BUILD_FREEBSD]} {
+        set need_xen_hosts {}
+        set need_build_host FREEBSD
     } else {
         set need_xen_hosts $nh
-        set need_build_host 0
+        set need_build_host ""
     }
     set nested_layers_hosts {}
 
@@ -69,7 +72,8 @@ proc run-job {job} {
         eval run-ts broken  =             ts-hosts-allocate + $need_xen_hosts
     }
 
-    if {$need_build_host} { catching-otherwise broken prepare-build-host }
+    if {![string compare $need_build_host LINUX]} { catching-otherwise broken prepare-build-host-linux }
+    if {![string compare $need_build_host FREEBSD]}  { catching-otherwise broken prepare-build-host-freebsd }
 
     if {$ok} { setstatus running                                          }
 
@@ -89,7 +93,7 @@ proc run-job {job} {
         set need_xen_hosts [lunappend nested_layers_hosts]
     }
 
-    if {$need_build_host && $anyfailed} {
+    if {[string length $need_build_host] && $anyfailed} {
 	run-ts  !broken capture-logs      ts-logs-capture + host
     }
 
@@ -106,7 +110,7 @@ proc run-job {job} {
 
     if {$ok} { setstatus pass                                             }
 
-    if {$need_build_host && $ok} { jobdb::preserve-task 90 }
+    if {[string length $need_build_host] && $ok} { jobdb::preserve-task 90 }
 
     if {$anyfailed} {
         jobdb::logputs stdout "at least one test failed"
@@ -535,11 +539,12 @@ proc need-hosts/host-examine-linux {} {
 
 #---------- builds ----------
 
-proc need-hosts/build {} { return BUILD }
-proc need-hosts/build-kern {} { return BUILD }
-proc need-hosts/build-libvirt {} { return BUILD }
-proc need-hosts/build-rumprun {} { return BUILD }
-proc need-hosts/build-xtf {} { return BUILD }
+proc need-hosts/build {} { return BUILD_LINUX }
+proc need-hosts/build-kern {} { return BUILD_LINUX }
+proc need-hosts/build-libvirt {} { return BUILD_LINUX }
+proc need-hosts/build-rumprun {} { return BUILD_LINUX }
+proc need-hosts/build-xtf {} { return BUILD_LINUX }
+proc need-hosts/build-freebsd {} { return BUILD_FREEBSD }
 
 proc run-job/build {} {
     run-ts . = ts-xen-build
@@ -566,13 +571,23 @@ proc run-job/build-xtf {} {
     run-ts . = ts-xtf-build
 }
 
-proc prepare-build-host {} {
+proc run-job/build-freebsd {} {
+    run-ts . = ts-freebsd-build
+}
+
+proc prepare-build-host-linux {} {
     global jobinfo
     run-ts broken = ts-hosts-allocate + host
     run-ts broken host-install(*) ts-host-install-twice
     run-ts . host-build-prep ts-xen-build-prep
 }
 
+proc prepare-build-host-freebsd {} {
+    global jobinfo
+    run-ts broken = ts-hosts-allocate + host
+    run-ts broken host-install(*) ts-freebsd-host-install
+}
+
 proc need-hosts/coverity {} { return BUILD }
 proc run-job/coverity {} {
     run-ts . = ts-coverity-build + host
