diff --git a/NEWS.rst b/NEWS.rst
index bbe2a58b80cf6f95ca8271f773b2de9b580eabd8_TkVXUy5yc3Q=..ca586c1c865ef153e3f5486903f5d91cfa682732_TkVXUy5yc3Q= 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -7,6 +7,8 @@
 
 Changes to docker_isort so that it can be used to apply changes.
 
+Compatibility with longpolling.
+
 16.10.0
 -------
 
diff --git a/odoo_scripts/do_tests.py b/odoo_scripts/do_tests.py
index bbe2a58b80cf6f95ca8271f773b2de9b580eabd8_b2Rvb19zY3JpcHRzL2RvX3Rlc3RzLnB5..ca586c1c865ef153e3f5486903f5d91cfa682732_b2Rvb19zY3JpcHRzL2RvX3Rlc3RzLnB5 100755
--- a/odoo_scripts/do_tests.py
+++ b/odoo_scripts/do_tests.py
@@ -224,6 +224,7 @@
             dbname,
             "--stop-after-init",
             "--max-cron-threads=0",
+            "--workers=0",
             "--no-flake8",
             "--no-isort",
             "--no-dev",
diff --git a/odoo_scripts/docker_client.py b/odoo_scripts/docker_client.py
index bbe2a58b80cf6f95ca8271f773b2de9b580eabd8_b2Rvb19zY3JpcHRzL2RvY2tlcl9jbGllbnQucHk=..ca586c1c865ef153e3f5486903f5d91cfa682732_b2Rvb19zY3JpcHRzL2RvY2tlcl9jbGllbnQucHk= 100644
--- a/odoo_scripts/docker_client.py
+++ b/odoo_scripts/docker_client.py
@@ -4,6 +4,7 @@
 import os
 import re
 import tarfile
+from multiprocessing import Process
 from typing import Any, Dict, List, Mapping, Optional, Pattern, Tuple
 
 import docker
@@ -102,6 +103,7 @@
         run_kwargs: Dict[str, Any],
         pull: bool = True,
         target_source_dict: Optional[Mapping[str, str]] = None,
+        display_log: bool = True,
     ):
         """Run a container with the provided args"""
         replaces: List[Tuple[Pattern[str], str]] = (
@@ -140,11 +142,10 @@
         def stop_remove():
             """Stop then remove the container."""
             if container:
-                if container.status == "running":
-                    _logger.debug("Stopping container %s", container.name)
-                    container.stop()
-                    _logger.debug("Waiting for container %s", container.name)
-                    container.wait()
+                _logger.debug("Stopping container %s", container.name)
+                container.stop()
+                _logger.debug("Waiting for container %s", container.name)
+                container.wait()
             remove()
 
         atexit.register(stop_remove)
@@ -157,6 +158,22 @@
         _logger.debug("Created container %s", container.name)
         _logger.debug("Starting container %s", container.name)
         container.start()
+        if run_kwargs.get("detach", False):
+            if display_log:
+                process = Process(target=DockerClient.print_log, args=(container,))
+                process.start()
+
+                def stop_remove_wait_process():
+                    stop_remove()
+                    if process and process.is_alive():
+                        _logger.info("Waiting for log process to finish")
+                        process.join()
+
+                atexit.register(stop_remove_wait_process)
+                atexit.unregister(stop_remove)
+
+            return container
+
         atexit.unregister(stop_remove)
         atexit.register(remove)
 
diff --git a/odoo_scripts/docker_dev_start.py b/odoo_scripts/docker_dev_start.py
index bbe2a58b80cf6f95ca8271f773b2de9b580eabd8_b2Rvb19zY3JpcHRzL2RvY2tlcl9kZXZfc3RhcnQucHk=..ca586c1c865ef153e3f5486903f5d91cfa682732_b2Rvb19zY3JpcHRzL2RvY2tlcl9kZXZfc3RhcnQucHk= 100755
--- a/odoo_scripts/docker_dev_start.py
+++ b/odoo_scripts/docker_dev_start.py
@@ -3,6 +3,7 @@
 """Launch a docker image but bind the local directory inside the container
 and define the module path automatically.
 """
+import atexit
 import logging
 import os
 import pwd
@@ -6,4 +7,5 @@
 import logging
 import os
 import pwd
+import socket
 import sys
@@ -9,3 +11,4 @@
 import sys
+import tempfile
 from argparse import ArgumentParser
 from configparser import ConfigParser
@@ -10,6 +13,7 @@
 from argparse import ArgumentParser
 from configparser import ConfigParser
+from contextlib import closing
 from subprocess import call, check_output
 from typing import List
 
 import dockerpty
@@ -12,7 +16,8 @@
 from subprocess import call, check_output
 from typing import List
 
 import dockerpty
+from docker.errors import APIError
 from docker.types import Mount
 from psycopg2 import OperationalError, connect
 from psycopg2.errors import DuplicateDatabase, DuplicateObject
@@ -370,6 +375,15 @@
         default=True,
         dest="python_dev_mode",
     )
+
+    # workers options
+    parser.add_argument(
+        "--workers",
+        help="Number of workers [default: %(default)s]",
+        default=0,
+        type=int,
+    )
+
     return parser
 
 
@@ -434,6 +448,9 @@
     marabunta_db_password = nmspc.marabunta_db_password
     marabunta_force_version = nmspc.marabunta_force_version
     python_dev_mode = nmspc.python_dev_mode
+    workers = nmspc.workers
+    # XXX should that variable be called multi_workers?
+    longpolling = workers > 0
 
     if restore_filename:
         if not database:
@@ -478,9 +495,8 @@
     # options is only used with subprocess call
     options = {
         "name": project_name,
-        "hostname": project_name,
         "tty": True,
         "extra_hosts": {},
         "ports": {},
         "environment": {},
     }
@@ -482,10 +498,16 @@
         "tty": True,
         "extra_hosts": {},
         "ports": {},
         "environment": {},
     }
-    if not use_host_network:
-        options["ports"][8069] = 8069
+    # if not longpolling:
+    options["hostname"] = project_name
+    # TODO detect that from configuration or force it
+    exposed_port = 8069
+    # TODO detect that from configuration or force it
+    odoo_run_port = 8069
+    if not longpolling and not use_host_network:
+        options["ports"][exposed_port] = odoo_run_port
     mounts: List[Mount] = []
     """Mounts for Odoo docker"""
     tested_modules = None
@@ -500,7 +522,9 @@
     arg = [
         "populate"
         if populate_model or populate_size
-        else ("coverage-start" if coverage else "start")
+        else ("coverage-start" if coverage else "start"),
+        "--workers",
+        str(workers),
     ]
     if db_password:
         arg.extend(("--db_password", db_password))
@@ -1239,6 +1263,87 @@
     if marabunta_db_password:
         options["environment"]["MARABUNTA_DB_PASSWORD"] = marabunta_db_password
 
+    if longpolling:
+        longpolling_port = find_free_port()
+        run_kwargs = {
+            "command": ["caddy", "run", "--config", "/etc/caddy/Caddyfile"],
+            "detach": True,
+            "name": f"{project_name}_caddy",
+            "mounts": [],
+        }
+        # force the port, so that the caddyfile is correct whatever the configuration
+        if use_host_network:
+            caddy_port = str(odoo_run_port)
+            # change the port odoo runs on
+            # TODO hopefully does not return 8069/odoo_run_port
+            odoo_run_port = find_free_port()
+            options["environment"]["MARABUNTA_WEB_PORT"] = str(odoo_run_port)
+        else:
+            caddy_port = "2019"
+        # TODO pre odoo 13: check if http-port should be changed to xmlrpc-port
+        arg.extend(
+            (
+                "--longpolling-port",
+                str(longpolling_port),
+                "--http-port",
+                str(odoo_run_port),
+            )
+        )
+
+        odoo_host = "" if use_host_network else project_name
+        caddyfile = f"""{{
+    log {{
+        format console
+    }}
+}}
+
+:{caddy_port} {{
+    reverse_proxy /longpolling/* {odoo_host}:{longpolling_port}
+    reverse_proxy {odoo_host}:{odoo_run_port}
+}}
+"""
+        with tempfile.NamedTemporaryFile(
+            delete=False, prefix="Caddyfile."
+        ) as caddyfile_tmp:
+            caddyfile_tmp.write(caddyfile.encode("UTF-8"))
+
+        def cleanup_caddyfile():
+            os.unlink(caddyfile_tmp.name)
+
+        atexit.register(cleanup_caddyfile)
+
+        run_kwargs["mounts"].append(
+            Mount("/etc/caddy/Caddyfile", caddyfile_tmp.name, type="bind")
+        )
+        if use_host_network:
+            run_kwargs["network_mode"] = "host"
+        else:
+            try:
+                client.networks.create(
+                    name=project_name, driver="bridge", check_duplicate=True
+                )
+            except APIError:
+                # TODO handle that more correctly
+                # probably already exists
+                pass
+            run_kwargs.update(
+                {
+                    "network": project_name,
+                    "ports": {caddy_port: odoo_run_port},
+                    "hostname": "caddy",
+                }
+            )
+            options["network"] = project_name
+
+        _logger.info(
+            f"Starting caddy, use http://localhost:{caddy_port}/ to access Odoo"
+        )
+        # TODO there is an error as Odoo is not started up yet when this is started
+        #  so maybe start it after Odoo started? (only happens if odoo windows are
+        #  opened)
+        # TODO also error when Odoo is powered off
+        DockerClient.run("caddy", "latest", run_kwargs)
+
     interactive = True
     if interactive:
         options["stdin_open"] = True
@@ -1242,6 +1347,7 @@
     interactive = True
     if interactive:
         options["stdin_open"] = True
+
     odoo_container = client.containers.create(
         image, command=arg, mounts=mounts, **options
     )
@@ -1254,4 +1360,5 @@
     else:
         # XXX untested
         #  that might need a way to pass any signal to the container
+        #  probably prevent the caddy start bellow
         result = odoo_container.start()
@@ -1257,6 +1364,7 @@
         result = odoo_container.start()
+
     # only remove after getting the return code
     odoo_container.remove()
     return result
 
 
@@ -1258,7 +1366,14 @@
     # only remove after getting the return code
     odoo_container.remove()
     return result
 
 
+def find_free_port():
+    with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
+        s.bind(("", 0))
+        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        return s.getsockname()[1]
+
+
 if __name__ == "__main__":
     sys.exit(main(sys.argv[1:]))
diff --git a/odoo_scripts/docker_postgresql.py b/odoo_scripts/docker_postgresql.py
index bbe2a58b80cf6f95ca8271f773b2de9b580eabd8_b2Rvb19zY3JpcHRzL2RvY2tlcl9wb3N0Z3Jlc3FsLnB5..ca586c1c865ef153e3f5486903f5d91cfa682732_b2Rvb19zY3JpcHRzL2RvY2tlcl9wb3N0Z3Jlc3FsLnB5 100644
--- a/odoo_scripts/docker_postgresql.py
+++ b/odoo_scripts/docker_postgresql.py
@@ -9,6 +9,7 @@
 import sys
 import tempfile
 import time
+from functools import partial
 from typing import Callable, Tuple
 
 import docker
@@ -79,9 +80,7 @@
             for mount_dict in container.attrs["Mounts"]:
                 if mount_dict["Destination"] == "/var/run/postgresql":
                     source = mount_dict["Source"]
-                    break
-    if source:
-        return container, lambda: stop_postgresql(container), source
+            return container, partial(stop_postgresql, container), source
     volumes = {
         pg_data_volume_name: {
             "bind": "/var/lib/postgresql/data",
@@ -107,8 +106,7 @@
     _logger.info("Starting postgresql container %s", pg.name)
     pg.start()
 
-    def stop_pg():
-        return stop_postgresql(pg)
+    stop_pg = partial(stop_postgresql, pg)
 
     if stop_at_exit:
         atexit.register(stop_pg)