diff --git a/app/static/js/cm-resize.js b/app/static/js/cm-resize.js
new file mode 100644
index 0000000000000000000000000000000000000000..78116e0373f74aa05d06f4b3ab51b0bcc5bc4033
--- /dev/null
+++ b/app/static/js/cm-resize.js
@@ -0,0 +1,262 @@
+/*!
+ * cm-resize v1.0.0
+ * https://github.com/Sphinxxxx/cm-resize
+ *
+ * Copyright 2017-2018 Andreas Borgen (https://github.com/Sphinxxxx)
+ * Released under the MIT license.
+ */
+(function (global, factory) {
+	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+	typeof define === 'function' && define.amd ? define(factory) :
+	(global.cmResize = factory());
+}(this, (function () { 'use strict';
+
+function dragTracker(options) {
+
+
+    var ep = Element.prototype;
+    if (!ep.matches) ep.matches = ep.msMatchesSelector || ep.webkitMatchesSelector;
+    if (!ep.closest) ep.closest = function (s) {
+        var node = this;
+        do {
+            if (node.matches(s)) return node;
+            node = node.tagName === 'svg' ? node.parentNode : node.parentElement;
+        } while (node);
+
+        return null;
+    };
+
+    options = options || {};
+    var container = options.container || document.documentElement,
+        selector = options.selector,
+        callback = options.callback || console.log,
+        callbackStart = options.callbackDragStart,
+        callbackEnd = options.callbackDragEnd,
+
+    callbackClick = options.callbackClick,
+        propagate = options.propagateEvents,
+        roundCoords = options.roundCoords !== false,
+        dragOutside = options.dragOutside !== false,
+
+    handleOffset = options.handleOffset || options.handleOffset !== false;
+    var offsetToCenter = null;
+    switch (handleOffset) {
+        case 'center':
+            offsetToCenter = true;break;
+        case 'topleft':
+        case 'top-left':
+            offsetToCenter = false;break;
+    }
+
+    var dragged = void 0,
+        mouseOffset = void 0,
+        dragStart = void 0;
+
+    function getMousePos(e, elm, offset, stayWithin) {
+        var x = e.clientX,
+            y = e.clientY;
+
+        function respectBounds(value, min, max) {
+            return Math.max(min, Math.min(value, max));
+        }
+
+        if (elm) {
+            var bounds = elm.getBoundingClientRect();
+            x -= bounds.left;
+            y -= bounds.top;
+
+            if (offset) {
+                x -= offset[0];
+                y -= offset[1];
+            }
+            if (stayWithin) {
+                x = respectBounds(x, 0, bounds.width);
+                y = respectBounds(y, 0, bounds.height);
+            }
+
+            if (elm !== container) {
+                var center = offsetToCenter !== null ? offsetToCenter
+                : elm.nodeName === 'circle' || elm.nodeName === 'ellipse';
+
+                if (center) {
+                    x -= bounds.width / 2;
+                    y -= bounds.height / 2;
+                }
+            }
+        }
+        return roundCoords ? [Math.round(x), Math.round(y)] : [x, y];
+    }
+
+    function stopEvent(e) {
+        e.preventDefault();
+        if (!propagate) {
+            e.stopPropagation();
+        }
+    }
+
+    function onDown(e) {
+        if (selector) {
+            dragged = selector instanceof Element ? selector.contains(e.target) ? selector : null : e.target.closest(selector);
+        } else {
+            dragged = {};
+        }
+
+        if (dragged) {
+            stopEvent(e);
+
+            mouseOffset = selector && handleOffset ? getMousePos(e, dragged) : [0, 0];
+            dragStart = getMousePos(e, container, mouseOffset);
+            if (roundCoords) {
+                dragStart = dragStart.map(Math.round);
+            }
+
+            if (callbackStart) {
+                callbackStart(dragged, dragStart);
+            }
+        }
+    }
+
+    function onMove(e) {
+        if (!dragged) {
+            return;
+        }
+        stopEvent(e);
+
+        var pos = getMousePos(e, container, mouseOffset, !dragOutside);
+        callback(dragged, pos, dragStart);
+    }
+
+    function onEnd(e) {
+        if (!dragged) {
+            return;
+        }
+
+        if (callbackEnd || callbackClick) {
+            var pos = getMousePos(e, container, mouseOffset, !dragOutside);
+
+            if (callbackClick && dragStart[0] === pos[0] && dragStart[1] === pos[1]) {
+                callbackClick(dragged, dragStart);
+            }
+            if (callbackEnd) {
+                callbackEnd(dragged, pos, dragStart);
+            }
+        }
+        dragged = null;
+    }
+
+
+    container.addEventListener('mousedown', function (e) {
+        if (isLeftButton(e)) {
+            onDown(e);
+        }
+    });
+    container.addEventListener('touchstart', function (e) {
+        relayTouch(e, onDown);
+    });
+
+    window.addEventListener('mousemove', function (e) {
+        if (!dragged) {
+            return;
+        }
+
+        if (isLeftButton(e)) {
+            onMove(e);
+        }
+        else {
+                onEnd(e);
+            }
+    });
+    window.addEventListener('touchmove', function (e) {
+        relayTouch(e, onMove);
+    });
+
+    window.addEventListener('mouseup', function (e) {
+        if (dragged && !isLeftButton(e)) {
+            onEnd(e);
+        }
+    });
+    function onTouchEnd(e) {
+        onEnd(tweakTouch(e));
+    }
+    container.addEventListener('touchend', onTouchEnd);
+    container.addEventListener('touchcancel', onTouchEnd);
+
+    function isLeftButton(e) {
+        return e.buttons !== undefined ? e.buttons === 1 :
+        e.which === 1;
+    }
+    function relayTouch(e, handler) {
+        if (e.touches.length !== 1) {
+            onEnd(e);return;
+        }
+
+        handler(tweakTouch(e));
+    }
+    function tweakTouch(e) {
+        var touch = e.targetTouches[0];
+        if (!touch) {
+            touch = e.changedTouches[0];
+        }
+
+        touch.preventDefault = e.preventDefault.bind(e);
+        touch.stopPropagation = e.stopPropagation.bind(e);
+        return touch;
+    }
+}
+
+document.documentElement.firstElementChild 
+.appendChild(document.createElement('style')).textContent = '.cm-resize-handle{display:block;position:absolute;bottom:0;right:0;z-index:99;width:18px;height:18px;background:url("data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' width=\'30\' height=\'30\' viewBox=\'0,0 16,16\'%3E%3Cpath stroke=\'gray\' stroke-width=\'2\' d=\'M-1,12 l18,-18 M-1,18 l18,-18 M-1,24 l18,-18 M-1,30 l18,-18\'/%3E%3C/svg%3E") center/cover;box-shadow:inset -1px -1px 0 0 silver;cursor:nwse-resize}';
+
+function cmResize(cm, config) {
+    config = config || {};
+
+    var minW = config.minWidth || 200,
+        minH = config.minHeight || 100,
+        resizeW = config.resizableWidth !== false,
+        resizeH = config.resizableHeight !== false,
+        css = config.cssClass || 'cm-resize-handle';
+
+    var cmElement = cm.display.wrapper,
+        cmHandle = config.handle || function () {
+        var h = cmElement.appendChild(document.createElement('div'));
+        h.className = css;
+        return h;
+    }();
+
+    var vScroll = cmElement.querySelector('.CodeMirror-vscrollbar'),
+        hScroll = cmElement.querySelector('.CodeMirror-hscrollbar');
+    function constrainScrollbars() {
+        if (!config.handle) {
+            vScroll.style.bottom = '18px';
+            hScroll.style.right = '18px';
+        }
+    }
+    cm.on('update', constrainScrollbars);
+    constrainScrollbars();
+
+    var startPos = void 0,
+        startSize = void 0;
+    dragTracker({
+        container: cmHandle.offsetParent,
+        selector: cmHandle,
+
+        callbackDragStart: function callbackDragStart(handle, pos) {
+            startPos = pos;
+            startSize = [cmElement.clientWidth, cmElement.clientHeight];
+        },
+        callback: function callback(handle, pos) {
+            var diffX = pos[0] - startPos[0],
+                diffY = pos[1] - startPos[1],
+                cw = resizeW ? Math.max(minW, startSize[0] + diffX) : null,
+                ch = resizeH ? Math.max(minH, startSize[1] + diffY) : null;
+
+            cm.setSize(cw, ch);
+        }
+    });
+
+    return cmHandle;
+}
+
+return cmResize;
+
+})));
diff --git a/app/static/js/groups.js b/app/static/js/groups.js
index e660d72b301951e1004e4e596d8d658df6b90549..14635267baea07258ef5886e076fe9db6e62274a 100644
--- a/app/static/js/groups.js
+++ b/app/static/js/groups.js
@@ -27,6 +27,11 @@ $(document).ready(function() {
         mode: "yaml"
     });
     groupVarsEditor.setSize(null, 120);
+    var handle = cmResize(groupVarsEditor, {
+      minHeight: 120,
+      resizableWidth: false,
+      resizableHeight: true,
+    });
   }
 
   var groups_table =  $("#groups_table").DataTable({
diff --git a/app/static/js/hosts.js b/app/static/js/hosts.js
index 463fff207cf6b6297481022886609108ee7e400a..d79f195194eaea8f4445d7742fabcb0e197db235 100644
--- a/app/static/js/hosts.js
+++ b/app/static/js/hosts.js
@@ -6,6 +6,11 @@ $(document).ready(function() {
         mode: "yaml"
     });
     hostVarsEditor.setSize(null, 120);
+    var handle = cmResize(hostVarsEditor, {
+      minHeight: 120,
+      resizableWidth: false,
+      resizableHeight: true,
+    });
   }
 
   function set_default_ip() {
diff --git a/app/templates/base.html b/app/templates/base.html
index 79d304ad0a8c82c60ebe1786691dde5306b74db6..05b686cb632f6b94b0b0ee1b9464ea00c85acea7 100644
--- a/app/templates/base.html
+++ b/app/templates/base.html
@@ -69,6 +69,7 @@
   <script src="{{ url_for('static', filename='js/showdown.min.js') }}"></script>
   <script src="{{ url_for('static', filename='js/codemirror.js') }}"></script>
   <script src="{{ url_for('static', filename='js/yaml.js') }}"></script>
+  <script src="{{ url_for('static', filename='js/cm-resize.js') }}"></script>
   <script type=text/javascript>
     $SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
   </script>