diff --git a/CHANGES.rst b/CHANGES.rst
index ae1fb8e34a2..773ba99bda8 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -64,7 +64,7 @@ CHANGES
- Raise `ClientDisconnectedError` to `FlowControlStreamReader` read function
if `ClientSession` object is closed by client when reading data. #1323
--
+- Document deployment without `Gunicorn` #1120
-
diff --git a/docs/deployment.rst b/docs/deployment.rst
new file mode 100644
index 00000000000..134d76c26eb
--- /dev/null
+++ b/docs/deployment.rst
@@ -0,0 +1,308 @@
+=========================
+aiohttp server deployment
+=========================
+
+There are several options for aiohttp server deployment:
+
+* Standalone server
+
+* Running a pool of backend servers behind of :term:`nginx`, HAProxy
+ or other *reverse proxy server*
+
+* Using :term:`gunicorn` behind of *reverse proxy*
+
+Every method has own benefits and disadvantages.
+
+
+.. _aiohttp-deployment-standalone:
+
+Standalone
+==========
+
+Just call :func:`aiohttp.web.run_app` function passing
+:class:`aiohttp.web.Application` instance.
+
+
+The method is very simple and could be the best solution in some
+trivial cases. But it doesn't utilize all CPU cores.
+
+For running multiple aiohttp server instances use *reverse proxies*.
+
+.. _aiohttp-deployment-nginx-supervisord:
+
+Nginx+supervisord
+=================
+
+Running aiohttp servers behind :term:`nginx` makes several advantages.
+
+At first, nginx is the perfect frontend server. It may prevent many
+attacks based on malformed http protocol etc.
+
+Second, running several aiohttp instances behind nginx allows to
+utilize all CPU cores.
+
+Third, nginx serves static files much faster than built-in aiohttp
+static file support.
+
+But this way requires more complex configuration.
+
+Nginx configuration
+--------------------
+
+Here is short extraction about writing Nginx configuration file.
+It doesn't cover all available Nginx options.
+
+For full reference read `Nginx tutorial
+`_ and `official Nginx
+documentation
+`_.
+
+First configure HTTP server itself::
+
+ http {
+ server {
+ listen 80;
+ client_max_body_size 4G;
+
+ server example.com;
+
+ location / {
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_redirect off;
+ proxy_buffering off;
+ proxy_pass http://aiohttp;
+ }
+
+ location /static {
+ # path for static files
+ root /path/to/app/static;
+ }
+
+ }
+ }
+
+This config listens on port ``80`` for server named ``example.com``
+and redirects everything to ``aiohttp`` backend group.
+
+Also it serves static files from ``/path/to/app/static`` path as
+``example.com/static``.
+
+Next we need to configure *aiohttp upstream group*::
+
+ http {
+ upstream aiohttp {
+ fail_timeout=0 means we always retry an upstream even if it failed
+ # to return a good HTTP response
+
+ # TCP servers
+ server 127.0.0.1:8081 fail_timeout=0;
+ server 127.0.0.1:8082 fail_timeout=0;
+ server 127.0.0.1:8083 fail_timeout=0;
+ server 127.0.0.1:8084 fail_timeout=0;
+ }
+ }
+
+All HTTP requests for ``http://example.com`` except ones for
+``http://example.com/static`` will be redirected to
+``127.0.0.1:8081``, ``127.0.0.1:8082``, ``127.0.0.1:8083`` or
+``127.0.0.1:8084`` *backend proxies*.
+
+By default Nginx uses round-robin algorithm for backend selection.
+
+.. note::
+
+ Nginx is not the only existing *reverse proxy server* but the most
+ popular one. Alternatives like HAProxy may be used as well.
+
+Supervisord
+-----------
+
+After configuring Nginx we need to start our aiohttp backends. Better
+to use some tool for starting them automatically after system reboot
+or backend crash.
+
+There are very many ways to do it: Supervisord, Upstart, Systemd,
+Gaffer, Circus, Runit etc.
+
+Here we'll use `Supervisord `_ for example::
+
+ [program:aiohttp_1]
+ cmd=/path/to/aiohttp_example.py 8081
+ user=nobody
+ autostart=true
+ autorestart=true
+
+ [program:aiohttp_2]
+ cmd=/path/to/aiohttp_example.py 8082
+ user=nobody
+ autostart=true
+ autorestart=true
+
+ [program:aiohttp_3]
+ cmd=/path/to/aiohttp_example.py 8083
+ user=nobody
+ autostart=true
+ autorestart=true
+
+ [program:aiohttp_4]
+ cmd=/path/to/aiohttp_example.py 8084
+ user=nobody
+ autostart=true
+ autorestart=true
+
+The config will run four aiohttp server instances, ports are specified
+by command line.
+
+aiohttp server
+--------------
+
+The last step is preparing aiohttp server for working with supervisord.
+
+Assuming we have properly configured :class:`aiohttp.web.Application`
+and port is specified by command line the task is trivial::
+
+ # aiohttp_example.py
+ import argparse
+ from aiohttp import web
+
+ parser = argparse.ArgumentParser(description="aiohttp server example")
+ parser.add_argument('port', type=int)
+
+
+ if __name__ == '__main__':
+ app = web.Application()
+ # configure app
+
+ args = parser.parse_args()
+ web.run_app(app, port=args.port)
+
+For real use cases we perhaps need to configure other things like
+logging etc. but it's out of scope of the topic.
+
+
+.. _aiohttp-deployment-gunicorn:
+
+Nginx+Gunicorn
+==============
+
+aiohttp can be deployed using `Gunicorn
+`_, which is based on a
+pre-fork worker model. Gunicorn launches your app as worker processes
+for handling incoming requests.
+
+In opposite to deployment with :ref:`bare Nginx
+` the solution doesn't need to
+manually run several aiohttp processes and use tool like supervisord
+for monitoring it. But nothing is for free: running aiohttp
+application under gunicorn is slightly slower.
+
+
+Prepare environment
+-------------------
+
+You firstly need to setup your deployment environment. This example is
+based on Ubuntu 14.04.
+
+Create a directory for your application::
+
+ >> mkdir myapp
+ >> cd myapp
+
+Ubuntu has a bug in pyenv, so to create virtualenv you need to do some
+extra manipulation::
+
+ >> pyvenv-3.4 --without-pip venv
+ >> source venv/bin/activate
+ >> curl https://bootstrap.pypa.io/get-pip.py | python
+ >> deactivate
+ >> source venv/bin/activate
+
+Now that the virtual environment is ready, we'll proceed to install
+aiohttp and gunicorn::
+
+ >> pip install gunicorn
+ >> pip install -e git+https://github.com/KeepSafe/aiohttp.git#egg=aiohttp
+
+
+Application
+-----------
+
+Lets write a simple application, which we will save to file. We'll
+name this file *my_app_module.py*::
+
+ from aiohttp import web
+
+ def index(request):
+ return web.Response(text="Welcome home!")
+
+
+ my_web_app = web.Application()
+ my_web_app.router.add_get('/', index)
+
+
+Start Gunicorn
+--------------
+
+When `Running Gunicorn
+`_, you provide the name
+of the module, i.e. *my_app_module*, and the name of the app,
+i.e. *my_web_app*, along with other `Gunicorn Settings
+`_ provided as
+command line flags or in your config file.
+
+In this case, we will use:
+
+* the *'--bind'* flag to set the server's socket address;
+* the *'--worker-class'* flag to tell Gunicorn that we want to use a
+ custom worker subclass instead of one of the Gunicorn default worker
+ types;
+* you may also want to use the *'--workers'* flag to tell Gunicorn how
+ many worker processes to use for handling requests. (See the
+ documentation for recommendations on `How Many Workers?
+ `_)
+
+The custom worker subclass is defined in
+*aiohttp.worker.GunicornWebWorker* and should be used instead of the
+*gaiohttp* worker provided by Gunicorn, which supports only
+aiohttp.wsgi applications::
+
+ >> gunicorn my_app_module:my_web_app --bind localhost:8080 --worker-class aiohttp.worker.GunicornWebWorker
+ [2015-03-11 18:27:21 +0000] [1249] [INFO] Starting gunicorn 19.3.0
+ [2015-03-11 18:27:21 +0000] [1249] [INFO] Listening at: http://127.0.0.1:8080 (1249)
+ [2015-03-11 18:27:21 +0000] [1249] [INFO] Using worker: aiohttp.worker.GunicornWebWorker
+ [2015-03-11 18:27:21 +0000] [1253] [INFO] Booting worker with pid: 1253
+
+Gunicorn is now running and ready to serve requests to your app's
+worker processes.
+
+.. note::
+
+ If you want to use an alternative asyncio event loop
+ `uvloop `_, you can use the
+ ``aiohttp.worker.GunicornUVLoopWebWorker`` worker class.
+
+
+More information
+----------------
+
+The Gunicorn documentation recommends deploying Gunicorn behind an
+Nginx proxy server. See the `official documentation
+`_ for more
+information about suggested nginx configuration.
+
+
+Logging configuration
+---------------------
+
+``aiohttp`` and ``gunicorn`` use different format for specifying access log.
+
+By default aiohttp uses own defaults::
+
+ '%a %l %u %t "%r" %s %b "%{Referrer}i" "%{User-Agent}i"'
+
+For more information please read :ref:`Format Specification for Accees
+Log `.
+
+.. disqus::
+ :title: aiohttp deployment with gunicorn
diff --git a/docs/glossary.rst b/docs/glossary.rst
index 95d213d9597..0288a848e8c 100644
--- a/docs/glossary.rst
+++ b/docs/glossary.rst
@@ -45,6 +45,13 @@
https://pypi.python.org/pypi/cchardet/
+ gunicorn
+
+ Gunicorn 'Green Unicorn' is a Python WSGI HTTP Server for
+ UNIX.
+
+ http://gunicorn.org/
+
keep-alive
A technique for communicating between HTTP client and server
@@ -54,6 +61,13 @@
It makes communication faster by getting rid of connection
establishment for every request.
+ nginx
+
+ Nginx [engine x] is an HTTP and reverse proxy server, a mail
+ proxy server, and a generic TCP/UDP proxy server.
+
+ https://nginx.org/en/
+
resource
A concept reflects the HTTP **path**, every resource corresponds
diff --git a/docs/gunicorn.rst b/docs/gunicorn.rst
deleted file mode 100644
index 39a7c03fe68..00000000000
--- a/docs/gunicorn.rst
+++ /dev/null
@@ -1,118 +0,0 @@
-.. _deployment-using-gunicorn:
-
-Deployment using Gunicorn
-=========================
-
-aiohttp can be deployed using `Gunicorn
-`_, which is based on a
-pre-fork worker model. Gunicorn launches your app as worker processes
-for handling incoming requests.
-
-Prepare environment
--------------------
-
-You firstly need to setup your deployment environment. This example is
-based on Ubuntu 14.04.
-
-Create a directory for your application::
-
- >> mkdir myapp
- >> cd myapp
-
-Ubuntu has a bug in pyenv, so to create virtualenv you need to do some
-extra manipulation::
-
- >> pyvenv-3.4 --without-pip venv
- >> source venv/bin/activate
- >> curl https://bootstrap.pypa.io/get-pip.py | python
- >> deactivate
- >> source venv/bin/activate
-
-Now that the virtual environment is ready, we'll proceed to install
-aiohttp and gunicorn::
-
- >> pip install gunicorn
- >> pip install -e git+https://github.com/KeepSafe/aiohttp.git#egg=aiohttp
-
-
-Application
------------
-
-Lets write a simple application, which we will save to file. We'll
-name this file *my_app_module.py*::
-
- from aiohttp import web
-
- def index(request):
- return web.Response(text="Welcome home!")
-
-
- my_web_app = web.Application()
- my_web_app.router.add_get('/', index)
-
-
-Start Gunicorn
---------------
-
-When `Running Gunicorn
-`_, you provide the name
-of the module, i.e. *my_app_module*, and the name of the app,
-i.e. *my_web_app*, along with other `Gunicorn Settings
-`_ provided as
-command line flags or in your config file.
-
-In this case, we will use:
-
-* the *'--bind'* flag to set the server's socket address;
-* the *'--worker-class'* flag to tell Gunicorn that we want to use a
- custom worker subclass instead of one of the Gunicorn default worker
- types;
-* you may also want to use the *'--workers'* flag to tell Gunicorn how
- many worker processes to use for handling requests. (See the
- documentation for recommendations on `How Many Workers?
- `_)
-
-The custom worker subclass is defined in
-*aiohttp.worker.GunicornWebWorker* and should be used instead of the
-*gaiohttp* worker provided by Gunicorn, which supports only
-aiohttp.wsgi applications::
-
- >> gunicorn my_app_module:my_web_app --bind localhost:8080 --worker-class aiohttp.worker.GunicornWebWorker
- [2015-03-11 18:27:21 +0000] [1249] [INFO] Starting gunicorn 19.3.0
- [2015-03-11 18:27:21 +0000] [1249] [INFO] Listening at: http://127.0.0.1:8080 (1249)
- [2015-03-11 18:27:21 +0000] [1249] [INFO] Using worker: aiohttp.worker.GunicornWebWorker
- [2015-03-11 18:27:21 +0000] [1253] [INFO] Booting worker with pid: 1253
-
-Gunicorn is now running and ready to serve requests to your app's
-worker processes.
-
-.. note::
-
- If you want to use an alternative asyncio event loop
- `uvloop `_, you can use the
- ``aiohttp.worker.GunicornUVLoopWebWorker`` worker class.
-
-
-More information
-----------------
-
-The Gunicorn documentation recommends deploying Gunicorn behind an
-Nginx proxy server. See the `official documentation
-`_ for more
-information about suggested nginx configuration.
-
-
-Logging configuration
----------------------
-
-``aiohttp`` and ``gunicorn`` use different format for specifying access log.
-
-By default aiohttp uses own defaults::
-
- '%a %l %u %t "%r" %s %b "%{Referrer}i" "%{User-Agent}i"'
-
-For more information please read :ref:`Format Specification for Accees
-Log `.
-
-.. disqus::
- :title: aiohttp deployment with gunicorn
diff --git a/docs/index.rst b/docs/index.rst
index a78a204c7c4..e531137f42f 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -205,7 +205,7 @@ Contents
api
logging
testing
- gunicorn
+ deployment
faq
essays
contributing
diff --git a/docs/logging.rst b/docs/logging.rst
index 342bcb6d9df..b6ad5ed1f51 100644
--- a/docs/logging.rst
+++ b/docs/logging.rst
@@ -95,7 +95,7 @@ Default access log format is::
.. note::
When `Gunicorn `_ is used for
- :ref:`deployment ` its default access log format
+ :ref:`deployment ` its default access log format
will be automatically replaced with the default aiohttp's access log format.
If Gunicorn's option access_logformat_ is
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
index 8a5cb60a5b4..76a6d376d74 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -17,6 +17,8 @@ autogenerates
autogeneration
awaitable
BaseEventLoop
+backend
+backends
basename
BasicAuth
BodyPartReader
@@ -61,6 +63,7 @@ epoll
fallback
filename
finalizers
+frontend
getall
gethostbyname
github
@@ -173,6 +176,10 @@ subpackage
subprotocol
subprotocols
subtype
+Supervisord
+supervisord
+Systemd
+Runit
Svetlov
symlink
symlinks