diff --git a/docs/man/xl.conf.5.pod b/docs/man/xl.conf.5.pod
index 41ee428744..efe259e47d 100644
--- a/docs/man/xl.conf.5.pod
+++ b/docs/man/xl.conf.5.pod
@@ -72,6 +72,25 @@ of memory given to domain 0 by default.
 
 Default: C<"auto">
 
+=item B<autoballoon_dom0_min_memmb=NUMBER>
+
+When autoballooning, C<xl> will refuse to balloon dom0 down below this
+number of megabytes.
+
+Note that C<libxl> has its own internal limit for ballooning dom0.
+
+If unset, C<autoballoon_dom0_min_mempct> will be used instead.
+
+=item B<autoballoon_dom0_min_mempct=NUMBER>
+
+When autoballooning, C<xl> will refuse to balloon dom0 down below this
+percentage of host memory.
+
+If C<autoballoon_dom0_min_memmb> is set, then this value will be
+ignored.
+
+Default: C<25>
+
 =item B<run_hotplug_scripts=BOOLEAN>
 
 If disabled hotplug scripts will be called from udev, as it used to
diff --git a/tools/examples/xl.conf b/tools/examples/xl.conf
index 95f2f442d3..c4cec25989 100644
--- a/tools/examples/xl.conf
+++ b/tools/examples/xl.conf
@@ -9,6 +9,15 @@
 # starts with all the host's memory.
 #autoballoon="auto"
 
+# If autoballooning is enabled, control the minimum value below which
+# xl will not balloon dom0.  autoballoon_dom0_min_memmb is the minimum
+# value in megabytes; autoballoon_dom0_min_mempct is a percentage of
+# host memory.  If autoballoon_dom0_min_memmb is set,
+# autoballoon_dom0_min_mempct will be ignored.  If neither is set, the
+# default value of autoballoon_dom0_min_mempct will be used.
+#autoballoon_dom0_min_memmb = 1024
+#autoballoon_dom0_min_mempct = 25
+
 # full path of the lockfile used by xl during domain creation
 #lockfile="/var/lock/xl"
 
diff --git a/tools/xl/xl.c b/tools/xl/xl.c
index 2a5ddd4390..ab6ee09a9d 100644
--- a/tools/xl/xl.c
+++ b/tools/xl/xl.c
@@ -37,6 +37,8 @@ xentoollog_logger_stdiostream *logger;
 int dryrun_only;
 int force_execution;
 int autoballoon = -1;
+uint64_t autoballoon_dom0_min_memkb;
+bool autoballoon_dom0_min_is_default;
 char *blkdev_start;
 int run_hotplug_scripts = 1;
 char *lockfile;
@@ -124,6 +126,42 @@ static void parse_global_config(const char *configfile,
     if (autoballoon == -1)
         autoballoon = auto_autoballoon();
 
+    if (!xlu_cfg_get_long(config, "autoballoon_dom0_min_memmb", &l, 0)) {
+        /* Convert from megabytes to kilobytes */
+        autoballoon_dom0_min_memkb = l * 1024;
+    } else {
+        /* Minimum KB not set; look for a minimum percentage of host RAM */
+        libxl_physinfo info;
+        const libxl_version_info *vinfo;
+
+        if (!xlu_cfg_get_long(config, "autoballoon_dom0_min_mempct", &l, 0)) {
+            if (l < 0 || l > 100) {
+                fprintf(stderr, "Invalid autoballoon_dom0_min_memkb option");
+                exit(EXIT_FAILURE);
+            }
+        } else {
+            /*
+             * Default to 25% of host memory.  Lower than this should
+             * require admin intervention.
+             */
+            l = 25;
+            autoballoon_dom0_min_is_default = true;
+        }
+
+        if (libxl_get_physinfo(ctx, &info) != 0) {
+            fprintf(stderr, "libxl_get_physinfo failed.\n");
+            exit(EXIT_FAILURE);
+        }
+
+        vinfo = libxl_get_version_info(ctx);
+
+        /* First calculate total host memory in kb */
+        autoballoon_dom0_min_memkb = (info.total_pages * vinfo->pagesize) / (1 << 10);
+
+        /* Then apply the percentage */
+        autoballoon_dom0_min_memkb = autoballoon_dom0_min_memkb * l / 100;
+    }
+
     if (!xlu_cfg_get_long (config, "run_hotplug_scripts", &l, 0))
         run_hotplug_scripts = l;
 
diff --git a/tools/xl/xl.h b/tools/xl/xl.h
index 06569c6c4a..d027656ed4 100644
--- a/tools/xl/xl.h
+++ b/tools/xl/xl.h
@@ -265,6 +265,8 @@ int child_report(xlchildnum child);
 
 /* global options */
 extern int autoballoon;
+uint64_t autoballoon_dom0_min_memkb;
+bool autoballoon_dom0_min_is_default;
 extern int run_hotplug_scripts;
 extern int dryrun_only;
 extern int claim_mode;
diff --git a/tools/xl/xl_vmcontrol.c b/tools/xl/xl_vmcontrol.c
index 435155a033..def8e6ee1a 100644
--- a/tools/xl/xl_vmcontrol.c
+++ b/tools/xl/xl_vmcontrol.c
@@ -322,7 +322,7 @@ static int domain_wait_event(uint32_t domid, libxl_event **event_r)
 static bool freemem(uint32_t domid, libxl_domain_config *d_config)
 {
     int rc, retries = 3;
-    uint64_t need_memkb, free_memkb;
+    uint64_t need_memkb;
 
     if (!autoballoon)
         return true;
@@ -332,6 +332,8 @@ static bool freemem(uint32_t domid, libxl_domain_config *d_config)
         return false;
 
     do {
+        uint64_t free_memkb, target_memkb;
+
         rc = libxl_get_free_memory(ctx, &free_memkb);
         if (rc < 0)
             return false;
@@ -339,7 +341,24 @@ static bool freemem(uint32_t domid, libxl_domain_config *d_config)
         if (free_memkb >= need_memkb)
             return true;
 
-        rc = libxl_set_memory_target(ctx, 0, free_memkb - need_memkb, 1, 0);
+        rc = libxl_get_memory_target(ctx, 0, &target_memkb);
+        if (rc < 0)
+            return false;
+
+        target_memkb -= need_memkb - free_memkb;
+
+        if (target_memkb < autoballoon_dom0_min_memkb) {
+            fprintf(stderr, "Autoballoon: %"PRIu64"kb below minimum dom0 target %"PRIu64"kb\n",
+                    target_memkb, autoballoon_dom0_min_memkb);
+            if (autoballoon_dom0_min_is_default) {
+                fprintf(stderr, "\nConsider setting autoballoon minimum target explicitly,\n" \
+                        "or setting dom0's memory allocation explicitly.\n" \
+                        "see the xl.conf man page for more information.\n\n");
+            }
+            return false;
+        }
+
+        rc = libxl_set_memory_target(ctx, 0, target_memkb, 0, 0);
         if (rc < 0)
             return false;
 
