+
+{{ content }}
diff --git a/docs/contrib/docs.md b/docs/contrib/docs.md
new file mode 100644
index 0000000..52c5da5
--- /dev/null
+++ b/docs/contrib/docs.md
@@ -0,0 +1,16 @@
+---
+title: "Documentation"
+layout: contrib
+---
+
+### Running docs locally
+
+To build the docs, run [jekyll](http://jekyllrb.com/):
+
+```
+jekyll serve
+```
+
+If you rather use Vagrant, see [these instructions][v].
+
+[v]: {{site.baseurl}}contrib/vagrant/
diff --git a/docs/contrib/github.md b/docs/contrib/github.md
new file mode 100644
index 0000000..18c5bfd
--- /dev/null
+++ b/docs/contrib/github.md
@@ -0,0 +1,11 @@
+---
+title: "Contributing to RQ"
+layout: contrib
+---
+
+If you'd like to contribute to RQ, simply [fork](https://github.com/nvie/rq)
+the project on GitHub and submit a pull request.
+
+Please bear in mind the philosiphy behind RQ: it should rather remain small and
+simple, than packed with features. And it should value insightfulness over
+performance.
diff --git a/docs/contrib/index.md b/docs/contrib/index.md
new file mode 100644
index 0000000..f503812
--- /dev/null
+++ b/docs/contrib/index.md
@@ -0,0 +1,63 @@
+---
+title: "RQ: Simple job queues for Python"
+layout: contrib
+---
+
+This document describes how RQ works internally when enqueuing or dequeueing.
+
+
+## Enqueueing internals
+
+Whenever a function call gets enqueued, RQ does two things:
+
+* It creates a job instance representing the delayed function call and persists
+ it in a Redis [hash][h]; and
+* It pushes the given job's ID onto the requested Redis queue.
+
+All jobs are stored in Redis under the `rq:job:` prefix, for example:
+
+ rq:job:55528e58-9cac-4e05-b444-8eded32e76a1
+
+The keys of such a job [hash][h] are:
+
+ created_at => '2012-02-13 14:35:16+0000'
+ enqueued_at => '2012-02-13 14:35:16+0000'
+ origin => 'default'
+ data =>
+ description => "count_words_at_url('http://nvie.com')"
+
+Depending on whether or not the job has run successfully or has failed, the
+following keys are available, too:
+
+ ended_at => '2012-02-13 14:41:33+0000'
+ result =>
+ exc_info =>
+
+[h]: http://redis.io/topics/data-types#hashes
+
+
+## Dequeueing internals
+
+Whenever a dequeue is requested, an RQ worker does two things:
+
+* It pops a job ID from the queue, and fetches the job data belonging to that
+ job ID;
+* It starts executing the function call.
+* If the job succeeds, its return value is written to the `result` hash key and
+ the hash itself is expired after 500 seconds; or
+* If the job failes, the exception information is written to the `exc_info`
+ hash key and the job ID is pushed onto the `failed` queue.
+
+
+## Cancelling jobs
+
+Any job ID that is encountered by a worker for which no job hash is found in
+Redis is simply ignored. This makes it easy to cancel jobs by simply removing
+the job hash. In Python:
+
+ from rq import cancel_job
+ cancel_job('2eafc1e6-48c2-464b-a0ff-88fd199d039c')
+
+Note that it is irrelevant on which queue the job resides. When a worker
+eventually pops the job ID from the queue and notes that the Job hash does not
+exist (anymore), it simply discards the job ID and continues with the next.
diff --git a/docs/contrib/testing.md b/docs/contrib/testing.md
new file mode 100644
index 0000000..8abcdc1
--- /dev/null
+++ b/docs/contrib/testing.md
@@ -0,0 +1,16 @@
+---
+title: "Testing"
+layout: contrib
+---
+
+### Testing RQ locally
+
+To run tests locally;
+
+```
+tox
+```
+
+If you rather use Vagrant, see [these instructions][v].
+
+[v]: {{site.baseurl}}contrib/vagrant/
diff --git a/docs/contrib/vagrant.md b/docs/contrib/vagrant.md
new file mode 100644
index 0000000..c114c7c
--- /dev/null
+++ b/docs/contrib/vagrant.md
@@ -0,0 +1,50 @@
+---
+title: "Using Vagrant"
+layout: contrib
+---
+
+If you don't feel like installing dependencies on your main development
+machine, you can use [Vagrant](https://www.vagrantup.com/). Here's how you run
+your tests and build the documentation on Vagrant.
+
+
+### Running tests in Vagrant
+
+To create a working Vagrant environment, use the following;
+
+```
+vagrant init ubuntu/trusty64
+vagrant up
+vagrant ssh -- "sudo apt-get -y install redis-server python-dev python-pip"
+vagrant ssh -- "sudo pip install --no-input redis hiredis mock"
+vagrant ssh -- "(cd /vagrant; ./run_tests)"
+```
+
+
+### Running docs on Vagrant
+
+```
+vagrant init ubuntu/trusty64
+vagrant up
+vagrant ssh -- "sudo apt-get -y install ruby-dev nodejs"
+vagrant ssh -- "sudo gem install jekyll"
+vagrant ssh -- "(cd /vagrant; jekyll serve)"
+```
+
+You'll also need to add a port forward entry to your `Vagrantfile`;
+
+```
+config.vm.network "forwarded_port", guest: 4000, host: 4001
+```
+
+Then you can access the docs using;
+
+```
+http://127.0.0.1:4001
+```
+
+You also may need to forcibly kill Jekyll if you ctrl+c;
+
+```
+vagrant ssh -- "sudo killall -9 jekyll"
+```
diff --git a/docs/css/reset.css b/docs/css/reset.css
new file mode 100644
index 0000000..e29c0f5
--- /dev/null
+++ b/docs/css/reset.css
@@ -0,0 +1,48 @@
+/* http://meyerweb.com/eric/tools/css/reset/
+ v2.0 | 20110126
+ License: none (public domain)
+*/
+
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed,
+figure, figcaption, footer, header, hgroup,
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-size: 100%;
+ font: inherit;
+ vertical-align: baseline;
+}
+/* HTML5 display-role reset for older browsers */
+article, aside, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section {
+ display: block;
+}
+body {
+ line-height: 1;
+}
+ol, ul {
+ list-style: none;
+}
+blockquote, q {
+ quotes: none;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+ content: '';
+ content: none;
+}
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
diff --git a/docs/css/screen.css b/docs/css/screen.css
new file mode 100644
index 0000000..212c4da
--- /dev/null
+++ b/docs/css/screen.css
@@ -0,0 +1,347 @@
+@import url("reset.css");
+
+html
+{
+ font-size: 62.5%;
+ -webkit-text-size-adjust: 110%;
+}
+
+body
+{
+ background: #DBE0DF url(../img/bg.png) 50% 0 repeat-y !important;
+ height: 100%;
+ font-family: Lato, sans-serif;
+ font-size: 150%;
+ font-weight: 300;
+ line-height: 1.55;
+ padding: 0 30px 80px;
+}
+
+header
+{
+ background: url(../img/ribbon.png) no-repeat 50% 0;
+ max-width: 430px;
+ width: 100%;
+ text-align: center;
+
+ padding: 240px 0 1em 0;
+ border-bottom: 1px dashed #e1e1e1;
+ margin: 0 auto 2em auto;
+}
+
+ul.inline
+{
+ list-style-type: none;
+ margin: 0;
+ padding: 0;
+}
+
+ul.inline li
+{
+ display: inline;
+ margin: 0 10px;
+}
+
+.subnav ul.inline li
+{
+ margin: 0 6px;
+}
+
+header a
+{
+ color: #3a3a3a;
+ border: 0;
+ font-size: 110%;
+ font-weight: 600;
+ text-decoration: none;
+ transition: color linear 0.1s;
+ -webkit-transition: color linear 0.1s;
+ -moz-transition: color linear 0.1s;
+}
+
+header a:hover
+{
+ border-bottom-color: rgba(0, 0, 0, 0.1);
+ color: rgba(0, 0, 0, 0.4);
+}
+
+.subnav
+{
+ text-align: center;
+ font-size: 94%;
+ margin: -3em auto 2em auto;
+}
+
+.subnav li
+{
+ background-color: white;
+ padding: 0 4px;
+}
+
+.subnav a
+{
+ text-decoration: none;
+}
+
+.container
+{
+ margin: 0 auto;
+ max-width: 430px;
+ width: 100%;
+}
+
+footer
+{
+ margin: 2em auto;
+ max-width: 430px;
+ width: 100%;
+ border-top: 1px dashed #e1e1e1;
+ padding-top: 1em;
+}
+
+footer p
+{
+ text-align: center;
+ font-size: 90%;
+ font-style: italic;
+ margin-bottom: 0;
+}
+
+footer a
+{
+ font-weight: 400;
+}
+
+pre
+{
+ margin: 0 0 1em 1em;
+ padding: 1em 1.8em;
+ color: #222;
+ border-bottom: 1px solid #ccc;
+ border-right: 1px solid #ccc;
+ background: #F3F3F0 url(../img/bq.png) top left no-repeat;
+ line-height: 1.15em;
+ overflow: auto;
+}
+
+code
+{
+ font-family: 'Droid Sans Mono', monospace;
+ font-weight: 400;
+ font-size: 80%;
+
+ line-height: 0.5em;
+
+ border: 1px solid #efeaea;
+ padding: 0.2em 0.4em;
+}
+
+pre code
+{
+ border: none;
+ padding: 0;
+}
+
+h1
+{
+ font-size: 280%;
+ font-weight: 400;
+}
+
+.ir
+{
+ display: block;
+ border: 0;
+ text-indent: -999em;
+ overflow: hidden;
+ background-color: transparent;
+ background-repeat: no-repeat;
+ text-align: left;
+ direction: ltr;
+}
+
+.ir br
+{
+ display: none;
+}
+
+h1#logo
+{
+ margin: 0 auto;
+ width: 305px;
+ height: 186px;
+ background-image: url(../img/logo2.png);
+}
+
+/*
+h1:hover:after
+{
+ color: rgba(0, 0, 0, 0.3);
+ content: attr(title);
+ font-size: 60%;
+ font-weight: 300;
+ margin: 0 0 0 0.5em;
+}
+*/
+
+h2
+{
+ font-size: 200%;
+ font-weight: 400;
+ margin: 0 0 0.4em;
+}
+
+h3
+{
+ font-size: 135%;
+ font-weight: 400;
+ margin: 0 0 0.25em;
+}
+
+p
+{
+ color: rgba(0, 0, 0, 0.7);
+ margin: 0 0 1em;
+}
+
+p:last-child
+{
+ margin-bottom: 0;
+}
+
+img
+{
+ border-radius: 4px;
+ float: left;
+ margin: 6px 12px 15px 0;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+}
+
+.nomargin
+{
+ margin: 0;
+}
+
+a
+{
+ border-bottom: 1px solid rgba(65, 131, 196, 0.1);
+ color: rgb(65, 131, 196);
+ font-weight: 600;
+ text-decoration: none;
+ transition: color linear 0.1s;
+ -webkit-transition: color linear 0.1s;
+ -moz-transition: color linear 0.1s;
+}
+
+a:hover
+{
+ border-bottom-color: rgba(0, 0, 0, 0.1);
+ color: rgba(0, 0, 0, 0.4);
+}
+
+em
+{
+ font-style: italic;
+}
+
+strong
+{
+ font-weight: 600;
+}
+
+acronym
+{
+ border-bottom: 1px dotted rgba(0, 0, 0, 0.1);
+ cursor: help;
+}
+
+blockquote
+{
+ font-style: italic;
+ padding: 1em;
+}
+
+ul
+{
+ list-style: circle;
+ margin: 0 0 1em 2em;
+ color: rgba(0, 0, 0, 0.7);
+}
+
+li
+{
+ font-size: 100%;
+}
+
+ol
+{
+ list-style-type: decimal;
+ margin: 0 0 1em 2em;
+ color: rgba(0, 0, 0, 0.7);
+}
+
+li
+{
+ font-size: 100%;
+}
+
+.warning
+{
+ position: relative;
+ padding: 7px 15px;
+ margin-bottom: 18px;
+ color: #404040;
+ background-color: #eedc94;
+ background-repeat: repeat-x;
+ background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1), to(#eedc94));
+ background-image: -moz-linear-gradient(top, #fceec1, #eedc94);
+ background-image: -ms-linear-gradient(top, #fceec1, #eedc94);
+ background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fceec1), color-stop(100%, #eedc94));
+ background-image: -webkit-linear-gradient(top, #fceec1, #eedc94);
+ background-image: -o-linear-gradient(top, #fceec1, #eedc94);
+ background-image: linear-gradient(top, #fceec1, #eedc94);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fceec1', endColorstr='#eedc94', GradientType=0);
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ border-color: #eedc94 #eedc94 #e4c652;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+ border-width: 1px;
+ border-style: solid;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
+ -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
+}
+
+.warning p
+{
+}
+
+.alert-message .close {
+ *margin-top: 3px;
+ /* IE7 spacing */
+
+}
+
+/*
+@media screen and (max-width: 1400px)
+{
+ body
+ {
+ padding-bottom: 60px;
+ padding-top: 60px;
+ }
+}
+
+@media screen and (max-width: 600px)
+{
+ body
+ {
+ padding-bottom: 40px;
+ padding-top: 30px;
+ }
+}
+*/
diff --git a/docs/css/syntax.css b/docs/css/syntax.css
new file mode 100644
index 0000000..c82ff1f
--- /dev/null
+++ b/docs/css/syntax.css
@@ -0,0 +1,61 @@
+.highlight { background: #ffffff; }
+.highlight .c { color: #999988; } /* Comment */
+.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
+.highlight .k { font-weight: bold; color: #555555; } /* Keyword */
+.highlight .kn { font-weight: bold; color: #555555; } /* Keyword */
+.highlight .o { font-weight: bold; color: #555555; } /* Operator */
+.highlight .cm { color: #999988; } /* Comment.Multiline */
+.highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */
+.highlight .c1 { color: #999988; } /* Comment.Single */
+.highlight .cs { color: #999999; font-weight: bold; } /* Comment.Special */
+.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
+.highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */
+.highlight .ge {} /* Generic.Emph */
+.highlight .gr { color: #aa0000 } /* Generic.Error */
+.highlight .gh { color: #999999 } /* Generic.Heading */
+.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
+.highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */
+.highlight .go { color: #888888 } /* Generic.Output */
+.highlight .gp { color: #555555 } /* Generic.Prompt */
+.highlight .gs { font-weight: bold } /* Generic.Strong */
+.highlight .gu { color: #aaaaaa } /* Generic.Subheading */
+.highlight .gt { color: #aa0000 } /* Generic.Traceback */
+.highlight .kc { font-weight: bold } /* Keyword.Constant */
+.highlight .kd { font-weight: bold } /* Keyword.Declaration */
+.highlight .kp { font-weight: bold } /* Keyword.Pseudo */
+.highlight .kr { font-weight: bold } /* Keyword.Reserved */
+.highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */
+.highlight .m { color: #009999 } /* Literal.Number */
+.highlight .s { color: #d14 } /* Literal.String */
+.highlight .na { color: #008080 } /* Name.Attribute */
+.highlight .nb { color: #0086B3 } /* Name.Builtin */
+.highlight .nc { color: #445588; font-weight: bold } /* Name.Class */
+.highlight .no { color: #008080 } /* Name.Constant */
+.highlight .ni { color: #800080 } /* Name.Entity */
+.highlight .ne { color: #aa0000; font-weight: bold } /* Name.Exception */
+.highlight .nf { color: #aa0000; font-weight: bold } /* Name.Function */
+.highlight .nn { color: #555555 } /* Name.Namespace */
+.highlight .nt { color: #000080 } /* Name.Tag */
+.highlight .nv { color: #008080 } /* Name.Variable */
+.highlight .ow { font-weight: bold } /* Operator.Word */
+.highlight .w { color: #bbbbbb } /* Text.Whitespace */
+.highlight .mf { color: #009999 } /* Literal.Number.Float */
+.highlight .mh { color: #009999 } /* Literal.Number.Hex */
+.highlight .mi { color: #009999 } /* Literal.Number.Integer */
+.highlight .mo { color: #009999 } /* Literal.Number.Oct */
+.highlight .sb { color: #d14 } /* Literal.String.Backtick */
+.highlight .sc { color: #d14 } /* Literal.String.Char */
+.highlight .sd { color: #d14 } /* Literal.String.Doc */
+.highlight .s2 { color: #d14 } /* Literal.String.Double */
+.highlight .se { color: #d14 } /* Literal.String.Escape */
+.highlight .sh { color: #d14 } /* Literal.String.Heredoc */
+.highlight .si { color: #d14 } /* Literal.String.Interpol */
+.highlight .sx { color: #d14 } /* Literal.String.Other */
+.highlight .sr { color: #009926 } /* Literal.String.Regex */
+.highlight .s1 { color: #d14 } /* Literal.String.Single */
+.highlight .ss { color: #990073 } /* Literal.String.Symbol */
+.highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */
+.highlight .vc { color: #008080 } /* Name.Variable.Class */
+.highlight .vg { color: #008080 } /* Name.Variable.Global */
+.highlight .vi { color: #008080 } /* Name.Variable.Instance */
+.highlight .il { color: #009999 } /* Literal.Number.Integer.Long */
diff --git a/docs/design/favicon.psd b/docs/design/favicon.psd
new file mode 100644
index 0000000..9674bb8
Binary files /dev/null and b/docs/design/favicon.psd differ
diff --git a/docs/design/rq-logo.psd b/docs/design/rq-logo.psd
new file mode 100644
index 0000000..52a60c7
Binary files /dev/null and b/docs/design/rq-logo.psd differ
diff --git a/docs/docs/connections.md b/docs/docs/connections.md
new file mode 100644
index 0000000..918b074
--- /dev/null
+++ b/docs/docs/connections.md
@@ -0,0 +1,145 @@
+---
+title: "RQ: Connections"
+layout: docs
+---
+
+Although RQ features the `use_connection()` command for convenience, it
+is deprecated, since it pollutes the global namespace. Instead, prefer explicit
+connection management using the `with Connection(...):` context manager, or
+pass in Redis connection references to queues directly.
+
+
+## Single Redis connection (easy)
+
+
+
+ Note:
+
+ The use of use_connection is deprecated.
+ Please don't use `use_connection` in your scripts.
+ Instead, use explicit connection management.
+
+
+
+In development mode, to connect to a default, local Redis server:
+
+{% highlight python %}
+from rq import use_connection
+use_connection()
+{% endhighlight %}
+
+In production, to connect to a specific Redis server:
+
+{% highlight python %}
+from redis import Redis
+from rq import use_connection
+
+redis = Redis('my.host.org', 6789, password='secret')
+use_connection(redis)
+{% endhighlight %}
+
+Be aware of the fact that `use_connection` pollutes the global namespace. It
+also implies that you can only ever use a single connection.
+
+
+## Multiple Redis connections
+
+However, the single connection pattern facilitates only those cases where you
+connect to a single Redis instance, and where you affect global context (by
+replacing the existing connection with the `use_connection()` call). You can
+only use this pattern when you are in full control of your web stack.
+
+In any other situation, or when you want to use multiple connections, you
+should use `Connection` contexts or pass connections around explicitly.
+
+
+### Explicit connections (precise, but tedious)
+
+Each RQ object instance (queues, workers, jobs) has a `connection` keyword
+argument that can be passed to the constructor. Using this, you don't need to
+use `use_connection()`. Instead, you can create your queues like this:
+
+{% highlight python %}
+from rq import Queue
+from redis import Redis
+
+conn1 = Redis('localhost', 6379)
+conn2 = Redis('remote.host.org', 9836)
+
+q1 = Queue('foo', connection=conn1)
+q2 = Queue('bar', connection=conn2)
+{% endhighlight %}
+
+Every job that is enqueued on a queue will know what connection it belongs to.
+The same goes for the workers.
+
+This approach is very precise, but rather verbose, and therefore, tedious.
+
+
+### Connection contexts (precise and concise)
+
+There is a better approach if you want to use multiple connections, though.
+Each RQ object instance, upon creation, will use the topmost Redis connection
+on the RQ connection stack, which is a mechanism to temporarily replace the
+default connection to be used.
+
+An example will help to understand it:
+
+{% highlight python %}
+from rq import Queue, Connection
+from redis import Redis
+
+with Connection(Redis('localhost', 6379)):
+ q1 = Queue('foo')
+ with Connection(Redis('remote.host.org', 9836)):
+ q2 = Queue('bar')
+ q3 = Queue('qux')
+
+assert q1.connection != q2.connection
+assert q2.connection != q3.connection
+assert q1.connection == q3.connection
+{% endhighlight %}
+
+You can think of this as if, within the `Connection` context, every newly
+created RQ object instance will have the `connection` argument set implicitly.
+Enqueueing a job with `q2` will enqueue it in the second (remote) Redis
+backend, even when outside of the connection context.
+
+
+### Pushing/popping connections
+
+If your code does not allow you to use a `with` statement, for example, if you
+want to use this to set up a unit test, you can use the `push_connection()` and
+`pop_connection()` methods instead of using the context manager.
+
+{% highlight python %}
+import unittest
+from rq import Queue
+from rq import push_connection, pop_connection
+
+class MyTest(unittest.TestCase):
+ def setUp(self):
+ push_connection(Redis())
+
+ def tearDown(self):
+ pop_connection()
+
+ def test_foo(self):
+ """Any queues created here use local Redis."""
+ q = Queue()
+ ...
+{% endhighlight %}
+
+### Sentinel support
+
+To use redis sentinel, you must specify a dictionary in the configuration file.
+Using this setting in conjunction with the systemd or docker containers with the
+automatic restart option allows workers and RQ to have a fault-tolerant connection to the redis.
+
+{% highlight python %}
+SENTINEL: {'INSTANCES':[('remote.host1.org', 26379), ('remote.host2.org', 26379), ('remote.host3.org', 26379)],
+ 'SOCKET_TIMEOUT': None,
+ 'PASSWORD': 'secret',
+ 'DB': 2,
+ 'MASTER_NAME': 'master'}
+{% endhighlight %}
diff --git a/docs/docs/exceptions.md b/docs/docs/exceptions.md
new file mode 100644
index 0000000..8b1080c
--- /dev/null
+++ b/docs/docs/exceptions.md
@@ -0,0 +1,81 @@
+---
+title: "RQ: Exceptions"
+layout: docs
+---
+
+Jobs can fail due to exceptions occurring. When your RQ workers run in the
+background, how do you get notified of these exceptions?
+
+## Default: the `failed` queue
+
+The default safety net for RQ is the `failed` queue. Every job that fails
+execution is stored in here, along with its exception information (type,
+value, traceback). While this makes sure no failing jobs "get lost", this is
+of no use to get notified pro-actively about job failure.
+
+
+## Custom exception handlers
+
+Starting from version 0.3.1, RQ supports registering custom exception
+handlers. This makes it possible to replace the default behaviour (sending
+the job to the `failed` queue) altogether, or to take additional steps when an
+exception occurs.
+
+To do this, register your custom exception handler to an RQ worker as follows:
+
+{% highlight python %}
+with Connection():
+ q = Queue()
+ w = Worker([q])
+ w.push_exc_handler(my_handler)
+ w.work()
+{% endhighlight %}
+
+While the exception handlers are a FILO stack, most times you only want to
+register a single handler. Therefore, for convenience, you can pass it to the
+constructor directly, too:
+
+{% highlight python %}
+with Connection():
+ w = Worker([q], exception_handlers=[my_handler, self.move_to_failed_queue])
+ ...
+{% endhighlight %}
+
+The handler itself is a function that takes the following parameters: `job`,
+`exc_type`, `exc_value` and `traceback`:
+
+{% highlight python %}
+def my_handler(job, exc_type, exc_value, traceback):
+ # do custom things here
+ # for example, write the exception info to a DB
+ ...
+{% endhighlight %}
+
+You might also see the three exception arguments encoded as:
+
+{% highlight python %}
+def my_handler(job, *exc_info):
+ # do custom things here
+ ...
+{% endhighlight %}
+
+
+## Chaining exception handlers
+
+The handler itself is responsible for deciding whether or not the exception
+handling is done, or should fall through to the next handler on the stack.
+The handler can indicate this by returning a boolean. `False` means stop
+processing exceptions, `True` means continue and fall through to the next
+exception handler on the stack.
+
+It's important to know for implementors that, by default, when the handler
+doesn't have an explicit return value (thus `None`), this will be interpreted
+as `True` (i.e. continue with the next handler).
+
+To replace the default behaviour (i.e. moving the job to the `failed` queue),
+use a custom exception handler that doesn't fall through, for example:
+
+{% highlight python %}
+def black_hole(job, *exc_info):
+ return False
+{% endhighlight %}
diff --git a/docs/docs/index.md b/docs/docs/index.md
new file mode 100644
index 0000000..8f8bb0b
--- /dev/null
+++ b/docs/docs/index.md
@@ -0,0 +1,229 @@
+---
+title: "RQ: Documentation"
+layout: docs
+---
+
+A _job_ is a Python object, representing a function that is invoked
+asynchronously in a worker (background) process. Any Python function can be
+invoked asynchronously, by simply pushing a reference to the function and its
+arguments onto a queue. This is called _enqueueing_.
+
+
+## Enqueueing jobs
+
+To put jobs on queues, first declare a function:
+
+{% highlight python %}
+import requests
+
+def count_words_at_url(url):
+ resp = requests.get(url)
+ return len(resp.text.split())
+{% endhighlight %}
+
+Noticed anything? There's nothing special about this function! Any Python
+function call can be put on an RQ queue.
+
+To put this potentially expensive word count for a given URL in the background,
+simply do this:
+
+{% highlight python %}
+from rq import Queue
+from redis import Redis
+from somewhere import count_words_at_url
+
+# Tell RQ what Redis connection to use
+redis_conn = Redis()
+q = Queue(connection=redis_conn) # no args implies the default queue
+
+# Delay execution of count_words_at_url('http://nvie.com')
+job = q.enqueue(count_words_at_url, 'http://nvie.com')
+print job.result # => None
+
+# Now, wait a while, until the worker is finished
+time.sleep(2)
+print job.result # => 889
+{% endhighlight %}
+
+If you want to put the work on a specific queue, simply specify its name:
+
+{% highlight python %}
+q = Queue('low', connection=redis_conn)
+q.enqueue(count_words_at_url, 'http://nvie.com')
+{% endhighlight %}
+
+Notice the `Queue('low')` in the example above? You can use any queue name, so
+you can quite flexibly distribute work to your own desire. A common naming
+pattern is to name your queues after priorities (e.g. `high`, `medium`,
+`low`).
+
+In addition, you can add a few options to modify the behaviour of the queued
+job. By default, these are popped out of the kwargs that will be passed to the
+job function.
+
+* `timeout` specifies the maximum runtime of the job before it'll be considered
+ 'lost'. Its default unit is second and it can be an integer or a string representing an integer(e.g. `2`, `'2'`). Furthermore, it can be a string with specify unit including hour, minute, second(e.g. `'1h'`, `'3m'`, `'5s'`).
+* `result_ttl` specifies the expiry time of the key where the job result will
+ be stored
+* `ttl` specifies the maximum queued time of the job before it'll be cancelled
+* `depends_on` specifies another job (or job id) that must complete before this
+ job will be queued
+* `job_id` allows you to manually specify this job's `job_id`
+* `at_front` will place the job at the *front* of the queue, instead of the
+ back
+* `kwargs` and `args` lets you bypass the auto-pop of these arguments, ie:
+ specify a `timeout` argument for the underlying job function.
+
+In the last case, it may be advantageous to instead use the explicit version of
+`.enqueue()`, `.enqueue_call()`:
+
+{% highlight python %}
+q = Queue('low', connection=redis_conn)
+q.enqueue_call(func=count_words_at_url,
+ args=('http://nvie.com',),
+ timeout=30)
+{% endhighlight %}
+
+For cases where the web process doesn't have access to the source code running
+in the worker (i.e. code base X invokes a delayed function from code base Y),
+you can pass the function as a string reference, too.
+
+{% highlight python %}
+q = Queue('low', connection=redis_conn)
+q.enqueue('my_package.my_module.my_func', 3, 4)
+{% endhighlight %}
+
+
+## Working with Queues
+
+Besides enqueuing jobs, Queues have a few useful methods:
+
+{% highlight python %}
+from rq import Queue
+from redis import Redis
+
+redis_conn = Redis()
+q = Queue(connection=redis_conn)
+
+# Getting the number of jobs in the queue
+print len(q)
+
+# Retrieving jobs
+queued_job_ids = q.job_ids # Gets a list of job IDs from the queue
+queued_jobs = q.jobs # Gets a list of enqueued job instances
+job = q.fetch_job('my_id') # Returns job having ID "my_id"
+{% endhighlight %}
+
+### On the Design
+
+With RQ, you don't have to set up any queues upfront, and you don't have to
+specify any channels, exchanges, routing rules, or whatnot. You can just put
+jobs onto any queue you want. As soon as you enqueue a job to a queue that
+does not exist yet, it is created on the fly.
+
+RQ does _not_ use an advanced broker to do the message routing for you. You
+may consider this an awesome advantage or a handicap, depending on the problem
+you're solving.
+
+Lastly, it does not speak a portable protocol, since it depends on [pickle][p]
+to serialize the jobs, so it's a Python-only system.
+
+
+## The delayed result
+
+When jobs get enqueued, the `queue.enqueue()` method returns a `Job` instance.
+This is nothing more than a proxy object that can be used to check the outcome
+of the actual job.
+
+For this purpose, it has a convenience `result` accessor property, that
+will return `None` when the job is not yet finished, or a non-`None` value when
+the job has finished (assuming the job _has_ a return value in the first place,
+of course).
+
+
+## The `@job` decorator
+If you're familiar with Celery, you might be used to its `@task` decorator.
+Starting from RQ >= 0.3, there exists a similar decorator:
+
+{% highlight python %}
+from rq.decorators import job
+
+@job('low', connection=my_redis_conn, timeout=5)
+def add(x, y):
+ return x + y
+
+job = add.delay(3, 4)
+time.sleep(1)
+print job.result
+{% endhighlight %}
+
+
+## Bypassing workers
+
+For testing purposes, you can enqueue jobs without delegating the actual
+execution to a worker (available since version 0.3.1). To do this, pass the
+`async=False` argument into the Queue constructor:
+
+{% highlight pycon %}
+>>> q = Queue('low', async=False, connection=my_redis_conn)
+>>> job = q.enqueue(fib, 8)
+>>> job.result
+21
+{% endhighlight %}
+
+The above code runs without an active worker and executes `fib(8)`
+synchronously within the same process. You may know this behaviour from Celery
+as `ALWAYS_EAGER`. Note, however, that you still need a working connection to
+a redis instance for storing states related to job execution and completion.
+
+
+## Job dependencies
+
+New in RQ 0.4.0 is the ability to chain the execution of multiple jobs.
+To execute a job that depends on another job, use the `depends_on` argument:
+
+{% highlight python %}
+q = Queue('low', connection=my_redis_conn)
+report_job = q.enqueue(generate_report)
+q.enqueue(send_report, depends_on=report_job)
+{% endhighlight %}
+
+The ability to handle job dependencies allows you to split a big job into
+several smaller ones. A job that is dependent on another is enqueued only when
+its dependency finishes *successfully*.
+
+
+## The worker
+
+To learn about workers, see the [workers][w] documentation.
+
+[w]: {{site.baseurl}}workers/
+
+
+## Considerations for jobs
+
+Technically, you can put any Python function call on a queue, but that does not
+mean it's always wise to do so. Some things to consider before putting a job
+on a queue:
+
+* Make sure that the function's `__module__` is importable by the worker. In
+ particular, this means that you cannot enqueue functions that are declared in
+ the `__main__` module.
+* Make sure that the worker and the work generator share _exactly_ the same
+ source code.
+* Make sure that the function call does not depend on its context. In
+ particular, global variables are evil (as always), but also _any_ state that
+ the function depends on (for example a "current" user or "current" web
+ request) is not there when the worker will process it. If you want work done
+ for the "current" user, you should resolve that user to a concrete instance
+ and pass a reference to that user object to the job as an argument.
+
+
+## Limitations
+
+RQ workers will only run on systems that implement `fork()`. Most notably,
+this means it is not possible to run the workers on Windows.
+
+
+[m]: http://pypi.python.org/pypi/mailer
+[p]: http://docs.python.org/library/pickle.html
diff --git a/docs/docs/jobs.md b/docs/docs/jobs.md
new file mode 100644
index 0000000..b8db603
--- /dev/null
+++ b/docs/docs/jobs.md
@@ -0,0 +1,95 @@
+---
+title: "RQ: Documentation"
+layout: docs
+---
+
+For some use cases it might be useful have access to the current job ID or
+instance from within the job function itself. Or to store arbitrary data on
+jobs.
+
+
+## Accessing the "current" job
+
+_New in version 0.3.3._
+
+Since job functions are regular Python functions, you have to ask RQ for the
+current job ID, if any. To do this, you can use:
+
+{% highlight python %}
+from rq import get_current_job
+
+def add(x, y):
+ job = get_current_job()
+ print 'Current job: %s' % (job.id,)
+ return x + y
+{% endhighlight %}
+
+
+## Storing arbitrary data on jobs
+
+_Improved in 0.8.0._
+
+To add/update custom status information on this job, you have access to the
+`meta` property, which allows you to store arbitrary pickleable data on the job
+itself:
+
+{% highlight python %}
+import socket
+
+def add(x, y):
+ job = get_current_job()
+ job.meta['handled_by'] = socket.gethostname()
+ job.save_meta()
+
+ # do more work
+ time.sleep(1)
+ return x + y
+{% endhighlight %}
+
+
+## Time to live for job in queue
+
+_New in version 0.4.7._
+
+A job has two TTLs, one for the job result and one for the job itself. This means that if you have
+job that shouldn't be executed after a certain amount of time, you can define a TTL as such:
+
+{% highlight python %}
+# When creating the job:
+job = Job.create(func=say_hello, ttl=43)
+
+# or when queueing a new job:
+job = q.enqueue(count_words_at_url, 'http://nvie.com', ttl=43)
+{% endhighlight %}
+
+
+## Failed Jobs
+
+If a job fails and raises an exception, the worker will put the job in a failed job queue.
+On the Job instance, the `is_failed` property will be true. To fetch all failed jobs, scan
+through the `get_failed_queue()` queue.
+
+{% highlight python %}
+from redis import StrictRedis
+from rq import push_connection, get_failed_queue, Queue
+from rq.job import Job
+
+
+con = StrictRedis()
+push_connection(con)
+
+def div_by_zero(x):
+ return x / 0
+
+job = Job.create(func=div_by_zero, args=(1, 2, 3))
+job.origin = 'fake'
+job.save()
+fq = get_failed_queue()
+fq.quarantine(job, Exception('Some fake error'))
+assert fq.count == 1
+
+fq.requeue(job.id)
+
+assert fq.count == 0
+assert Queue('fake').count == 1
+{% endhighlight %}
\ No newline at end of file
diff --git a/docs/docs/monitoring.md b/docs/docs/monitoring.md
new file mode 100644
index 0000000..0f53554
--- /dev/null
+++ b/docs/docs/monitoring.md
@@ -0,0 +1,105 @@
+---
+title: "RQ: Monitoring"
+layout: docs
+---
+
+Monitoring is where RQ shines.
+
+The easiest way is probably to use the [RQ dashboard][dashboard], a separately
+distributed tool, which is a lightweight webbased monitor frontend for RQ,
+which looks like this:
+
+[![RQ dashboard](/img/dashboard.png)][dashboard]
+
+To install, just do:
+
+{% highlight console %}
+$ pip install rq-dashboard
+$ rq-dashboard
+{% endhighlight %}
+
+It can also be integrated easily in your Flask app.
+
+
+## Monitoring at the console
+
+To see what queues exist and what workers are active, just type `rq info`:
+
+{% highlight console %}
+$ rq info
+high |██████████████████████████ 20
+low |██████████████ 12
+default |█████████ 8
+3 queues, 45 jobs total
+
+Bricktop.19233 idle: low
+Bricktop.19232 idle: high, default, low
+Bricktop.18349 idle: default
+3 workers, 3 queues
+{% endhighlight %}
+
+
+## Querying by queue names
+
+You can also query for a subset of queues, if you're looking for specific ones:
+
+{% highlight console %}
+$ rq info high default
+high |██████████████████████████ 20
+default |█████████ 8
+2 queues, 28 jobs total
+
+Bricktop.19232 idle: high, default
+Bricktop.18349 idle: default
+2 workers, 2 queues
+{% endhighlight %}
+
+
+## Organising workers by queue
+
+By default, `rq info` prints the workers that are currently active, and the
+queues that they are listening on, like this:
+
+{% highlight console %}
+$ rq info
+...
+
+Mickey.26421 idle: high, default
+Bricktop.25458 busy: high, default, low
+Turkish.25812 busy: high, default
+3 workers, 3 queues
+{% endhighlight %}
+
+To see the same data, but organised by queue, use the `-R` (or `--by-queue`)
+flag:
+
+{% highlight console %}
+$ rq info -R
+...
+
+high: Bricktop.25458 (busy), Mickey.26421 (idle), Turkish.25812 (busy)
+low: Bricktop.25458 (busy)
+default: Bricktop.25458 (busy), Mickey.26421 (idle), Turkish.25812 (busy)
+failed: –
+3 workers, 4 queues
+{% endhighlight %}
+
+
+## Interval polling
+
+By default, `rq info` will print stats and exit.
+You can specify a poll interval, by using the `--interval` flag.
+
+{% highlight console %}
+$ rq info --interval 1
+{% endhighlight %}
+
+`rq info` will now update the screen every second. You may specify a float
+value to indicate fractions of seconds. Be aware that low interval values will
+increase the load on Redis, of course.
+
+{% highlight console %}
+$ rq info --interval 0.5
+{% endhighlight %}
+
+[dashboard]: https://github.com/nvie/rq-dashboard
diff --git a/docs/docs/results.md b/docs/docs/results.md
new file mode 100644
index 0000000..e5cc9ca
--- /dev/null
+++ b/docs/docs/results.md
@@ -0,0 +1,115 @@
+---
+title: "RQ: Documentation"
+layout: docs
+---
+
+Enqueueing jobs is delayed execution of function calls. This means we're
+solving a problem, but are getting back a few in return.
+
+
+## Dealing with results
+
+Python functions may have return values, so jobs can have them, too. If a job
+returns a non-`None` return value, the worker will write that return value back
+to the job's Redis hash under the `result` key. The job's Redis hash itself
+will expire after 500 seconds by default after the job is finished.
+
+The party that enqueued the job gets back a `Job` instance as a result of the
+enqueueing itself. Such a `Job` object is a proxy object that is tied to the
+job's ID, to be able to poll for results.
+
+
+**On the return value's TTL**
+Return values are written back to Redis with a limited lifetime (via a Redis
+expiring key), which is merely to avoid ever-growing Redis databases.
+
+From RQ >= 0.3.1, The TTL value of the job result can be specified using the
+`result_ttl` keyword argument to `enqueue()` and `enqueue_call()` calls. It
+can also be used to disable the expiry altogether. You then are responsible
+for cleaning up jobs yourself, though, so be careful to use that.
+
+You can do the following:
+
+ q.enqueue(foo) # result expires after 500 secs (the default)
+ q.enqueue(foo, result_ttl=86400) # result expires after 1 day
+ q.enqueue(foo, result_ttl=0) # result gets deleted immediately
+ q.enqueue(foo, result_ttl=-1) # result never expires--you should delete jobs manually
+
+Additionally, you can use this for keeping around finished jobs without return
+values, which would be deleted immediately by default.
+
+ q.enqueue(func_without_rv, result_ttl=500) # job kept explicitly
+
+
+## Dealing with exceptions
+
+Jobs can fail and throw exceptions. This is a fact of life. RQ deals with
+this in the following way.
+
+Job failure is too important not to be noticed and therefore the job's return
+value should never expire. Furthermore, it should be possible to retry failed
+jobs. Typically, this is something that needs manual interpretation, since
+there is no automatic or reliable way of letting RQ judge whether it is safe
+for certain tasks to be retried or not.
+
+When an exception is thrown inside a job, it is caught by the worker,
+serialized and stored under the job's Redis hash's `exc_info` key. A reference
+to the job is put on the `failed` queue.
+
+The job itself has some useful properties that can be used to aid inspection:
+
+* the original creation time of the job
+* the last enqueue date
+* the originating queue
+* a textual description of the desired function invocation
+* the exception information
+
+This makes it possible to inspect and interpret the problem manually and
+possibly resubmit the job.
+
+
+## Dealing with interruption
+
+When workers get killed in the polite way (Ctrl+C or `kill`), RQ tries hard not
+to lose any work. The current work is finished after which the worker will
+stop further processing of jobs. This ensures that jobs always get a fair
+change to finish themselves.
+
+However, workers can be killed forcefully by `kill -9`, which will not give the
+workers a chance to finish the job gracefully or to put the job on the `failed`
+queue. Therefore, killing a worker forcefully could potentially lead to
+damage.
+
+Just sayin'.
+
+
+## Dealing with job timeouts
+
+By default, jobs should execute within 180 seconds. After that, the worker
+kills the work horse and puts the job onto the `failed` queue, indicating the
+job timed out.
+
+If a job requires more (or less) time to complete, the default timeout period
+can be loosened (or tightened), by specifying it as a keyword argument to the
+`enqueue()` call, like so:
+
+{% highlight python %}
+q = Queue()
+q.enqueue(mytask, args=(foo,), kwargs={'bar': qux}, timeout=600) # 10 mins
+{% endhighlight %}
+
+You can also change the default timeout for jobs that are enqueued via specific
+queue instances at once, which can be useful for patterns like this:
+
+{% highlight python %}
+# High prio jobs should end in 8 secs, while low prio
+# work may take up to 10 mins
+high = Queue('high', default_timeout=8) # 8 secs
+low = Queue('low', default_timeout=600) # 10 mins
+
+# Individual jobs can still override these defaults
+low.enqueue(really_really_slow, timeout=3600) # 1 hr
+{% endhighlight %}
+
+Individual jobs can still specify an alternative timeout, as workers will
+respect these.
diff --git a/docs/docs/testing.md b/docs/docs/testing.md
new file mode 100644
index 0000000..f2378a5
--- /dev/null
+++ b/docs/docs/testing.md
@@ -0,0 +1,41 @@
+---
+title: "RQ: Testing"
+layout: docs
+---
+
+## Workers inside unit tests
+
+You may wish to include your RQ tasks inside unit tests. However many frameworks (such as Django) use in-memory databases which do not play nicely with the default `fork()` behaviour of RQ.
+
+Therefore, you must use the SimpleWorker class to avoid fork();
+
+{% highlight python %}
+from redis import Redis
+from rq import SimpleWorker, Queue
+
+queue = Queue(connection=Redis())
+queue.enqueue(my_long_running_job)
+worker = SimpleWorker([queue], connection=queue.connection)
+worker.work(burst=True) # Runs enqueued job
+# Check for result...
+{% endhighlight %}
+
+
+## Running Jobs in unit tests
+
+Another solution for testing purposes is to use the `async=False` queue
+parameter, that instructs it to instantly perform the job in the same
+thread instead of dispatching it to the workers. Workers are not required
+anymore.
+Additionally, we can use fakeredis to mock a redis instance, so we don't have to
+run a redis server separately. The instance of the fake redis server can
+be directly passed as the connection argument to the queue:
+
+{% highlight python %}
+from fakeredis import FakeStrictRedis
+from rq import Queue
+
+queue = Queue(async=False, connection=FakeStrictRedis())
+job = queue.enqueue(my_long_running_job)
+assert job.is_finished
+{% endhighlight %}
\ No newline at end of file
diff --git a/docs/docs/workers.md b/docs/docs/workers.md
new file mode 100644
index 0000000..7ce595e
--- /dev/null
+++ b/docs/docs/workers.md
@@ -0,0 +1,287 @@
+---
+title: "RQ: Simple job queues for Python"
+layout: docs
+---
+
+A worker is a Python process that typically runs in the background and exists
+solely as a work horse to perform lengthy or blocking tasks that you don't want
+to perform inside web processes.
+
+
+## Starting workers
+
+To start crunching work, simply start a worker from the root of your project
+directory:
+
+{% highlight console %}
+$ rq worker high normal low
+*** Listening for work on high, normal, low
+Got send_newsletter('me@nvie.com') from default
+Job ended normally without result
+*** Listening for work on high, normal, low
+...
+{% endhighlight %}
+
+Workers will read jobs from the given queues (the order is important) in an
+endless loop, waiting for new work to arrive when all jobs are done.
+
+Each worker will process a single job at a time. Within a worker, there is no
+concurrent processing going on. If you want to perform jobs concurrently,
+simply start more workers.
+
+
+### Burst mode
+
+By default, workers will start working immediately and will block and wait for
+new work when they run out of work. Workers can also be started in _burst
+mode_ to finish all currently available work and quit as soon as all given
+queues are emptied.
+
+{% highlight console %}
+$ rq worker --burst high normal low
+*** Listening for work on high, normal, low
+Got send_newsletter('me@nvie.com') from default
+Job ended normally without result
+No more work, burst finished.
+Registering death.
+{% endhighlight %}
+
+This can be useful for batch work that needs to be processed periodically, or
+just to scale up your workers temporarily during peak periods.
+
+
+## Inside the worker
+
+### The worker life-cycle
+
+The life-cycle of a worker consists of a few phases:
+
+1. _Boot_. Loading the Python environment.
+2. _Birth registration_. The worker registers itself to the system so it knows
+ of this worker.
+3. _Start listening_. A job is popped from any of the given Redis queues.
+ If all queues are empty and the worker is running in burst mode, quit now.
+ Else, wait until jobs arrive.
+4. _Prepare job execution_. The worker tells the system that it will begin work
+ by setting its status to `busy` and registers job in the `StartedJobRegistry`.
+5. _Fork a child process._
+ A child process (the "work horse") is forked off to do the actual work in
+ a fail-safe context.
+6. _Process work_. This performs the actual job work in the work horse.
+7. _Cleanup job execution_. The worker sets its status to `idle` and sets both
+ the job and its result to expire based on `result_ttl`. Job is also removed
+ from `StartedJobRegistry` and added to to `FinishedJobRegistry` in the case
+ of successful execution, or `FailedQueue` in the case of failure.
+8. _Loop_. Repeat from step 3.
+
+
+## Performance notes
+
+Basically the `rq worker` shell script is a simple fetch-fork-execute loop.
+When a lot of your jobs do lengthy setups, or they all depend on the same set
+of modules, you pay this overhead each time you run a job (since you're doing
+the import _after_ the moment of forking). This is clean, because RQ won't
+ever leak memory this way, but also slow.
+
+A pattern you can use to improve the throughput performance for these kind of
+jobs can be to import the necessary modules _before_ the fork. There is no way
+of telling RQ workers to perform this set up for you, but you can do it
+yourself before starting the work loop.
+
+To do this, provide your own worker script (instead of using `rq worker`).
+A simple implementation example:
+
+{% highlight python %}
+#!/usr/bin/env python
+import sys
+from rq import Connection, Worker
+
+# Preload libraries
+import library_that_you_want_preloaded
+
+# Provide queue names to listen to as arguments to this script,
+# similar to rq worker
+with Connection():
+ qs = sys.argv[1:] or ['default']
+
+ w = Worker(qs)
+ w.work()
+{% endhighlight %}
+
+
+### Worker names
+
+Workers are registered to the system under their names, see [monitoring][m].
+By default, the name of a worker is equal to the concatenation of the current
+hostname and the current PID. To override this default, specify the name when
+starting the worker, using the `--name` option.
+
+[m]: /docs/monitoring/
+
+
+### Retrieving worker information
+
+`Worker` instances store their runtime information in Redis. Here's how to
+retrieve them:
+
+{% highlight python %}
+from redis import Redis
+from rq import Queue, Worker
+
+# Returns all workers registered in this connection
+redis = Redis()
+workers = Worker.all(connection=redis)
+
+# Returns all workers in this queue (new in version 0.10.0)
+queue = Queue('queue_name')
+workers = Worker.all(queue=queue)
+{% endhighlight %}
+
+_New in version 0.10.0._
+
+If you only want to know the number of workers for monitoring purposes, using
+`Worker.count()` is much more performant.
+
+{% highlight python %}
+from redis import Redis
+from rq import Worker
+
+redis = Redis()
+
+# Count the number of workers in this Redis connection
+workers = Worker.count(connection=redis)
+
+# Count the number of workers for a specific queue
+queue = Queue('queue_name', connection=redis)
+workers = Worker.all(queue=queue)
+
+{% endhighlight %}
+
+
+### Worker statistics
+
+_New in version 0.9.0._
+
+If you want to check the utilization of your queues, `Worker` instances
+store a few useful information:
+
+{% highlight python %}
+from rq.worker import Worker
+worker = Worker.find_by_key('rq:worker:name')
+
+worker.successful_job_count # Number of jobs finished successfully
+worker.failed_job_count. # Number of failed jobs processed by this worker
+worker.total_working_time # Number of time spent executing jobs
+{% endhighlight %}
+
+
+## Taking down workers
+
+If, at any time, the worker receives `SIGINT` (via Ctrl+C) or `SIGTERM` (via
+`kill`), the worker wait until the currently running task is finished, stop
+the work loop and gracefully register its own death.
+
+If, during this takedown phase, `SIGINT` or `SIGTERM` is received again, the
+worker will forcefully terminate the child process (sending it `SIGKILL`), but
+will still try to register its own death.
+
+
+## Using a config file
+
+_New in version 0.3.2._
+
+If you'd like to configure `rq worker` via a configuration file instead of
+through command line arguments, you can do this by creating a Python file like
+`settings.py`:
+
+{% highlight python %}
+REDIS_URL = 'redis://localhost:6379/1'
+
+# You can also specify the Redis DB to use
+# REDIS_HOST = 'redis.example.com'
+# REDIS_PORT = 6380
+# REDIS_DB = 3
+# REDIS_PASSWORD = 'very secret'
+
+# Queues to listen on
+QUEUES = ['high', 'normal', 'low']
+
+# If you're using Sentry to collect your runtime exceptions, you can use this
+# to configure RQ for it in a single step
+# The 'sync+' prefix is required for raven: https://github.com/nvie/rq/issues/350#issuecomment-43592410
+SENTRY_DSN = 'sync+http://public:secret@example.com/1'
+{% endhighlight %}
+
+The example above shows all the options that are currently supported.
+
+_Note: The_ `QUEUES` _and_ `REDIS_PASSWORD` _settings are new since 0.3.3._
+
+To specify which module to read settings from, use the `-c` option:
+
+{% highlight console %}
+$ rq worker -c settings
+{% endhighlight %}
+
+
+## Custom worker classes
+
+_New in version 0.4.0._
+
+There are times when you want to customize the worker's behavior. Some of the
+more common requests so far are:
+
+1. Managing database connectivity prior to running a job.
+2. Using a job execution model that does not require `os.fork`.
+3. The ability to use different concurrency models such as
+ `multiprocessing` or `gevent`.
+
+You can use the `-w` option to specify a different worker class to use:
+
+{% highlight console %}
+$ rq worker -w 'path.to.GeventWorker'
+{% endhighlight %}
+
+
+## Custom Job and Queue classes
+
+_Will be available in next release._
+
+You can tell the worker to use a custom class for jobs and queues using
+`--job-class` and/or `--queue-class`.
+
+{% highlight console %}
+$ rq worker --job-class 'custom.JobClass' --queue-class 'custom.QueueClass'
+{% endhighlight %}
+
+Don't forget to use those same classes when enqueueing the jobs.
+
+For example:
+
+{% highlight python %}
+from rq import Queue
+from rq.job import Job
+
+class CustomJob(Job):
+ pass
+
+class CustomQueue(Queue):
+ job_class = CustomJob
+
+queue = CustomQueue('default', connection=redis_conn)
+queue.enqueue(some_func)
+{% endhighlight %}
+
+
+## Custom exception handlers
+
+_New in version 0.5.5._
+
+If you need to handle errors differently for different types of jobs, or simply want to customize
+RQ's default error handling behavior, run `rq worker` using the `--exception-handler` option:
+
+{% highlight console %}
+$ rq worker --exception-handler 'path.to.my.ErrorHandler'
+
+# Multiple exception handlers is also supported
+$ rq worker --exception-handler 'path.to.my.ErrorHandler' --exception-handler 'another.ErrorHandler'
+{% endhighlight %}
diff --git a/docs/favicon.png b/docs/favicon.png
new file mode 100644
index 0000000..1ff2af3
Binary files /dev/null and b/docs/favicon.png differ
diff --git a/docs/img/bg.png b/docs/img/bg.png
new file mode 100644
index 0000000..82aa205
Binary files /dev/null and b/docs/img/bg.png differ
diff --git a/docs/img/bq.png b/docs/img/bq.png
new file mode 100644
index 0000000..d8efdd7
Binary files /dev/null and b/docs/img/bq.png differ
diff --git a/docs/img/dashboard.png b/docs/img/dashboard.png
new file mode 100644
index 0000000..378a752
Binary files /dev/null and b/docs/img/dashboard.png differ
diff --git a/docs/img/logo.png b/docs/img/logo.png
new file mode 100644
index 0000000..34fb765
Binary files /dev/null and b/docs/img/logo.png differ
diff --git a/docs/img/logo2.png b/docs/img/logo2.png
new file mode 100644
index 0000000..357c661
Binary files /dev/null and b/docs/img/logo2.png differ
diff --git a/docs/img/ribbon.png b/docs/img/ribbon.png
new file mode 100644
index 0000000..5b10b85
Binary files /dev/null and b/docs/img/ribbon.png differ
diff --git a/docs/img/warning.png b/docs/img/warning.png
new file mode 100644
index 0000000..7c389bd
Binary files /dev/null and b/docs/img/warning.png differ
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..15e77ed
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,85 @@
+---
+title: "RQ: Simple job queues for Python"
+layout: default
+---
+
+RQ (_Redis Queue_) is a simple Python library for queueing jobs and processing
+them in the background with workers. It is backed by Redis and it is designed
+to have a low barrier to entry. It can be integrated in your web stack easily.
+
+RQ requires Redis >= 2.7.0.
+
+## Getting started
+
+First, run a Redis server. You can use an existing one. To put jobs on
+queues, you don't have to do anything special, just define your typically
+lengthy or blocking function:
+
+{% highlight python %}
+import requests
+
+def count_words_at_url(url):
+ resp = requests.get(url)
+ return len(resp.text.split())
+{% endhighlight %}
+
+Then, create a RQ queue:
+
+{% highlight python %}
+from redis import Redis
+from rq import Queue
+
+q = Queue(connection=Redis())
+{% endhighlight %}
+
+And enqueue the function call:
+
+{% highlight python %}
+from my_module import count_words_at_url
+result = q.enqueue(
+ count_words_at_url, 'http://nvie.com')
+{% endhighlight %}
+
+For a more complete example, refer to the [docs][d]. But this is the essence.
+
+[d]: {{site.baseurl}}docs/
+
+
+### The worker
+
+To start executing enqueued function calls in the background, start a worker
+from your project's directory:
+
+{% highlight console %}
+$ rq worker
+*** Listening for work on default
+Got count_words_at_url('http://nvie.com') from default
+Job result = 818
+*** Listening for work on default
+{% endhighlight %}
+
+That's about it.
+
+
+## Installation
+
+Simply use the following command to install the latest released version:
+
+ pip install rq
+
+If you want the cutting edge version (that may well be broken), use this:
+
+ pip install -e git+git@github.com:nvie/rq.git@master#egg=rq
+
+
+## Project history
+
+This project has been inspired by the good parts of [Celery][1], [Resque][2]
+and [this snippet][3], and has been created as a lightweight alternative to
+existing queueing frameworks, with a low barrier to entry.
+
+[m]: http://pypi.python.org/pypi/mailer
+[p]: http://docs.python.org/library/pickle.html
+[1]: http://www.celeryproject.org/
+[2]: https://github.com/defunkt/resque
+[3]: http://flask.pocoo.org/snippets/73/
diff --git a/docs/patterns/django.md b/docs/patterns/django.md
new file mode 100644
index 0000000..4f0fc9b
--- /dev/null
+++ b/docs/patterns/django.md
@@ -0,0 +1,23 @@
+---
+title: "RQ: Using with Django"
+layout: patterns
+---
+
+## Using RQ with Django
+
+The simplest way of using RQ with Django is to use
+[django-rq](https://github.com/ui/django-rq). Follow the instructions in the
+README.
+
+### Manually
+
+In order to use RQ together with Django, you have to start the worker in
+a "Django context". Possibly, you have to write a custom Django management
+command to do so. In many cases, however, setting the `DJANGO_SETTINGS_MODULE`
+environmental variable will already do the trick.
+
+If `settings.py` is your Django settings file (as it is by default), use this:
+
+{% highlight console %}
+$ DJANGO_SETTINGS_MODULE=settings rq worker high default low
+{% endhighlight %}
diff --git a/docs/patterns/index.md b/docs/patterns/index.md
new file mode 100644
index 0000000..64c60b2
--- /dev/null
+++ b/docs/patterns/index.md
@@ -0,0 +1,69 @@
+---
+title: "RQ: Using RQ on Heroku"
+layout: patterns
+---
+
+
+## Using RQ on Heroku
+
+To setup RQ on [Heroku][1], first add it to your
+`requirements.txt` file:
+
+ redis==2.10.5
+ rq==0.7.0
+
+Create a file called `run-worker.py` with the following content (assuming you
+are using [Redis To Go][2] with Heroku):
+
+{% highlight python %}
+import os
+import urlparse
+from redis import Redis
+from rq import Queue, Connection
+from rq.worker import HerokuWorker as Worker
+
+listen = ['high', 'default', 'low']
+
+redis_url = os.getenv('REDISTOGO_URL')
+if not redis_url:
+ raise RuntimeError('Set up Redis To Go first.')
+
+urlparse.uses_netloc.append('redis')
+url = urlparse.urlparse(redis_url)
+conn = Redis(host=url.hostname, port=url.port, db=0, password=url.password)
+
+if __name__ == '__main__':
+ with Connection(conn):
+ worker = Worker(map(Queue, listen))
+ worker.work()
+{% endhighlight %}
+
+Than, add the command to your `Procfile`:
+
+ worker: python -u run-worker.py
+
+Now, all you have to do is spin up a worker:
+
+{% highlight console %}
+$ heroku scale worker=1
+{% endhighlight %}
+
+
+## Putting RQ under foreman
+
+[Foreman][3] is probably the process manager you use when you host your app on
+Heroku, or just because it's a pretty friendly tool to use in development.
+
+When using RQ under `foreman`, you may experience that the workers are a bit
+quiet sometimes. This is because of Python buffering the output, so `foreman`
+cannot (yet) echo it. Here's a related [Wiki page][4].
+
+Just change the way you run your worker process, by adding the `-u` option (to
+force stdin, stdout and stderr to be totally unbuffered):
+
+ worker: python -u run-worker.py
+
+[1]: https://heroku.com
+[2]: https://devcenter.heroku.com/articles/redistogo
+[3]: https://github.com/ddollar/foreman
+[4]: https://github.com/ddollar/foreman/wiki/Missing-Output
diff --git a/docs/patterns/sentry.md b/docs/patterns/sentry.md
new file mode 100644
index 0000000..ecce264
--- /dev/null
+++ b/docs/patterns/sentry.md
@@ -0,0 +1,47 @@
+---
+title: "RQ: Sending exceptions to Sentry"
+layout: patterns
+---
+
+## Sending exceptions to Sentry
+
+[Sentry](https://www.getsentry.com/) is a popular exception gathering service
+that RQ supports integrating with since version 0.3.1, through its custom
+exception handlers.
+
+RQ includes a convenience function that registers your existing Sentry client
+to send all exceptions to.
+
+An example:
+
+{% highlight python %}
+from raven import Client
+from raven.transport.http import HTTPTransport
+from rq.contrib.sentry import register_sentry
+
+client = Client('', transport=HTTPTransport)
+register_sentry(client, worker)
+{% endhighlight %}
+
+Where `worker` is your RQ worker instance. After that, call `worker.work(...)`
+to start the worker. All exceptions that occur are reported to Sentry
+automatically.
+
+
+
+ Note:
+
+ Error delivery to Sentry is known to be unreliable with RQ when using
+ async transports (the default is). So you are encouraged to use the
+ HTTPTransport or RequestsHTTPTransport when
+ creating your client. See the code sample above, or the Raven
+ documentation.
+
+
+Read more on RQ's [custom exception handling](/docs/exceptions/) capabilities.
diff --git a/docs/patterns/supervisor.md b/docs/patterns/supervisor.md
new file mode 100644
index 0000000..f45a12e
--- /dev/null
+++ b/docs/patterns/supervisor.md
@@ -0,0 +1,76 @@
+---
+title: "Putting RQ under supervisor"
+layout: patterns
+---
+
+## Putting RQ under supervisor
+
+[Supervisor][1] is a popular tool for managing long-running processes in
+production environments. It can automatically restart any crashed processes,
+and you gain a single dashboard for all of the running processes that make up
+your product.
+
+RQ can be used in combination with supervisor easily. You'd typically want to
+use the following supervisor settings:
+
+{% highlight ini %}
+[program:myworker]
+; Point the command to the specific rq command you want to run.
+; If you use virtualenv, be sure to point it to
+; /path/to/virtualenv/bin/rq
+; Also, you probably want to include a settings module to configure this
+; worker. For more info on that, see http://python-rq.org/docs/workers/
+command=/path/to/rq worker -c mysettings high normal low
+; process_num is required if you specify >1 numprocs
+process_name=%(program_name)s-%(process_num)s
+
+; If you want to run more than one worker instance, increase this
+numprocs=1
+
+; This is the directory from which RQ is ran. Be sure to point this to the
+; directory where your source code is importable from
+directory=/path/to
+
+; RQ requires the TERM signal to perform a warm shutdown. If RQ does not die
+; within 10 seconds, supervisor will forcefully kill it
+stopsignal=TERM
+
+; These are up to you
+autostart=true
+autorestart=true
+{% endhighlight %}
+
+### Conda environments
+
+[Conda][2] virtualenvs can be used for RQ jobs which require non-Python
+dependencies. You can use a similar approach as with regular virtualenvs.
+
+{% highlight ini %}
+[program:myworker]
+; Point the command to the specific rq command you want to run.
+; For conda virtual environments, install RQ into your env.
+; Also, you probably want to include a settings module to configure this
+; worker. For more info on that, see http://python-rq.org/docs/workers/
+environment=PATH='/opt/conda/envs/myenv/bin'
+command=/opt/conda/envs/myenv/bin/rq worker -c mysettings high normal low
+; process_num is required if you specify >1 numprocs
+process_name=%(program_name)s-%(process_num)s
+
+; If you want to run more than one worker instance, increase this
+numprocs=1
+
+; This is the directory from which RQ is ran. Be sure to point this to the
+; directory where your source code is importable from
+directory=/path/to
+
+; RQ requires the TERM signal to perform a warm shutdown. If RQ does not die
+; within 10 seconds, supervisor will forcefully kill it
+stopsignal=TERM
+
+; These are up to you
+autostart=true
+autorestart=true
+{% endhighlight %}
+
+[1]: http://supervisord.org/
+[2]: https://conda.io/docs/