Environment variables
All runtime configuration is read from environment variables. The canonical reference is
backend/.env.example in the repository. Copy it and edit:
cd backend
cp .env.example .env
Required
| Variable | Default | Description |
|---|---|---|
SECRET_KEY |
dev placeholder | Django's signing key, and it also signs every JWT. Startup fails outside dev if it is left at the placeholder. It must be at least 32 bytes: HS256 needs a key as long as its hash, and a shorter one only produces a warning nobody reads. Changing it signs everybody out. |
ENV_TYPE |
dev |
dev or prod. Controls where media is written and which security checks are enforced at startup. |
DEBUG |
True |
Set to False in production. |
ALLOWED_HOSTS |
localhost,127.0.0.1 |
Comma-separated. |
FRONTEND_URL |
http://localhost:5173 |
The web app's base URL, and the one setting that carries every link this system emails: magic-link sign-in, the customer's invoice and estimate portal links, the CSAT survey, and internal assignment notices. Outside dev a loopback value is refused at startup, because left at the default it mails your customers a link to their own machine. |
Database
There is no DATABASE_URL. The connection is assembled from five separate variables:
| Variable | Default |
|---|---|
DBNAME |
crm_db |
DBUSER |
postgres |
DBPASSWORD |
postgres |
DBHOST |
localhost |
DBPORT |
5432 |
PostgreSQL only, and the application user must not be a Postgres superuser. Superusers bypass row-level security, which is what isolates one organisation's rows from another's. See Postgres + RLS.
Connection pooling
| Variable | Default | Description |
|---|---|---|
DB_POOL_ENABLED |
False |
Turn on psycopg's connection pool. |
DB_POOL_MIN_SIZE |
2 |
|
DB_POOL_MAX_SIZE |
10 |
CONN_MAX_AGE is pinned to 0 because Django refuses a non-zero value alongside a pool.
Celery
| Variable | Default |
|---|---|
CELERY_BROKER_URL |
redis://localhost:6379/0 |
CELERY_RESULT_BACKEND |
redis://localhost:6379/0 |
Not optional if you want the scheduled work: recurring invoices, overdue marking, payment reminders, expired estimates, SLA breach scanning and stale-timer cleanup are all periodic jobs. Without a worker and a beat process, those features are silently inert.
| Variable | Default | Description |
|---|---|---|
EMAIL_BACKEND |
console backend | Use django_ses.SESBackend for AWS SES. |
DEFAULT_FROM_EMAIL |
noreply@localhost |
|
ADMIN_EMAIL |
admin@localhost |
|
AWS_SES_REGION_NAME |
ap-south-1 |
Read only when the backend is SES. |
AWS_SES_REGION_ENDPOINT |
derived from the region |
SES falls back to IAM role credentials if AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are unset.
For plain SMTP, set EMAIL_BACKEND to Django's SMTP backend and add Django's own EMAIL_HOST
family; the project does not define those itself.
Google OAuth
| Variable | Description |
|---|---|
GOOGLE_CLIENT_ID |
Leave blank to disable Google sign-in. |
GOOGLE_CLIENT_SECRET |
Magic-link sign-in works without either, so OAuth is genuinely optional.
CORS, CSRF and proxies
| Variable | Default | Description |
|---|---|---|
CORS_ALLOWED_ORIGINS |
dev origins | Comma-separated. |
CORS_ALLOW_ALL |
False |
Do not enable in production. |
CSRF_TRUSTED_ORIGINS |
dev origin | |
TRUST_PROXY_SSL_HEADER |
False |
Set to True only when TLS terminates at a proxy that is the only way in. It makes Django trust X-Forwarded-Proto, which is what lets HSTS emit a header at all. On direct HTTP it becomes a value any caller can forge. |
DOMAIN_NAME |
http://localhost:8000 |
The API's own origin. |
Frontend
The SvelteKit app reads two variables, and neither is named what this page used to claim:
| Variable | Description |
|---|---|
PUBLIC_DJANGO_API_URL |
Base URL of the Django backend. |
PUBLIC_SENTRY_DSN |
Optional error reporting. |
Anything prefixed PUBLIC_ is shipped to the browser. Never put a secret there.
Media and attachments
Files are written to the local filesystem under media/, always. There is no S3 or object-storage
backend: the project configures no STORAGES or DEFAULT_FILE_STORAGE, so AWS_BUCKET_NAME in
the example file is currently inert for uploads and the AWS keys matter only for SES. If you need
object storage, that is a change to the Django settings rather than an environment variable.
A single attachment is capped at 25 MB, and CSV imports and lead uploads at 5 MB.
Corrections
This page previously documented DJANGO_SECRET_KEY, JWT_SIGNING_KEY, DATABASE_URL, REDIS_URL,
EMAIL_HOST, EMAIL_HOST_USER, EMAIL_HOST_PASSWORD, EMAIL_PORT, GOOGLE_REDIRECT_URI,
AWS_STORAGE_BUCKET_NAME, AWS_S3_ENDPOINT_URL, PUBLIC_API_URL and PUBLIC_GOOGLE_CLIENT_ID, and
pointed at a .env.docker.example file. None of those names is read by crm/settings.py and that
file does not exist, so anyone configuring a deployment from this page could not have started the
application. Kept as a note rather than deleted, so somebody debugging a .env written from the old
version can see what happened.