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:
 | 
					.. _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
 | 
					Vagrant
 | 
				
			||||||
.......
 | 
					.......
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user