the_inventory

Environment Configuration Guide

This guide explains how to configure The Inventory backend API for different environments using environment variables.

Note: This covers the backend API only. For frontend configuration, see the-inventory-ui.


Table of Contents


Quick Start

Local Development (5 minutes)

# 1. Copy environment template (root directory)
cp .env.example .env.local

# 2. Navigate to backend
cd src

# 3. Run migrations
python manage.py migrate

# 4. Create superuser
python manage.py createsuperuser

# 5. Start Django
python manage.py runserver

That’s it! Default values work for local development:

Docker Deployment

# Build image
docker build -t the_inventory .

# Run with environment variables
docker run -p 8000:8000 \
  -e SECRET_KEY="your-secret-key" \
  -e DATABASE_URL="postgresql://user:pass@db:5432/inventory" \
  -e REDIS_URL="redis://redis:6379/0" \
  the_inventory

Environment variables are read from your orchestration platform (Render, Docker Compose, Kubernetes, etc.) — not from .env files.


Configuration Overview

How Environment Variables Are Loaded

Backend (Django):

┌─────────────────────────────────────────┐
│  OS Environment Variables               │ ← From platform (Render, K8s, Docker, etc.)
│  .env file (root directory)             │ ← From .env or .env.local
│  Code defaults (settings/*.py)          │ ← Fallback defaults
└─────────────────────────────────────────┘

Priority: OS environment > .env file > code defaults

Note: Files in .gitignore (.env, .env.local) are never committed — you must set environment variables on your platform (Render, Docker, K8s, etc.) for production.

Settings Pattern

The project uses split Django settings:

File When Used Debug Database Purpose
the_inventory/settings/dev.py Local development True SQLite Fast iteration, all features enabled
the_inventory/settings/production.py Render, Docker, K8s False PostgreSQL (required) Security hardening, optimized defaults
the_inventory/settings/base.py Always Shared config (apps, middleware, templates)

Selection: DJANGO_SETTINGS_MODULE environment variable (default: production in containers, dev locally)


Backend Environment Variables

Core Settings

Required and important variables for both development and production.

SECRET_KEY

ALLOWED_HOSTS

DJANGO_LOG_LEVEL

LANGUAGE_CODE & TIME_ZONE (Optional)

Database & Storage

DATABASE_URL

STATIC_URL & MEDIA_URL (Optional)

Redis & Caching

REDIS_URL

CELERY_BROKER_URL

CELERY_TASK_ALWAYS_EAGER

Frontend Integration & URLs

FRONTEND_URL

PUBLIC_BASE_URL (Optional)

WAGTAILADMIN_BASE_URL (Optional)

WAGTAIL_SITE_NAME (Optional)

Tenants & Registration

ENABLE_PUBLIC_TENANT_REGISTRATION

AUDIT_TENANT_ACCESS

CORS & CSRF Security

CORS_ALLOWED_ORIGINS

CORS_ALLOW_ALL_ORIGINS

CORS_ALLOW_CREDENTIALS

CORS_EXTRA_HEADERS (Optional)

CSRF_TRUSTED_ORIGINS

USE_X_FORWARDED_PROTO

REST API & JWT Authentication

API_PAGE_SIZE

JWT_ACCESS_TOKEN_MINUTES

JWT_REFRESH_TOKEN_DAYS

The system uses HTTP-only cookies for secure browser-based JWT authentication. These settings control how JWT tokens are transmitted via cookies and ensure cookies work correctly in both development (localhost) and production (subdomains) scenarios.

Important: JWT cookies cannot be shared across mismatched hostnames (e.g., localhost vs 127.0.0.1). Ensure your frontend and backend use consistent hostnames.

Development (Single Machine with localhost):

# Frontend: http://localhost:3000
# Backend: http://localhost:8000
JWT_COOKIE_DOMAIN=          # (unset, same-domain only)
JWT_COOKIE_PATH=/
JWT_COOKIE_SECURE=false     # (allow HTTP)
JWT_COOKIE_SAMESITE=Lax
JWT_ACCESS_TOKEN_COOKIE_MAX_AGE=300
JWT_REFRESH_TOKEN_COOKIE_MAX_AGE=604800

Production (Subdomain Sharing over HTTPS):

# Frontend: https://app.example.com
# Backend: https://api.example.com
JWT_COOKIE_DOMAIN=.example.com      # Share across subdomains
JWT_COOKIE_PATH=/
JWT_COOKIE_SECURE=true              # HTTPS required
JWT_COOKIE_SAMESITE=Lax
JWT_ACCESS_TOKEN_COOKIE_MAX_AGE=300
JWT_REFRESH_TOKEN_COOKIE_MAX_AGE=604800

Production (Cross-Origin SPA over HTTPS):

# Frontend: https://widgets.example.com
# Backend: https://api.different.com
JWT_COOKIE_DOMAIN=.different.com
JWT_COOKIE_PATH=/
JWT_COOKIE_SECURE=true              # HTTPS required
JWT_COOKIE_SAMESITE=None            # Cross-site cookies
JWT_ACCESS_TOKEN_COOKIE_MAX_AGE=300
JWT_REFRESH_TOKEN_COOKIE_MAX_AGE=604800

Caching TTLs

STOCK_CACHE_TTL_SECONDS

DASHBOARD_CACHE_TTL_SECONDS

OpenAPI Documentation

API_DOC_TITLE, API_DOC_DESCRIPTION, API_DOC_VERSION

Email Configuration

EMAIL_BACKEND

EMAIL_HOST, EMAIL_PORT, EMAIL_USE_TLS, EMAIL_USE_SSL

EMAIL_HOST_USER, EMAIL_HOST_PASSWORD

DEFAULT_FROM_EMAIL

Environment-Specific Defaults

Local Development

Variable Dev Default Notes
DEBUG True Full error pages, static file serving
SECRET_KEY insecure-dev-key Insecure but fine for local
ALLOWED_HOSTS * Accept all hosts locally
DATABASE_URL SQLite (db.sqlite3) File-based, no setup needed
REDIS_URL Not required Uses in-memory cache if not set
CELERY_TASK_ALWAYS_EAGER true Tasks run synchronously
EMAIL_BACKEND console Emails printed to console
CORS_ALLOWED_ORIGINS http://localhost:3000,http://localhost:5173 Local frontend origins
JWT_COOKIE_SECURE false Allow JWT cookies over HTTP

Result:

Production (Docker/Render/K8s)

Variable Production Default Notes
DEBUG False Error pages, no stack traces
SECRET_KEY Required Must be set via platform secrets
ALLOWED_HOSTS Required Must match your domain
DATABASE_URL Required PostgreSQL connection string
REDIS_URL Optional Recommended for caching/Celery
CELERY_TASK_ALWAYS_EAGER false Queue tasks to Redis broker
EMAIL_BACKEND smtp Use real SMTP server
CORS_ALLOWED_ORIGINS Not set — must override Your production frontend URL(s)
JWT_COOKIE_SECURE true Only send JWT cookies over HTTPS

Result:

—## Setup Guides

Local Development Setup

Prerequisites

Django Backend

  1. Clone the repository:
    git clone https://github.com/Ndevu12/the_inventory.git
    cd the_inventory
    
  2. Create virtual environment:
    python -m venv venv
    source venv/bin/activate  # On Windows: venv\Scripts\activate
    
  3. Install dependencies:
    pip install -r requirements.txt
    pip install -r requirements-dev.txt  # For testing/linting tools
    
  4. Copy environment template:
    cp .env.example .env.local
    
  5. Run migrations:
    python manage.py migrate
    
  6. Create superuser:
    python manage.py createsuperuser
    
  7. (Optional) Seed database:
    python manage.py seed_database --clear --create-default
    
  8. Start server:
    python manage.py runserver
    

    Visit: http://localhost:8000/admin/

Next.js Frontend

  1. Navigate to frontend directory:
    cd frontend
    
  2. Copy environment template:
    cp .env.local.example .env.local
    
  3. Install dependencies:
    yarn install
    
  4. Start development server:
    yarn dev
    

    Visit: http://localhost:3000

Verify Integration

The system uses HTTP-only cookies for JWT authentication. For cookies to work correctly between frontend and backend, both must use the same hostname:

✅ Correct (will work):

❌ Incorrect (cookies won’t be shared):

To verify:

  1. Login at http://localhost:3000
  2. Open browser DevTools → Application/Storage → Cookies
  3. Verify access_token and refresh_token cookies are present for localhost
  4. If cookies show domain 127.0.0.1 but frontend URL shows localhost, you have a hostname mismatch

Docker Deployment

Build and Run Locally

# Build image
docker build -t the_inventory:latest .

# Run with minimal environment (uses defaults)
docker run -p 8000:8000 \
  -e SECRET_KEY="$(python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())')" \
  -e ALLOWED_HOSTS="localhost,127.0.0.1" \
  the_inventory:latest

Deploy to Render

  1. Connect your GitHub repository to Render

  2. Create a Web Service with these Environment variables:

    DJANGO_SETTINGS_MODULE        production
    SECRET_KEY                    <generate and paste here>
    ALLOWED_HOSTS                 your-service.onrender.com
    DATABASE_URL                  <Render PostgreSQL connection string>
    REDIS_URL                     <Render Redis connection string>
    FRONTEND_URL                  https://your-frontend.vercel.app
    AUTO_SEED_DATABASE            false  # true only for initial setup
    
  3. Database will auto-migrate via entrypoint.sh

Deploy Frontend to Vercel

  1. Fork the repository

  2. Create new Vercel project from frontend/ directory

  3. Set these Environment variables:

    NEXT_PUBLIC_API_URL      https://your-service.onrender.com/api/v1
    NEXT_PUBLIC_APP_NAME     The Inventory - Production
    
  4. Deploy — Vercel will build and deploy automatically


Production Checklist

Before deploying to production:


Translations (Django & Next.js)

Django (gettext)

Next.js (next-intl)


Troubleshooting

Common Issues

Django won’t start with “DisallowedHost” error

Error:

400 Bad Request — Invalid HTTP_HOST header: 'my-service.onrender.com'. 
Expected one of: ['localhost', '127.0.0.1']

Solution: Add your domain to ALLOWED_HOSTS:

ALLOWED_HOSTS=localhost,127.0.0.1,my-service.onrender.com

Frontend can’t reach API (CORS error)

Error (browser console):

Access to XMLHttpRequest at 'http://localhost:8000/api/v1/...' from origin 'http://localhost:3000' 
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Solution: Ensure backend has frontend origin in CORS_ALLOWED_ORIGINS:

CORS_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173

If frontend is on different machine/IP:

CORS_ALLOWED_ORIGINS=http://192.168.1.100:3000

“SECRET_KEY is missing” error in production

Error:

ValueError: SECRET_KEY is required for production deployments

Solution: Set SECRET_KEY via platform environment:

  1. Generate: python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"
  2. Store in Render Secrets, GitHub Secrets, or AWS Secrets Manager
  3. Set as environment variable on service

Never hardcode secrets in .env files committed to git!


Database connection fails

Error:

django.core.exceptions.ImproperlyConfigured: 
The 'django.contrib.postgres' app is available, 
but not installed. (No installed app with label 'postgres'.)

Solution: Ensure DATABASE_URL is set correctly:

# Format: postgresql://[user]:[password]@[host]:[port]/[database]
DATABASE_URL=postgresql://postgres:password@localhost:5432/the_inventory

Check connection:

# Test connection locally
psql postgresql://postgres:password@localhost:5432/the_inventory -c "SELECT 1"

Redis connection fails

Error:

ConnectionError: Error 111 connecting to localhost:6379. Connection refused.

Solution: Redis is optional. If you don’t have Redis running:


Email not sending

Error:

No handlers could be found for logger "django.request"
Connection refused connecting to SMTP server

Solution:

  1. Check email backend:
    EMAIL_BACKEND=django.core.mail.backends.console.EmailBackend  # Dev only
    
  2. For production SMTP:
    EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
    EMAIL_HOST=smtp.gmail.com
    EMAIL_PORT=587
    EMAIL_USE_TLS=true
    EMAIL_HOST_USER=your-email@gmail.com
    EMAIL_HOST_PASSWORD=your-app-password  # Use app-specific password for Gmail!
    DEFAULT_FROM_EMAIL=noreply@example.com
    
  3. Test locally:
    python manage.py shell
    >>> from django.core.mail import send_mail
    >>> send_mail(
    ...     'Test', 
    ...     'This is a test email', 
    ...     'noreply@example.com', 
    ...     ['recipient@example.com']
    ... )
    

“Unexpected keyword argument: SEED_MODELS” error

Error:

TypeError: seed_database() got unexpected keyword argument 'seed_models'

Solution: The environment variable is SEED_MODELS (with underscore), not SEED-MODELS:

# ✅ Correct
SEED_MODELS=categories,products

# ❌ Wrong
SEED_MODELS=categories,products  # (typo in env var name)

Migrations pending error

Error:

You have unapplied migrations for apps: inventory, ...

Solution: Run migrations:

python manage.py migrate

Or in Docker, migrations auto-run via entrypoint.sh.


Getting Help

If you’re stuck:

  1. Check logs — look for error messages
    • Django: python manage.py runserver console output
    • Docker: docker logs <container-id>
    • Render: Dashboard → Logs tab
  2. Enable debug logging:
    DJANGO_LOG_LEVEL=DEBUG
    
  3. Consult documentation:
  4. Open an issue on GitHub with error messages and environment details

Quick Reference

Environment Variables by Category

Category Variables
Core SECRET_KEY, ALLOWED_HOSTS, DJANGO_LOG_LEVEL
Database DATABASE_URL, STATIC_URL, MEDIA_URL
Caching REDIS_URL, CELERY_BROKER_URL, CELERY_TASK_ALWAYS_EAGER
URLs FRONTEND_URL, PUBLIC_BASE_URL, WAGTAILADMIN_BASE_URL
Security CORS_ALLOWED_ORIGINS, CSRF_TRUSTED_ORIGINS, JWT_COOKIE_SAMESITE, JWT_COOKIE_SECURE
Tenants ENABLE_PUBLIC_TENANT_REGISTRATION, AUDIT_TENANT_ACCESS
API API_PAGE_SIZE, JWT_ACCESS_TOKEN_MINUTES, JWT_REFRESH_TOKEN_DAYS
Caching TTLs STOCK_CACHE_TTL_SECONDS, DASHBOARD_CACHE_TTL_SECONDS
Docs API_DOC_TITLE, API_DOC_DESCRIPTION, API_DOC_VERSION
Email EMAIL_BACKEND, EMAIL_HOST, EMAIL_PORT, EMAIL_HOST_USER, DEFAULT_FROM_EMAIL
Seeding AUTO_SEED_DATABASE, SEED_CLEAR, SEED_QUIET, SEED_TENANT, SEED_MODELS
Frontend NEXT_PUBLIC_API_URL, NEXT_PUBLIC_APP_NAME

All Environment Variables (Testing Checklist)

Backend (.env or platform environment):

Frontend (.env.local):


Additional Resources