mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-11-02 18:47:10 -05:00 
			
		
		
		
	Added some configs for Apache & Nginx/gunicorn
This commit is contained in:
		
							parent
							
								
									53909cf9b0
								
							
						
					
					
						commit
						c5aafdb43d
					
				
							
								
								
									
										170
									
								
								docs/setup.rst
									
									
									
									
									
								
							
							
						
						
									
										170
									
								
								docs/setup.rst
									
									
									
									
									
								
							@ -377,6 +377,176 @@ second period.
 | 
			
		||||
 | 
			
		||||
.. _setup-permanent-vagrant:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Using a Real Webserver
 | 
			
		||||
......................
 | 
			
		||||
 | 
			
		||||
The default is to use Django's development server, as that's easy and does the
 | 
			
		||||
job well enough on a home network.  However, if you want to do things right,
 | 
			
		||||
it's probably a good idea to use a webserver capable of handling more than one
 | 
			
		||||
thread.
 | 
			
		||||
 | 
			
		||||
Apache
 | 
			
		||||
~~~~~~
 | 
			
		||||
 | 
			
		||||
This is a configuration supplied by `steckerhalter`_ on GitHub.  It uses Apache
 | 
			
		||||
and mod_wsgi, with a Paperless installation in /home/paperless/:
 | 
			
		||||
 | 
			
		||||
.. code:: apache
 | 
			
		||||
 | 
			
		||||
    <VirtualHost *:80>
 | 
			
		||||
        ServerName example.com
 | 
			
		||||
 | 
			
		||||
        Alias /static/ /home/paperless/paperless/static/
 | 
			
		||||
        <Directory /home/paperless/paperless/static>
 | 
			
		||||
            Require all granted
 | 
			
		||||
        </Directory>
 | 
			
		||||
 | 
			
		||||
        WSGIScriptAlias / /home/paperless/paperless/src/paperless/wsgi.py
 | 
			
		||||
        WSGIDaemonProcess example.com user=paperless group=paperless threads=5 python-path=/home/paperless/paperless/src:/home/paperless/.env/lib/python3.4/site-packages
 | 
			
		||||
        WSGIProcessGroup example.com
 | 
			
		||||
 | 
			
		||||
        <Directory /home/paperless/paperless/src/paperless>
 | 
			
		||||
            <Files wsgi.py>
 | 
			
		||||
                Require all granted
 | 
			
		||||
            </Files>
 | 
			
		||||
        </Directory>
 | 
			
		||||
    </VirtualHost>
 | 
			
		||||
 | 
			
		||||
.. _steckerhalter: https://github.com/steckerhalter
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Nginx + Gunicorn
 | 
			
		||||
~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
If you're using Nginx, the most common setup is to combine it with a
 | 
			
		||||
Python-based server like Gunicorn so that Nginx is acting as a proxy.  Below is
 | 
			
		||||
a copy of a simple Nginx configuration fragment making use of SSL and IPv6 to
 | 
			
		||||
refer to a gunicorn instance listening on a local Unix socket:
 | 
			
		||||
 | 
			
		||||
.. code:: nginx
 | 
			
		||||
 | 
			
		||||
    upstream transfer_server {
 | 
			
		||||
      server unix:/run/example.com/gunicorn.sock fail_timeout=0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    # Redirect requests on port 80 to 443
 | 
			
		||||
    server {
 | 
			
		||||
      listen 80;
 | 
			
		||||
      listen [::]:80;
 | 
			
		||||
      server_name example.com;
 | 
			
		||||
      rewrite ^ https://$server_name$request_uri? permanent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    server {
 | 
			
		||||
 | 
			
		||||
      listen 443 ssl;
 | 
			
		||||
      listen [::]:443;
 | 
			
		||||
      client_max_body_size 4G;
 | 
			
		||||
      server_name example.com;
 | 
			
		||||
      keepalive_timeout 5;
 | 
			
		||||
      root /var/www/example.com;
 | 
			
		||||
 | 
			
		||||
      ssl on;
 | 
			
		||||
 | 
			
		||||
      ssl_certificate         /etc/letsencrypt/live/example.com/fullchain.pem;
 | 
			
		||||
      ssl_certificate_key     /etc/letsencrypt/live/example.com/privkey.pem;
 | 
			
		||||
      ssl_trusted_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
 | 
			
		||||
      ssl_session_timeout 1d;
 | 
			
		||||
      ssl_session_cache shared:SSL:50m;
 | 
			
		||||
 | 
			
		||||
      # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
 | 
			
		||||
      # Generate with:
 | 
			
		||||
      #   openssl dhparam -out /etc/nginx/dhparam.pem 2048
 | 
			
		||||
      ssl_dhparam /etc/nginx/dhparam.pem;
 | 
			
		||||
 | 
			
		||||
      # What Mozilla calls "Intermediate configuration"
 | 
			
		||||
      # Copied from https://mozilla.github.io/server-side-tls/ssl-config-generator/
 | 
			
		||||
      ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
 | 
			
		||||
      ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
 | 
			
		||||
      ssl_prefer_server_ciphers on;
 | 
			
		||||
 | 
			
		||||
      add_header Strict-Transport-Security max-age=15768000;
 | 
			
		||||
 | 
			
		||||
      ssl_stapling on;
 | 
			
		||||
      ssl_stapling_verify on;
 | 
			
		||||
 | 
			
		||||
      access_log /var/log/nginx/example.com.log main;
 | 
			
		||||
      error_log /var/log/nginx/example.com.err info;
 | 
			
		||||
 | 
			
		||||
      location / {
 | 
			
		||||
        try_files $uri @proxy_to_app;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      location @proxy_to_app {
 | 
			
		||||
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 | 
			
		||||
        proxy_set_header X-Forwarded-Proto https;
 | 
			
		||||
        proxy_set_header Host $host;
 | 
			
		||||
        proxy_redirect off;
 | 
			
		||||
        proxy_pass http://transfer_server;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
Once you've got Nginx configured, you'll want to have a configuration file for
 | 
			
		||||
your gunicorn instance.  This should do the trick:
 | 
			
		||||
 | 
			
		||||
.. code:: python
 | 
			
		||||
 | 
			
		||||
    import os
 | 
			
		||||
 | 
			
		||||
    bind = 'unix:/run/example.com/gunicorn.sock'
 | 
			
		||||
    backlog = 2048
 | 
			
		||||
    workers = 6
 | 
			
		||||
    worker_class = 'sync'
 | 
			
		||||
    worker_connections = 1000
 | 
			
		||||
    timeout = 30
 | 
			
		||||
    keepalive = 2
 | 
			
		||||
    debug = False
 | 
			
		||||
    spew = False
 | 
			
		||||
    daemon = False
 | 
			
		||||
    pidfile = None
 | 
			
		||||
    umask = 0
 | 
			
		||||
    user = None
 | 
			
		||||
    group = None
 | 
			
		||||
    tmp_upload_dir = None
 | 
			
		||||
    errorlog = '/var/log/example.com/gunicorn.err'
 | 
			
		||||
    loglevel = 'warning'
 | 
			
		||||
    accesslog = '/var/log/example.com/gunicorn.log'
 | 
			
		||||
    proc_name = None
 | 
			
		||||
 | 
			
		||||
    def post_fork(server, worker):
 | 
			
		||||
        server.log.info("Worker spawned (pid: %s)", worker.pid)
 | 
			
		||||
 | 
			
		||||
    def pre_fork(server, worker):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def pre_exec(server):
 | 
			
		||||
        server.log.info("Forked child, re-executing.")
 | 
			
		||||
 | 
			
		||||
    def when_ready(server):
 | 
			
		||||
        server.log.info("Server is ready. Spawning workers")
 | 
			
		||||
 | 
			
		||||
    def worker_int(worker):
 | 
			
		||||
        worker.log.info("worker received INT or QUIT signal")
 | 
			
		||||
 | 
			
		||||
        ## get traceback info
 | 
			
		||||
        import threading, sys, traceback
 | 
			
		||||
        id2name = dict([(th.ident, th.name) for th in threading.enumerate()])
 | 
			
		||||
        code = []
 | 
			
		||||
        for threadId, stack in sys._current_frames().items():
 | 
			
		||||
            code.append("\n# Thread: %s(%d)" % (id2name.get(threadId,""),
 | 
			
		||||
                threadId))
 | 
			
		||||
            for filename, lineno, name, line in traceback.extract_stack(stack):
 | 
			
		||||
                code.append('File: "%s", line %d, in %s' % (filename,
 | 
			
		||||
                    lineno, name))
 | 
			
		||||
                if line:
 | 
			
		||||
                    code.append("  %s" % (line.strip()))
 | 
			
		||||
        worker.log.debug("\n".join(code))
 | 
			
		||||
 | 
			
		||||
    def worker_abort(worker):
 | 
			
		||||
        worker.log.info("worker received SIGABRT signal")
 | 
			
		||||
 | 
			
		||||
Vagrant
 | 
			
		||||
.......
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user