diff --git a/CHANGELOG.md b/CHANGELOG.md
index e69de29..632520b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -0,0 +1,2 @@
+# Version 1.0.4
+- fix for https://github.com/WolframResearch/WolframWebEngineForPython/issues/10
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..9e493f9
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,18 @@
+FROM wolframresearch/wolframengine:latest
+
+USER root
+
+RUN apt-get update -y && \
+  apt-get install -y python3 python3-pip && \
+  python3 -m pip install wolframclient
+
+COPY . /tmp/build
+RUN pip3 install /tmp/build && \
+  rm -r /tmp/build && \
+  chown -R wolframengine /srv
+
+USER wolframengine
+EXPOSE 18000
+
+ENTRYPOINT ["/usr/bin/python3", "-m", "wolframwebengine", "--domain", "0.0.0.0"]
+CMD ["/srv"]
\ No newline at end of file
diff --git a/MANIFEST.in b/MANIFEST.in
index a75f28c..13747c8 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,3 +1,7 @@
 include LICENSE PacletInfo.m run.py
 recursive-include wolframwebengine *.m
+recursive-include wolframwebengine *.wl
+recursive-include wolframwebengine *.wxf
+recursive-include wolframwebengine *.mx
+recursive-include wolframwebengine *.json
 recursive-include wolframwebengine/tests *.py
\ No newline at end of file
diff --git a/README.md b/README.md
index d672e62..1d829e7 100644
--- a/README.md
+++ b/README.md
@@ -148,6 +148,33 @@ http://localhost:18000/static.json
 One advantage of a multi-file application structure is that is very easy to extend the application. You can simply place new files into the appropriate location in your application directory and they will automatically be served.
 
 
+## Using Docker
+
+Wolfram Web Engine for Python is available as [a container image from Docker Hub](https://hub.docker.com/r/wolframresearch/wolframwebengineforpython) for use in containerized environments.
+
+This image is based on the [official Wolfram Engine Docker image](https://hub.docker.com/r/wolframresearch/wolframengine); information on product activation and license terms
+is available on the [Docker Hub page](https://hub.docker.com/r/wolframresearch/wolframengine) for the latter image.
+
+```
+# exposes the server on port 8080 of the host machine
+>>> docker run -ti -p 8080:18000 wolframresearch/wolframwebengineforpython --demo
+
+# serve files from the /srv directory
+>>> docker run -ti -p 8080:18000 wolframresearch/wolframwebengineforpython /srv
+```
+
+The commands above do not include activation/licensing configuration; see the [official Wolfram Engine Docker image](https://hub.docker.com/r/wolframresearch/wolframengine) for information on activating the Wolfram Engine kernel.
+
+
+Note regarding on-demand licensing: As Wolfram Web Engine for Python does not use WolframScript, the `-entitlement` command-line option and the `WOLFRAMSCRIPT_ENTITLEMENTID`
+environment variable cannot be used to pass an on-demand license entitlement ID to the Wolfram Engine kernel inside this image.
+As a workaround, the `WOLFRAMINIT` environment variable can be set to pass both the entitlement ID and the license server address to the kernel:
+
+```
+>>> docker run -ti -p 8080:18000 --env WOLFRAMINIT='-pwfile !cloudlm.wolfram.com -entitlement O-WSTD-DA42-GKX4Z6NR2DSZR' wolframresearch/wolframwebengineforpython --demo
+```
+
+
 ## Options
 
 ```
@@ -166,6 +193,8 @@ optional arguments:
   --domain DOMAIN       Insert the domain.
   --kernel KERNEL       Insert the kernel path.
   --poolsize POOLSIZE   Insert the kernel pool size.
+  --startuptimeout SECONDS
+                        Startup timeout (in seconds) for kernels in the pool.
   --cached              The server will cache the WL input expression.
   --lazy                The server will start the kernels on the first
                         request.
@@ -266,6 +295,34 @@ Index           index.wl
 (Press CTRL+C to quit) 
 ```
 
+#### --domain DOMAIN
+
+Allows you to specify the DOMAIN of the webserver. By default the webserver only listens to localhost, use `0.0.0.0` to listen on all network interfaces.
+
+```
+>>> python3 -m wolframwebengine --domain 0.0.0.0
+----------------------------------------------------------------------
+Address         http://0.0.0.0:18000/
+Folder          /Users/rdv/Desktop
+Index           index.wl
+----------------------------------------------------------------------
+(Press CTRL+C to quit) 
+```
+
+#### --initfile FILE
+
+Allows you to specify a custom file containing code to be run when a new kernel is started
+
+```
+>>> python3 -m wolframwebengine --initfile myinit.m
+----------------------------------------------------------------------
+Address         http://localhost:18000/
+Folder          /Users/rdv/Desktop
+Index           index.wl
+----------------------------------------------------------------------
+(Press CTRL+C to quit) 
+```
+
 #### --kernel KERNEL
 
 Allows you to specify the Kernel path
@@ -293,11 +350,42 @@ Index           index.wl
 (Press CTRL+C to quit)
 ```
 
+#### --startuptimeout SECONDS
+
+By default, an attempt to start a kernel will be aborted if the kernel is not ready after 20 seconds. If your application contains long-running initialization code, you may need to raise this timeout.
+```
+>>> python3 -m wolframwebengine
+(...)
+Kernel process started with PID: 485
+Socket exception: Failed to read any message from socket tcp://127.0.0.1:5106 after 20.0 seconds and 245 retries.
+Failed to start.
+
+
+>>> python3 -m wolframwebengine --startuptimeout 50
+(...)
+Kernel process started with PID: 511
+Connected to logging socket: tcp://127.0.0.1:5447
+Kernel 511 is ready. Startup took 35.43 seconds.
+```
+
 #### --lazy 
 
 If the option is present the server will wait for the first request to spawn the kernels, instead of spawning them immediately.
 
 
+#### --client_max_size MB
+
+The maximum amount of megabytes allowed for file upload. Defaults to 10.
+```
+>>> python3 -m wolframwebengine --client_max_size 150
+----------------------------------------------------------------------
+Address         http://localhost:18000/
+Folder          /Users/rdv/Desktop
+Index           index.wl
+----------------------------------------------------------------------
+(Press CTRL+C to quit)
+```
+
 ## Integrating an existing application
 
 Wolfram Web Engine for Python can be used to augment an existing python application instead of creating a new one.
diff --git a/setup.py b/setup.py
index ca3bf82..04b89b9 100644
--- a/setup.py
+++ b/setup.py
@@ -22,17 +22,17 @@ def load_tests():
 
 setup(
     name = 'wolframwebengine',
-    version = '1.0.0.dev1',
+    version = '1.0.4',
     description = 'A Python library with various tools to start a wolfram engine a server content.',
     keywords=['Wolfram Language', 'Wolfram Desktop', 'Mathematica', 'Web Development', 'Wolfram Web Engine'],
     author = 'Wolfram Research, Riccardo Di Virgilio',
     author_email = 'support@wolfram.com, riccardod@wolfram.com',
-    include_package_data=True,
     packages=find_packages(),
     test_suite='setup.load_tests',
     python_requires='>=3.5.3',
+    include_package_data=True,
     install_requires = [
-        'wolframclient>=1.1.0',
+        'wolframclient>=1.1.10',
         'aiohttp>=3.5.4'
     ],
     project_urls={
diff --git a/wolframwebengine/cli/commands/benchmark_server.py b/wolframwebengine/cli/commands/benchmark_server.py
index a118cd0..320ce5b 100644
--- a/wolframwebengine/cli/commands/benchmark_server.py
+++ b/wolframwebengine/cli/commands/benchmark_server.py
@@ -5,8 +5,8 @@
 
 from wolframclient.cli.utils import SimpleCommand
 from wolframclient.serializers import export
-from wolframclient.utils.api import aiohttp
-from wolframclient.utils.asyncio import run_in_loop, wait_all
+from wolframclient.utils.api import aiohttp, asyncio
+from wolframclient.utils.asyncio import run_in_loop
 from wolframclient.utils.encoding import force_text
 from wolframclient.utils.functional import iterate
 
@@ -62,7 +62,7 @@ def generate_tasks(self, requests, clients, url):
     @run_in_loop
     async def wait_for_tasks(self, requests, clients, url):
         # Wait for all of the coroutines to finish.
-        results = await wait_all(self.generate_tasks(requests, clients, url))
+        results = await asyncio.gather(*self.generate_tasks(requests, clients, url))
         results = tuple(iterate(*results))
         return results
 
diff --git a/wolframwebengine/cli/commands/runserver.py b/wolframwebengine/cli/commands/runserver.py
index ea62672..13f4d5c 100644
--- a/wolframwebengine/cli/commands/runserver.py
+++ b/wolframwebengine/cli/commands/runserver.py
@@ -6,13 +6,13 @@
 
 from aiohttp import web
 from aiohttp.abc import AbstractAccessLogger
-
 from wolframclient.cli.utils import SimpleCommand
 from wolframclient.exception import WolframKernelException
 from wolframclient.utils.api import asyncio
 from wolframclient.utils.decorators import cached_property, to_dict
 from wolframclient.utils.functional import first
 from wolframclient.utils.importutils import module_path
+
 from wolframwebengine.server.app import create_session, create_view, is_wl_code
 
 
@@ -38,9 +38,17 @@ def add_arguments(self, parser):
         parser.add_argument("--port", default=18000, help="Insert the port.")
         parser.add_argument("--domain", default="localhost", help="Insert the domain.")
         parser.add_argument("--kernel", default=None, help="Insert the kernel path.")
+        parser.add_argument("--initfile", default=None, help="Insert the initfile path.")
         parser.add_argument(
             "--poolsize", default=1, help="Insert the kernel pool size.", type=int
         )
+        parser.add_argument(
+            "--startuptimeout",
+            default=20,
+            help="Startup timeout (in seconds) for kernels in the pool.",
+            type=int,
+            metavar="SECONDS",
+        )
         parser.add_argument(
             "--cached",
             default=False,
@@ -65,6 +73,14 @@ def add_arguments(self, parser):
             choices=tuple(self.demo_choices.keys()),
         )
 
+        parser.add_argument(
+            "--client_max_size",
+            default=10,
+            dest="client_max_size",
+            help="Maximum size of client uploads, in Mb",
+            type=float,
+        )
+
     def print_line(self, f="", s=""):
         self.print(f.ljust(15), s)
 
@@ -88,26 +104,55 @@ def demo_choices(self):
     def demo_path(self, *args):
         return module_path("wolframwebengine", "examples", "demo", *args)
 
-    def handle(self, domain, port, path, kernel, poolsize, lazy, index, demo, **opts):
+    def handle(
+        self,
+        domain,
+        port,
+        path,
+        kernel,
+        poolsize,
+        lazy,
+        index,
+        demo,
+        initfile,
+        startuptimeout,
+        client_max_size,
+        **opts
+    ):
 
         if demo is None or demo:
             path = self.demo_path(self.demo_choices[demo])
 
         path = os.path.abspath(os.path.expanduser(path))
 
+        client_max_size = int(client_max_size * (1024 ** 2))
+
         try:
-            session = create_session(kernel, poolsize=poolsize)
+            session = create_session(
+                kernel, poolsize=poolsize, initfile=initfile, STARTUP_TIMEOUT=startuptimeout
+            )
 
         except WolframKernelException as e:
             self.print(e)
             self.print("Use --help to display all available options.")
             sys.exit(1)
 
+        loop = asyncio.get_event_loop()
+
         async def main():
 
             view = create_view(session, path, index=index, **opts)
 
-            runner = self.ServerRunner(self.Server(view, access_log_class=self.AccessLogger))
+            def request_factory(*args, **opts):
+                return web.BaseRequest(
+                    *args, **opts, client_max_size=client_max_size, loop=loop
+                )
+
+            runner = self.ServerRunner(
+                self.Server(
+                    view, access_log_class=self.AccessLogger, request_factory=request_factory
+                )
+            )
             await runner.setup()
             await self.TCPSite(runner, domain, port).start()
 
@@ -135,7 +180,7 @@ async def main():
 
             self.print_separator()
 
-            if sys.platform == 'win32':
+            if sys.platform == "win32":
                 self.print_line("(Press CTRL+BREAK to quit)")
             else:
                 self.print_line("(Press CTRL+C to quit)")
@@ -150,7 +195,6 @@ async def main():
             while True:
                 await asyncio.sleep(3600)
 
-        loop = asyncio.get_event_loop()
         try:
             loop.run_until_complete(main())
         except KeyboardInterrupt:
diff --git a/wolframwebengine/examples/aiohttp_application.py b/wolframwebengine/examples/aiohttp_application.py
index bbed942..1e9e9e2 100644
--- a/wolframwebengine/examples/aiohttp_application.py
+++ b/wolframwebengine/examples/aiohttp_application.py
@@ -1,9 +1,9 @@
 from __future__ import absolute_import, print_function, unicode_literals
 
 from aiohttp import web
-
 from wolframclient.evaluation import WolframEvaluatorPool
 from wolframclient.language import wl
+
 from wolframwebengine.web import aiohttp_wl_view
 
 session = WolframEvaluatorPool(poolsize=4)
diff --git a/wolframwebengine/examples/demo/form.wl b/wolframwebengine/examples/demo/form.wl
index 23183bd..c0dfa22 100644
--- a/wolframwebengine/examples/demo/form.wl
+++ b/wolframwebengine/examples/demo/form.wl
@@ -1,9 +1,10 @@
-FormFunction[
-    {"image" -> "Image", "filter" -> ImageEffect[]}, 
-    ImageEffect[#image, #filter] &,
-    "PNG",
+FormPage[
+    {"From" -> "City", "To" -> "City"}, 
+    GeoGraphics[
+        Style[Line[TravelDirections[{#From, #To}]], Thick, Red]
+    ] &, 
     AppearanceRules -> <|
-        "Title" -> "Welcome to Wolfram Web Engine",
+        "Title" -> "Get travel directions for your trip",
         "Description" -> TemplateApply["This is a sample application running on a `` Kernel.", $VersionNumber]
     |>
 ]
\ No newline at end of file
diff --git a/wolframwebengine/examples/djangoapp/manage.py b/wolframwebengine/examples/djangoapp/manage.py
new file mode 100644
index 0000000..78aef40
--- /dev/null
+++ b/wolframwebengine/examples/djangoapp/manage.py
@@ -0,0 +1,25 @@
+from __future__ import absolute_import, print_function, unicode_literals
+
+import sys
+
+
+def main():
+    try:
+        from django.conf import settings
+        from django.core.management import execute_from_command_line
+    except ImportError as exc:
+        raise ImportError(
+            "Couldn't import Django. Are you sure it's installed and "
+            "available on your PYTHONPATH environment variable? Did you "
+            "forget to activate a virtual environment?"
+        ) from exc
+
+    settings.configure(
+        ROOT_URLCONF="wolframwebengine.examples.djangoapp.urls", ALLOWED_HOSTS="*", DEBUG=True
+    )
+
+    execute_from_command_line(sys.argv)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/wolframwebengine/examples/djangoapp/urls.py b/wolframwebengine/examples/djangoapp/urls.py
index 995d436..38fc48d 100644
--- a/wolframwebengine/examples/djangoapp/urls.py
+++ b/wolframwebengine/examples/djangoapp/urls.py
@@ -2,9 +2,9 @@
 
 from django.http import HttpResponse
 from django.urls import path
-
 from wolframclient.evaluation import WolframLanguageSession
 from wolframclient.language import wl
+
 from wolframwebengine.web import django_wl_view
 
 session = WolframLanguageSession()
diff --git a/wolframwebengine/server/app.py b/wolframwebengine/server/app.py
index df55e19..48d8b5d 100644
--- a/wolframwebengine/server/app.py
+++ b/wolframwebengine/server/app.py
@@ -1,12 +1,13 @@
 from __future__ import absolute_import, print_function, unicode_literals
 
+import logging
 import os
 
 from aiohttp import web
-
 from wolframclient.evaluation import WolframEvaluatorPool, WolframLanguageAsyncSession
 from wolframclient.language import wl
 from wolframclient.utils.functional import last
+
 from wolframwebengine.server.explorer import get_wl_handler_path_from_folder
 from wolframwebengine.web import aiohttp_wl_view
 
@@ -29,15 +30,25 @@ def get_wl_handler(path):
     return EXTENSIONS[last(os.path.splitext(path)).lower()]
 
 
-def create_session(path=None, poolsize=1, inputform_string_evaluation=False, **opts):
+def create_session(
+    path=None,
+    poolsize=1,
+    inputform_string_evaluation=False,
+    kernel_loglevel=logging.INFO,
+    **opts
+):
     if poolsize <= 1:
         return WolframLanguageAsyncSession(
-            path, inputform_string_evaluation=inputform_string_evaluation, **opts
+            path,
+            inputform_string_evaluation=inputform_string_evaluation,
+            kernel_loglevel=kernel_loglevel,
+            **opts
         )
     return WolframEvaluatorPool(
         path,
         poolsize=poolsize,
         inputform_string_evaluation=inputform_string_evaluation,
+        kernel_loglevel=kernel_loglevel,
         **opts
     )
 
diff --git a/wolframwebengine/tests/application_aiohttp.py b/wolframwebengine/tests/application_aiohttp.py
index a35c7f3..2436d9c 100644
--- a/wolframwebengine/tests/application_aiohttp.py
+++ b/wolframwebengine/tests/application_aiohttp.py
@@ -5,10 +5,10 @@
 from aiohttp import web
 from aiohttp.formdata import FormData
 from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop
-
 from wolframclient.language import wl
 from wolframclient.utils.functional import first
 from wolframclient.utils.importutils import module_path
+
 from wolframwebengine.server.app import create_session, create_view
 from wolframwebengine.web import aiohttp_wl_view
 from wolframwebengine.web.utils import auto_wait
diff --git a/wolframwebengine/tests/application_django.py b/wolframwebengine/tests/application_django.py
index a0b2902..2a85cc2 100644
--- a/wolframwebengine/tests/application_django.py
+++ b/wolframwebengine/tests/application_django.py
@@ -2,10 +2,10 @@
 
 from django.core.files.uploadedfile import SimpleUploadedFile
 from django.test import Client
-
 from wolframclient.utils.api import json
 from wolframclient.utils.functional import first
 from wolframclient.utils.tests import TestCase as BaseTestCase
+
 from wolframwebengine.web.utils import auto_wait
 
 
diff --git a/wolframwebengine/tests/folder_explorer.py b/wolframwebengine/tests/folder_explorer.py
index a2f7b41..e01df9a 100644
--- a/wolframwebengine/tests/folder_explorer.py
+++ b/wolframwebengine/tests/folder_explorer.py
@@ -4,6 +4,7 @@
 
 from wolframclient.utils.importutils import module_path
 from wolframclient.utils.tests import TestCase as BaseTestCase
+
 from wolframwebengine.server.explorer import get_wl_handler_path_from_folder
 
 
diff --git a/wolframwebengine/web/__init__.py b/wolframwebengine/web/__init__.py
index 50c15d2..9ac6a47 100644
--- a/wolframwebengine/web/__init__.py
+++ b/wolframwebengine/web/__init__.py
@@ -3,6 +3,7 @@
 from functools import partial
 
 from wolframclient.utils.importutils import API
+
 from wolframwebengine.web.utils import is_coroutine_function
 
 available_backends = API(
diff --git a/wolframwebengine/web/aiohttp.py b/wolframwebengine/web/aiohttp.py
index 0e7dacb..80867d5 100644
--- a/wolframwebengine/web/aiohttp.py
+++ b/wolframwebengine/web/aiohttp.py
@@ -6,6 +6,7 @@
 from wolframclient.utils.api import aiohttp
 from wolframclient.utils.decorators import to_dict
 from wolframclient.utils.encoding import force_text
+
 from wolframwebengine.web.utils import (
     make_generate_httpresponse_expression,
     process_generate_httpresponse_expression,
diff --git a/wolframwebengine/web/django.py b/wolframwebengine/web/django.py
index 7e4c31d..26dae4a 100644
--- a/wolframwebengine/web/django.py
+++ b/wolframwebengine/web/django.py
@@ -4,10 +4,10 @@
 from operator import attrgetter
 
 from django.http import HttpResponse
-
 from wolframclient.language import wl
 from wolframclient.utils.decorators import to_dict
 from wolframclient.utils.functional import first, iterate, last
+
 from wolframwebengine.web.utils import (
     auto_wait,
     make_generate_httpresponse_expression,
diff --git a/wolframwebengine/web/utils.py b/wolframwebengine/web/utils.py
index d28baff..31d7eaf 100644
--- a/wolframwebengine/web/utils.py
+++ b/wolframwebengine/web/utils.py
@@ -31,9 +31,9 @@ def make_generate_httpresponse_expression(request, expression):
 
 def process_generate_httpresponse_expression(response):
     if isinstance(response, dict):
-        if not response.get('BodyByteArray', None):
-            #empty byte array is returning a an empty list, we need an empty byte array
-            response['BodyByteArray'] = b''
+        if not response.get("BodyByteArray", None):
+            # empty byte array is returning a an empty list, we need an empty byte array
+            response["BodyByteArray"] = b""
         return response
     return {
         "BodyByteArray": export(response, target_format="wl"),
