diff --git a/Caddyfile b/Caddyfile new file mode 100644 index 0000000..23268c6 --- /dev/null +++ b/Caddyfile @@ -0,0 +1,77 @@ +# Example Caddyfile for InvenTree +# The following environment variables may be used: +# - INVENTREE_SITE_URL: The upstream URL of the InvenTree site (default: inventree.localhost) +# - INVENTREE_SERVER: The internal URL of the InvenTree container (default: http://inventree-server:8000) +# +# Note that while this file is a good starting point, it may need to be modified to suit your specific requirements +# +# Ref to the Caddyfile documentation: https://caddyserver.com/docs/caddyfile + + +# Logging configuration for Caddy +(log_common) { + log { + output file /var/log/caddy/{args[0]}.access.log + } +} + +# CORS headers control (used for static and media files) +(cors-headers) { + header Allow GET,HEAD,OPTIONS + header Access-Control-Allow-Origin * + header Access-Control-Allow-Methods GET,HEAD,OPTIONS + header Access-Control-Allow-Headers Authorization,Content-Type,User-Agent + + @cors_preflight{args[0]} method OPTIONS + + handle @cors_preflight{args[0]} { + respond "" 204 + } +} + +# The default server address is configured in the .env file +# If not specified, the default address is used - http://inventree.localhost +# If you need to listen on multiple addresses, or use a different port, you can modify this section directly +http://inventree.ctbk.de { + import log_common inventree + + encode gzip + + request_body { + max_size 100MB + } + + # Handle static request files + handle_path /static/* { + import cors-headers static + + root * /var/www/static + file_server + } + + # Handle media request files + handle_path /media/* { + import cors-headers media + + root * /var/www/media + file_server + + # Force download of media files (for security) + # Comment out this line if you do not want to force download + header Content-Disposition attachment + + # Authentication is handled by the forward_auth directive + # This is required to ensure that media files are only accessible to authenticated users + forward_auth {$INVENTREE_SERVER:"http://inventree-server:8000"} { + uri /auth/ + } + } + + # All other requests are proxied to the InvenTree server + reverse_proxy {$INVENTREE_SERVER:"http://inventree-server:8000"} { + + # If you are running behind another proxy, you may need to specify 'trusted_proxies' + # Ref: https://caddyserver.com/docs/json/apps/http/servers/trusted_proxies/ + # trusted_proxies ... + } +} diff --git a/compose.yml b/compose.yml index efafa65..f5af5c6 100644 --- a/compose.yml +++ b/compose.yml @@ -1,10 +1,8 @@ -version: "3.8" - # Docker compose recipe for a production-ready InvenTree setup, with the following containers: # - PostgreSQL as the database backend # - gunicorn as the InvenTree web server # - django-q as the InvenTree background worker process -# - nginx as a reverse proxy +# - Caddy as a reverse proxy # - redis as the cache manager (optional, disabled by default) # --------------------- @@ -34,16 +32,20 @@ version: "3.8" # INVENTREE_TAG=0.7.5 # +# ---------------------------- +# Docker compose customization +# ---------------------------- +# If you wish to customize the docker-compose script, you should only do so if you understand the stack! +# Do not expect support for customizations that are not part of the standard InvenTree setup! + services: # Database service # Use PostgreSQL as the database backend inventree-db: + image: postgres:13 container_name: inventree-db - image: ${POSTGRES_IMAGE:?You must provide the 'POSTGRES_IMAGE' variable in the .env file} expose: - ${INVENTREE_DB_PORT:-5432}/tcp - env_file: - - .env environment: - PGDATA=/var/lib/postgresql/data/pgdb - POSTGRES_USER=${INVENTREE_DB_USER:?You must provide the 'INVENTREE_DB_USER' variable in the .env file} @@ -55,43 +57,39 @@ services: restart: unless-stopped # redis acts as database cache manager - # only runs under the "redis" profile : https://docs.docker.com/compose/profiles/ inventree-cache: + image: redis:7-alpine container_name: inventree-cache - image: ${REDIS_IMAGE:?You must provide the 'REDIS_IMAGE' variable in the .env file} - depends_on: - - inventree-db env_file: - .env - profiles: - - redis expose: - - ${INVENTREE_CACHE_PORT:-6379} + - ${INVENTREE_CACHE_PORT:-6379} restart: always # InvenTree web server service # Uses gunicorn as the web server inventree-server: - container_name: inventree-server # If you wish to specify a particular InvenTree version, do so here - image: ${INVENTREE_IMAGE:?You must provide the 'INVENTREE_IMAGE' variable in the .env file} + image: inventree/inventree:${INVENTREE_TAG:-stable} + container_name: inventree-server + # Only change this port if you understand the stack. expose: - - 8000 - env_file: - - .env + - 8000 depends_on: - inventree-db + - inventree-cache + env_file: + - .env volumes: # Data volume must map to /home/inventree/data - - ${INVENTREE_EXT_VOLUME:?You must specify the 'INVENTREE_EXT_VOLUME' variable in the .env file!}:/home/inventree/data:z - - ./plugins:/home/inventree/InvenTree/plugins:z + - ${INVENTREE_EXT_VOLUME}:/home/inventree/data:z restart: unless-stopped # Background worker process handles long-running or periodic tasks inventree-worker: - container_name: inventree-worker # If you wish to specify a particular InvenTree version, do so here - image: ${INVENTREE_IMAGE:?You must provide the 'INVENTREE_IMAGE' variable in the .env file} + image: inventree/inventree:${INVENTREE_TAG:-stable} + container_name: inventree-worker command: invoke worker depends_on: - inventree-server @@ -99,26 +97,26 @@ services: - .env volumes: # Data volume must map to /home/inventree/data - - ${INVENTREE_EXT_VOLUME:?You must specify the 'INVENTREE_EXT_VOLUME' variable in the .env file!}:/home/inventree/data:z + - ${INVENTREE_EXT_VOLUME}:/home/inventree/data:z restart: unless-stopped - # nginx acts as a reverse proxy - # static files are served directly by nginx - # media files are served by nginx, although authentication is redirected to inventree-server - # web requests are redirected to gunicorn - # NOTE: You will need to provide a working nginx.conf file! + # caddy acts as reverse proxy and static file server + # https://hub.docker.com/_/caddy inventree-proxy: container_name: inventree-proxy - image: ${NGINX_IMAGE:?You must provide the 'NGINX_IMAGE' variable in the .env file} + image: caddy:alpine + restart: always depends_on: - inventree-server ports: - # Default web port is 1337 (can be changed in the .env file) - - ${INVENTREE_WEB_PORT:-1337}:8080 + - ${INVENTREE_WEB_PORT:-80}:80 + - 443:443 + env_file: + - .env volumes: - # Provide nginx configuration file to the container - # Refer to the provided example file as a starting point - - ./nginx.prod.conf:/etc/nginx/conf.d/default.conf:ro,Z - # nginx proxy needs access to static and media files - - ${INVENTREE_EXT_VOLUME:?You must specify the 'INVENTREE_EXT_VOLUME' variable in the .env file!}:/var/www:z - restart: unless-stopped + - ./Caddyfile:/etc/caddy/Caddyfile:ro,z + - ${INVENTREE_EXT_VOLUME}/static:/var/www/static:z + - ${INVENTREE_EXT_VOLUME}/media:/var/www/media:z + - ${INVENTREE_EXT_VOLUME}:/var/log:z + - ${INVENTREE_EXT_VOLUME}:/data:z + - ${INVENTREE_EXT_VOLUME}:/config:z diff --git a/nginx.prod.conf b/nginx.prod.conf deleted file mode 100644 index 1ebdcd2..0000000 --- a/nginx.prod.conf +++ /dev/null @@ -1,64 +0,0 @@ -server { - - # Listen for connection on (internal) port 8080 (unprivileged nginx) - listen 8080; - - real_ip_header proxy_protocol; - - location / { - - proxy_set_header Host $http_host; - proxy_set_header X-Forwarded-By $server_addr:$server_port; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header CLIENT_IP $remote_addr; - - proxy_pass_request_headers on; - - proxy_redirect off; - - client_max_body_size 100M; - - proxy_buffering off; - proxy_request_buffering off; - - # Change 'inventree-server' to the name of the inventree server container, - # and '8000' to the INVENTREE_WEB_PORT (if not default) - proxy_pass http://inventree-server:8000; - } - - # Redirect any requests for static files - location /static/ { - alias /var/www/static/; - autoindex on; - - # Caching settings - expires 30d; - add_header Pragma public; - add_header Cache-Control "public"; - } - - # Redirect any requests for media files - location /media/ { - alias /var/www/media/; - - # Media files require user authentication - auth_request /auth; - - # Content header to force download - add_header Content-disposition "attachment"; - } - - # Use the 'user' API endpoint for auth - location /auth { - internal; - - proxy_pass http://inventree-server:8000/auth/; - - proxy_pass_request_body off; - proxy_set_header Content-Length ""; - proxy_set_header X-Original-URI $request_uri; - } - -} diff --git a/sample.env b/sample.env index 40df1e0..859f58e 100644 --- a/sample.env +++ b/sample.env @@ -1,67 +1,55 @@ -# InvenTree environment variables for a postgresql production setup +# InvenTree environment variables for docker compose deployment +# For a full list of the available configuration options, refer to the InvenTree documentation: +# https://docs.inventree.org/en/stable/start/config/ + +# Specify the name of the docker-compose project COMPOSE_PROJECT_NAME=inventree -# Location of persistent database data (stored external to the docker containers) -# Note: You *must* un-comment this line, and point it to a path on your local machine +# InvenTree version tag (e.g. 'stable' / 'latest' / 'x.x.x') +INVENTREE_TAG=stable -# e.g. Linux -INVENTREE_EXT_VOLUME=./data +# InvenTree server URL - update this to match your server URL +INVENTREE_SITE_URL="http://inventree.localhost" +#INVENTREE_SITE_URL="http://192.168.1.2" # You can specify a local IP address here +#INVENTREE_SITE_URL="https://inventree.my-domain.com" # Or a public domain name (which you control) -# e.g. Windows (docker desktop) -#INVENTREE_EXT_VOLUME=c:/Users/me/inventree-data - -# Default web port for the InvenTree server -INVENTREE_WEB_PORT=8080 +# Specify the location of the external data volume +# By default, placed in local directory 'inventree-data' +INVENTREE_EXT_VOLUME=./inventree-data # Ensure debug is false for a production setup -INVENTREE_DEBUG=False INVENTREE_LOG_LEVEL=WARNING +# Enable custom plugins? +INVENTREE_PLUGINS_ENABLED=True + +# Run migrations automatically? +INVENTREE_AUTO_UPDATE=True + +# InvenTree superuser account details +# Un-comment (and complete) these lines to auto-create an admin account +#INVENTREE_ADMIN_USER= +#INVENTREE_ADMIN_PASSWORD= +#INVENTREE_ADMIN_EMAIL= + # Database configuration options -# Note: The example setup is for a PostgreSQL database +# DO NOT CHANGE THESE SETTINGS (unless you really know what you are doing) INVENTREE_DB_ENGINE=postgresql INVENTREE_DB_NAME=inventree INVENTREE_DB_HOST=inventree-db INVENTREE_DB_PORT=5432 -# Redis cache setup (disabled by default) -# Un-comment the following lines to enable Redis cache -# Note that you will also have to run docker-compose with the --profile redis command -# Refer to settings.py for other cache options -#INVENTREE_CACHE_HOST=inventree-cache -#INVENTREE_CACHE_PORT=6379 +# Database credentials - These should be changed from the default values! +# Note: These are *NOT* the InvenTree server login credentials, +# they are the credentials for the PostgreSQL database +INVENTREE_DB_USER=pguser +INVENTREE_DB_PASSWORD=pgpassword + +# Redis cache setup +# Refer to the documentation for other cache options +INVENTREE_CACHE_ENABLED=True +INVENTREE_CACHE_HOST=inventree-cache +INVENTREE_CACHE_PORT=6379 # Options for gunicorn server -INVENTREE_GUNICORN_TIMEOUT=30 - -# Enable custom plugins? -INVENTREE_PLUGINS_ENABLED=False - -# Image tag that should be used -INVENTREE_IMAGE=inventree/inventree:0.11.3 -REDIS_IMAGE=redis:7.0-alpine -NGINX_IMAGE=nginxinc/nginx-unprivileged:stable-alpine -# Postgres image must match version of pgdump in inventree image -POSTGRES_IMAGE=postgres:13-alpine - -# InvenTree admin account details -# make sure to use secure credentials these lines to auto-create an admin acount -INVENTREE_ADMIN_USER=admin -INVENTREE_ADMIN_PASSWORD=password -INVENTREE_ADMIN_EMAIL=admin@inventree.example - -# Database credentials - These must be configured before running -# Change from the default values! -INVENTREE_DB_USER=inventree -INVENTREE_DB_PASSWORD=password - -# Django configuration -INVENTREE_SECRET_KEY=some-secret-key -INVENTREE_SITE_URL=inventree.example.com,www.inventree.example.com - -# SSO Config -INVENTREE_SOCIAL_BACKENDS=allauth.socialaccount.providers.openid_connect - -HKNG_OIDC_URL=https://keycloak.example.com/realms/master/.well-known/openid-configuration -HKNG_OIDC_CLIENT_ID=example-client -HKNG_OIDC_SECRET=example-secret +INVENTREE_GUNICORN_TIMEOUT=90