Configuration
All settings come from environment variables. In Docker, put them in .env
next to the compose file; .env.example lists every one. In development all
of them are optional. Production reads everything from the environment; see
Deploying for the ones a first deploy needs.
Core
| Variable |
Default |
What it does |
DJANGO_SETTINGS_MODULE |
core.settings.development |
core.settings.production on a server |
SECRET_KEY |
|
Django's secret key; required in production |
DEBUG |
false |
Never on in production |
DATABASE_URL |
postgres://postgres:postgres@localhost:5432/postgres |
PostgreSQL 17 |
REDIS_URL |
redis://localhost:6379/0 |
Celery's broker and the cache |
CACHE_URL |
REDIS_URL |
A separate cache, if wanted |
LOG_LEVEL |
INFO |
|
Feeds and articles
| Variable |
Default |
What it does |
FEED_BACKOFF_BASE_MINUTES |
30 |
First retry delay for a failing feed, doubling each time |
FEED_MAX_BACKOFF_HOURS |
24 |
Longest retry delay |
FEED_STALE_DAYS |
60 |
A feed with nothing new for this long is shown as stale |
ARTICLE_REFETCH_DAYS |
7 |
Refetch undated articles daily while this young; 0 turns it off |
ARTICLE_TEXT_DAYS |
0 |
Drop older articles' text (not bookmarked ones); 0 keeps it |
Site and accounts
| Variable |
Default |
What it does |
SITE_NAME |
Curated Search |
Initial site name (then edited in the admin); error email subjects |
SIGNUP_OPEN |
false |
Initial signup switch, read by the first migrate only |
INVITATION_EXPIRY_DAYS |
7 |
How long an invitation link works |
STAFF_REQUIRE_2FA |
true |
Staff must use an authenticator app or passkey |
SITE_OWNER |
|
Shown on the privacy page |
CONTACT_EMAIL |
|
Privacy page and footer |
SITE_URL |
https://DOMAIN |
Links in emails |
Production
| Variable |
Default |
What it does |
DOMAIN |
|
The site's domain; also the default for the next two |
ALLOWED_HOSTS |
DOMAIN |
Comma-separated |
CSRF_TRUSTED_ORIGINS |
https://DOMAIN |
Comma-separated |
POSTGRES_PASSWORD |
|
The database superuser: backups and admin work only |
APP_DB_PASSWORD |
|
The app's own database role (deploy/initdb.sh) |
REDIS_PASSWORD |
|
openssl rand -hex 32; REDIS_URL is built from it |
ADMIN_URL |
|
The admin's path; keep it secret |
IMAGE |
:latest |
Pin an image version, e.g. ghcr.io/joeriksson/curated-search-next:sha-abc1234 |
TRUSTED_PROXY_COUNT |
1 |
Reverse proxies in front of the app; 0 if none |
SECURE_SSL_REDIRECT |
true |
The app redirects http to https itself |
SECURE_HSTS_SECONDS |
3600 |
Raise (e.g. 31536000) once HTTPS works well |
CSP_REPORT_ONLY |
false |
true: the Content-Security-Policy only reports |
BACKUP_HOUR_UTC |
3 |
Daily database dump into ./backups |
BACKUP_KEEP |
14 |
Dumps kept |
Email
Without EMAIL_HOST, emails are only logged.
| Variable |
Default |
What it does |
EMAIL_HOST |
|
SMTP server, e.g. smtp.protonmail.ch |
EMAIL_PORT |
587 |
587 = STARTTLS, 465 = implicit TLS |
EMAIL_HOST_USER |
|
|
EMAIL_HOST_PASSWORD |
|
Proton: an SMTP token |
EMAIL_USE_TLS |
true on 587 |
STARTTLS |
EMAIL_USE_SSL |
true on 465 |
Implicit TLS |
EMAIL_TIMEOUT |
15 |
Seconds |
DEFAULT_FROM_EMAIL |
|
Sender of the site's emails |
ADMINS |
|
Who gets error reports and monitoring alerts |
SERVER_EMAIL |
DEFAULT_FROM_EMAIL |
Sender of error reports |
Limits, statistics and monitoring
| Variable |
Default |
What it does |
RATE_LIMIT_SEARCH |
120/m |
Per IP (or account); empty means no limit |
RATE_LIMIT_SEARCH_FEED |
30/m |
Search Atom feeds |
RATE_LIMIT_SUGGEST |
10/h |
Feed suggestions |
SEARCH_STATS |
true |
Daily search counts for the insights page |
SEARCH_STATS_DAYS |
90 |
How long they are kept |
SECURITY_LOG_DAYS |
90 |
Security events kept in the database |
MONITOR_REFRESH_LATE_MINUTES |
3 x the refresh interval |
Alert ADMINS when feeds haven't refreshed for this long |
MONITOR_FEED_FAILURES |
5 |
...or a feed has failed this many times in a row |