diff --git a/include/libweston/backend-headless.h b/include/libweston/backend-headless.h
index 08bd674b46fd79f059b2c1a37047b18d963306d8..1f53835e01f079c21c910f632ad32db371ec47bd 100644
--- a/include/libweston/backend-headless.h
+++ b/include/libweston/backend-headless.h
@@ -41,6 +41,9 @@ struct weston_headless_backend_config {
 
 	/** Whether to use the pixman renderer, default is no-op */
 	bool use_pixman;
+
+	/** Whether to use the GL renderer, conflicts with use_pixman */
+	bool use_gl;
 };
 
 #ifdef  __cplusplus
diff --git a/libweston/backend-headless/headless.c b/libweston/backend-headless/headless.c
index e5630b3a6173894de5b4bbbe12ab3205705b68d1..d087b2b0f429332c7cb9607aeaa350821a1e3ba2 100644
--- a/libweston/backend-headless/headless.c
+++ b/libweston/backend-headless/headless.c
@@ -32,18 +32,22 @@
 #include <string.h>
 #include <sys/time.h>
 #include <stdbool.h>
+#include <dlfcn.h>
+#include <drm_fourcc.h>
 
 #include <libweston/libweston.h>
 #include <libweston/backend-headless.h>
 #include "shared/helpers.h"
 #include "linux-explicit-synchronization.h"
 #include "pixman-renderer.h"
+#include "renderer-gl/gl-renderer.h"
 #include "presentation-time-server-protocol.h"
 #include <libweston/windowed-output-api.h>
 
 enum headless_renderer_type {
 	HEADLESS_NOOP,
 	HEADLESS_PIXMAN,
+	HEADLESS_GL,
 };
 
 struct headless_backend {
@@ -52,6 +56,8 @@ struct headless_backend {
 
 	struct weston_seat fake_seat;
 	enum headless_renderer_type renderer_type;
+
+	struct gl_renderer_interface *glri;
 };
 
 struct headless_head {
@@ -67,6 +73,11 @@ struct headless_output {
 	pixman_image_t *image;
 };
 
+static const uint32_t headless_formats[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+};
+
 static inline struct headless_head *
 to_headless_head(struct weston_head *base)
 {
@@ -126,6 +137,15 @@ headless_output_repaint(struct weston_output *output_base,
 	return 0;
 }
 
+static void
+headless_output_disable_gl(struct headless_output *output)
+{
+	struct weston_compositor *compositor = output->base.compositor;
+	struct headless_backend *b = to_headless_backend(compositor);
+
+	b->glri->output_destroy(&output->base);
+}
+
 static void
 headless_output_disable_pixman(struct headless_output *output)
 {
@@ -146,6 +166,9 @@ headless_output_disable(struct weston_output *base)
 	wl_event_source_remove(output->finish_frame_timer);
 
 	switch (b->renderer_type) {
+	case HEADLESS_GL:
+		headless_output_disable_gl(output);
+		break;
 	case HEADLESS_PIXMAN:
 		headless_output_disable_pixman(output);
 		break;
@@ -167,6 +190,25 @@ headless_output_destroy(struct weston_output *base)
 	free(output);
 }
 
+static int
+headless_output_enable_gl(struct headless_output *output)
+{
+	struct weston_compositor *compositor = output->base.compositor;
+	struct headless_backend *b = to_headless_backend(compositor);
+
+	if (b->glri->output_pbuffer_create(&output->base,
+					   output->base.current_mode->width,
+					   output->base.current_mode->height,
+					   GL_RENDERER_CONFIG_OPAQUE,
+					   headless_formats,
+					   ARRAY_LENGTH(headless_formats)) < 0) {
+		weston_log("failed to create gl renderer output state\n");
+		return -1;
+	}
+
+	return 0;
+}
+
 static int
 headless_output_enable_pixman(struct headless_output *output)
 {
@@ -209,6 +251,9 @@ headless_output_enable(struct weston_output *base)
 		wl_event_loop_add_timer(loop, finish_frame_handler, output);
 
 	switch (b->renderer_type) {
+	case HEADLESS_GL:
+		ret = headless_output_enable_gl(output);
+		break;
 	case HEADLESS_PIXMAN:
 		ret = headless_output_enable_pixman(output);
 		break;
@@ -339,6 +384,33 @@ headless_destroy(struct weston_compositor *ec)
 	free(b);
 }
 
+static int
+headless_gl_renderer_init(struct headless_backend *b)
+{
+	b->glri = weston_load_module("gl-renderer.so", "gl_renderer_interface");
+	if (!b->glri)
+		return -1;
+
+	/* GBM will load a dri driver, but even though they need symbols from
+	 * libglapi, in some version of Mesa they are not linked to it. Since
+	 * only the gl-renderer module links to it, the call above won't make
+	 * these symbols globally available, and loading the DRI driver fails.
+	 * Workaround this by dlopen()'ing libglapi with RTLD_GLOBAL. */
+	dlopen("libglapi.so.0", RTLD_LAZY | RTLD_GLOBAL);
+
+	if (b->glri->display_create(b->compositor,
+				    EGL_PLATFORM_SURFACELESS_MESA,
+				    EGL_DEFAULT_DISPLAY,
+				    GL_RENDERER_CONFIG_OPAQUE,
+				    GL_RENDERER_CONFIG_SURFACE_PBUFFER,
+				    headless_formats,
+				    ARRAY_LENGTH(headless_formats)) < 0) {
+		return -1;
+	}
+
+	return 0;
+}
+
 static const struct weston_windowed_output_api api = {
 	headless_output_set_size,
 	headless_head_create,
@@ -364,12 +436,22 @@ headless_backend_create(struct weston_compositor *compositor,
 	b->base.destroy = headless_destroy;
 	b->base.create_output = headless_output_create;
 
-	if (config->use_pixman)
+	if (config->use_pixman && config->use_gl) {
+		weston_log("Error: cannot use both Pixman *and* GL renderers.\n");
+		goto err_free;
+	}
+
+	if (config->use_gl)
+		b->renderer_type = HEADLESS_GL;
+	else if (config->use_pixman)
 		b->renderer_type = HEADLESS_PIXMAN;
 	else
 		b->renderer_type = HEADLESS_NOOP;
 
 	switch (b->renderer_type) {
+	case HEADLESS_GL:
+		ret = headless_gl_renderer_init(b);
+		break;
 	case HEADLESS_PIXMAN:
 		ret = pixman_renderer_init(compositor);
 		break;
diff --git a/libweston/backend-headless/meson.build b/libweston/backend-headless/meson.build
index 19b57605c113f7174f4a8c256bfb51d260d3ad79..790895383fe47e9448d9c8e9eaeaf46b64f223f7 100644
--- a/libweston/backend-headless/meson.build
+++ b/libweston/backend-headless/meson.build
@@ -12,7 +12,7 @@ plugin_headless = shared_library(
 	'headless-backend',
 	srcs_headless,
 	include_directories: include_directories('../..', '../../shared'),
-	dependencies: dep_libweston,
+	dependencies: [ dep_libweston, dep_libdrm_headers ],
 	name_prefix: '',
 	install: true,
 	install_dir: dir_module_libweston,
diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c
index f5c64b8e83b55ff7900120c2400847ded5f1e4b2..00b05f8f5e02fd998485ef9f43d4ec4d69e5ca88 100644
--- a/libweston/renderer-gl/gl-renderer.c
+++ b/libweston/renderer-gl/gl-renderer.c
@@ -3145,6 +3145,48 @@ gl_renderer_output_window_create(struct weston_output *output,
 	return ret;
 }
 
+static int
+gl_renderer_output_pbuffer_create(struct weston_output *output,
+				  int width,
+				  int height,
+				  enum gl_renderer_config_alpha config_alpha,
+				  const uint32_t *drm_formats,
+				  unsigned drm_formats_count)
+{
+	struct gl_renderer *gr = get_renderer(output->compositor);
+	EGLConfig pbuffer_config;
+	EGLSurface egl_surface;
+	int ret;
+	EGLint pbuffer_attribs[] = {
+		EGL_WIDTH, width,
+		EGL_HEIGHT, height,
+		EGL_NONE
+	};
+
+	pbuffer_config = gl_renderer_get_egl_config(gr, config_alpha,
+						    EGL_PBUFFER_BIT,
+						    drm_formats,
+						    drm_formats_count);
+	if (pbuffer_config == EGL_NO_CONFIG_KHR) {
+		weston_log("failed to choose EGL config for PbufferSurface\n");
+		return -1;
+	}
+
+	egl_surface = eglCreatePbufferSurface(gr->egl_display, pbuffer_config,
+					      pbuffer_attribs);
+	if (egl_surface == EGL_NO_SURFACE) {
+		weston_log("failed to create egl surface\n");
+		gl_renderer_print_egl_error_state();
+		return -1;
+	}
+
+	ret = gl_renderer_output_create(output, egl_surface);
+	if (ret < 0)
+		eglDestroySurface(gr->egl_display, egl_surface);
+
+	return ret;
+}
+
 static void
 gl_renderer_output_destroy(struct weston_output *output)
 {
@@ -3750,6 +3792,7 @@ gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface)
 WL_EXPORT struct gl_renderer_interface gl_renderer_interface = {
 	.display_create = gl_renderer_display_create,
 	.output_window_create = gl_renderer_output_window_create,
+	.output_pbuffer_create = gl_renderer_output_pbuffer_create,
 	.output_destroy = gl_renderer_output_destroy,
 	.output_set_border = gl_renderer_output_set_border,
 	.create_fence_fd = gl_renderer_create_fence_fd,
diff --git a/libweston/renderer-gl/gl-renderer.h b/libweston/renderer-gl/gl-renderer.h
index 90ae0e8d2cda2ccfaa0a91c5f73b3f365f9e07c1..08204fa2028195e8df5f636101bc3d52fe3e9b75 100644
--- a/libweston/renderer-gl/gl-renderer.h
+++ b/libweston/renderer-gl/gl-renderer.h
@@ -148,6 +148,37 @@ struct gl_renderer_interface {
 				    const uint32_t *drm_formats,
 				    unsigned drm_formats_count);
 
+	/**
+	 * Attach GL-renderer to the output with internal pixel storage
+	 *
+	 * \param output The output to create a rendering surface for.
+	 * \param width Width of the rendering surface in pixels.
+	 * \param height Height of the rendering surface in pixels.
+	 * \param config_alpha Determines if the EGLConfig should have
+	 * alpha channel or not.
+	 * \param drm_formats Array of DRM pixel formats that are acceptable.
+	 * \param drm_formats_count The drm_formats array length.
+	 * \return 0 on success, -1 on failure.
+	 *
+	 * This function creates the renderer data structures needed to repaint
+	 * the output. The repaint results will be kept internal and can only
+	 * be accessed through e.g. screen capture.
+	 *
+	 * An EGLConfig is chosen for the output, meeting the requirements in
+	 * config_alpha, drm_formats and drm_formats_count. If drm_formats_count
+	 * is zero then drm_formats can be NULL and EGL_NATIVE_VISUAL_ID
+	 * attribute is ignored on choosing an EGLConfig for the output.
+	 *
+	 * This function should be used only if \c display_create was called
+	 * with GL_RENDERER_CONFIG_SURFACE_PBUFFER.
+	 */
+	int (*output_pbuffer_create)(struct weston_output *output,
+				     int width,
+				     int height,
+				     enum gl_renderer_config_alpha config_alpha,
+				     const uint32_t *drm_formats,
+				     unsigned drm_formats_count);
+
 	void (*output_destroy)(struct weston_output *output);
 
 	/* Sets the output border.