diff --git a/tools/libxl/libxl_console.c b/tools/libxl/libxl_console.c
index 088a455b52..047d23d7ae 100644
--- a/tools/libxl/libxl_console.c
+++ b/tools/libxl/libxl_console.c
@@ -16,8 +16,8 @@
 
 #include "libxl_internal.h"
 
-static int libxl__console_tty_path(libxl__gc *gc, uint32_t domid, int cons_num,
-                                   libxl_console_type type, char **tty_path)
+int libxl__console_tty_path(libxl__gc *gc, uint32_t domid, int cons_num,
+                            libxl_console_type type, char **tty_path)
 {
     int rc;
     char *dom_path;
diff --git a/tools/libxl/libxl_create.c b/tools/libxl/libxl_create.c
index 3a7364e2ac..ada942bc8d 100644
--- a/tools/libxl/libxl_create.c
+++ b/tools/libxl/libxl_create.c
@@ -846,6 +846,8 @@ static void domcreate_devmodel_started(libxl__egc *egc,
 static void domcreate_attach_devices(libxl__egc *egc,
                                      libxl__multidev *multidev,
                                      int ret);
+static void console_xswait_callback(libxl__egc *egc, libxl__xswait_state *xswa,
+                                    int rc, const char *p);
 
 /* Our own function to clean up and call the user's callback.
  * The final call in the sequence. */
@@ -1151,6 +1153,8 @@ static void initiate_domain_create(libxl__egc *egc,
     if (ret)
         goto error_out;
 
+    libxl__xswait_init(&dcs->console_xswait);
+
     if (restore_fd >= 0 || dcs->soft_reset) {
         LOGD(DEBUG, domid, "restoring, not running bootloader");
         domcreate_bootloader_done(egc, &dcs->bl, 0);
@@ -1705,6 +1709,7 @@ static void domcreate_attach_devices(libxl__egc *egc,
     int domid = dcs->guest_domid;
     libxl_domain_config *const d_config = dcs->guest_config;
     const libxl__device_type *dt;
+    char *tty_path;
 
     if (ret) {
         LOGD(ERROR, domid, "unable to add %s devices",
@@ -1728,9 +1733,24 @@ static void domcreate_attach_devices(libxl__egc *egc,
         return;
     }
 
-    domcreate_console_available(egc, dcs);
+    ret = libxl__console_tty_path(gc, domid, 0, LIBXL_CONSOLE_TYPE_PV, &tty_path);
+    if (ret) {
+        LOG(ERROR, "failed to get domain %d console tty path",
+            domid);
+        goto error_out;
+    }
 
-    domcreate_complete(egc, dcs, 0);
+    dcs->console_xswait.ao = ao;
+    dcs->console_xswait.what = GCSPRINTF("domain %d console tty", domid);
+    dcs->console_xswait.path = tty_path;
+    dcs->console_xswait.timeout_ms = LIBXL_INIT_TIMEOUT * 1000;
+    dcs->console_xswait.callback = console_xswait_callback;
+    ret = libxl__xswait_start(gc, &dcs->console_xswait);
+    if (ret) {
+        LOG(ERROR, "unable to set up watch for domain %d console tty path",
+            domid);
+        goto error_out;
+    }
 
     return;
 
@@ -1739,6 +1759,30 @@ error_out:
     domcreate_complete(egc, dcs, ret);
 }
 
+static void console_xswait_callback(libxl__egc *egc, libxl__xswait_state *xswa,
+                                    int rc, const char *p)
+{
+    EGC_GC;
+    libxl__domain_create_state *dcs = CONTAINER_OF(xswa, *dcs, console_xswait);
+
+    if (rc) {
+        if (rc == ERROR_TIMEDOUT)
+            LOG(ERROR, "%s: timed out", xswa->what);
+        goto out;
+    }
+
+    if (p && p[0] != '\0') {
+        domcreate_console_available(egc, dcs);
+        goto out;
+    }
+
+    return;
+
+out:
+    libxl__xswait_stop(gc, xswa);
+    domcreate_complete(egc, dcs, rc);
+}
+
 static void domcreate_complete(libxl__egc *egc,
                                libxl__domain_create_state *dcs,
                                int rc)
@@ -1747,6 +1791,8 @@ static void domcreate_complete(libxl__egc *egc,
     libxl_domain_config *const d_config = dcs->guest_config;
     libxl_domain_config *d_config_saved = &dcs->guest_config_saved;
 
+    libxl__xswait_stop(gc, &dcs->console_xswait);
+
     libxl__domain_build_state_dispose(&dcs->build_state);
 
     if (!rc && d_config->b_info.exec_ssidref)
diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
index 4936446069..19d11cc6d6 100644
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -1517,6 +1517,8 @@ _hidden char *libxl__domain_device_libxl_path(libxl__gc *gc, uint32_t domid, uin
                                               libxl__device_kind device_kind);
 _hidden int libxl__parse_backend_path(libxl__gc *gc, const char *path,
                                       libxl__device *dev);
+_hidden int libxl__console_tty_path(libxl__gc *gc, uint32_t domid, int cons_num,
+                                    libxl_console_type type, char **tty_path);
 _hidden int libxl__device_destroy(libxl__gc *gc, libxl__device *dev);
 _hidden int libxl__wait_for_backend(libxl__gc *gc, const char *be_path,
                                     const char *state);
@@ -4180,6 +4182,7 @@ struct libxl__domain_create_state {
     /* necessary if the domain creation failed and we have to destroy it */
     libxl__domain_destroy_state dds;
     libxl__multidev multidev;
+    libxl__xswait_state console_xswait;
 };
 
 _hidden int libxl__device_nic_set_devids(libxl__gc *gc,
