Configuration
Every Archetype deployment is configured through environment variables — a small required core plus a large set of optional overrides for search, storage, security, and dataset vocabulary. This page is a reference for the variables that matter, grouped by component. The three services read config independently: the frontend (Next.js) from frontend/.env, the backend (Django) from api/config/.env, and the full-stack infrastructure compose from infrastructure/env_file (plus a .env beside it for Compose interpolation). Start from the shipped templates: frontend/.env.example, api/config/test.env, and infrastructure/env_file.example.
New to this? Start with the Run it locally, then come back to tune your instance. To load a dataset, see Load your own corpus.
Frontend (frontend/.env)
Section titled “Frontend (frontend/.env)”The frontend validates its required variables at build and startup (lib/env.ts, next.config.mjs) and throws on any missing one, so the app fails fast rather than booting misconfigured. In a production image the NEXT_PUBLIC_* values and CORS_ALLOWED_ORIGINS are Docker build args baked in at build time (frontend/Dockerfile), not runtime settings.
| Variable | Required | Purpose |
|---|---|---|
NEXT_PUBLIC_API_URL | yes | Public (browser-facing) backend base URL. No trailing slash. Used for data fetches, media, and API rewrites. |
NEXT_PUBLIC_IIIF_UPSTREAM | yes | Base URL of the Sipi IIIF image server, proxied via /iiif-proxy. Trailing slash is stripped automatically. Behind the infrastructure nginx, use the bare origin — never …/sipi, or the segment doubles and every image 404s. |
NEXT_PUBLIC_SITE_URL | yes | Canonical public site URL for metadata, sitemap, and robots. |
CORS_ALLOWED_ORIGINS | yes | Comma-separated origins allowed on the frontend’s own /api/* routes. |
INTERNAL_API_URL | no | Server-only base for SSR / server-component / route-handler fetches. Not a NEXT_PUBLIC_ var — never reaches the browser. Falls back to NEXT_PUBLIC_API_URL when unset. |
NEXT_PUBLIC_API_URL=https://api.archetype.example.orgNEXT_PUBLIC_IIIF_UPSTREAM=https://archetype.example.orgNEXT_PUBLIC_SITE_URL=https://archetype.example.orgCORS_ALLOWED_ORIGINS=https://archetype.example.org# Server-side fetches: http://api in the infrastructure stack,# http://host.docker.internal:8000 for the containerized dev frontend# INTERNAL_API_URL=http://apiServer-vs-client URL split. Inside a container,
localhostis the container itself, so browser-facingNEXT_PUBLIC_API_URLis unreachable from the Next server. SetINTERNAL_API_URLto point server-side fetches at a container-reachable host:http://host.docker.internal:8000for the dev container (already set infrontend/compose.yml),http://apiin the infrastructure stack. On host-runpnpm dev, leave it unset.
Backend (api/config/.env)
Section titled “Backend (api/config/.env)”Django reads config via django-environ in api/config/settings.py. Create the file with cp config/test.env config/.env; the dev just recipes refuse to run without it and pass it to docker compose as --env-file.
| Variable | Default | Purpose |
|---|---|---|
SECRET_KEY | insecure placeholder | Signs sessions/tokens. Boot fails if unset or left at the django-insecure… default while DEBUG=False. |
DEBUG | False | Debug mode. Off in production (enables HTTPS hardening, secure cookies, HSTS). |
DATABASE_URL | sqlite fallback | Postgres DSN, e.g. postgresql://user:pass@postgres:5432/db. |
ALLOWED_HOSTS | localhost,127.0.0.1 | Comma-separated hostnames Django will serve. |
CORS_ALLOWED_ORIGINS | localhost:3000/8000 | Origins allowed to call the API (scheme + host). |
CSRF_TRUSTED_ORIGINS | localhost:3000/8000 | Trusted origins for CSRF (admin + frontend). |
MEILISEARCH_URL | http://localhost:7700 | Meilisearch endpoint (compose-internal: http://meilisearch:7700). |
MEILISEARCH_API_KEY | empty | Master key; required in production. |
MEILISEARCH_INDEX_PREFIX | empty | Prefix to namespace indexes on a shared Meilisearch. |
IIIF_HOST | http://localhost:8182/ | Browser-facing Sipi base URL the API embeds in IIIF/thumbnail links. |
IIIF_INTERNAL_HOST | IIIF_HOST | Server-to-server Sipi address for the API’s own info.json probes (compose sets http://image_server:1024/sipi in production). |
UPLOADS_SIPI_BASE_URL | IIIF_HOST | Sipi address the Celery worker uses to tile-check a converted upload (compose sets http://image_server:1024/sipi in production). |
UPLOADS_STALE_AFTER_DAYS | 7 | Age at which cleanup_stale_uploads reaps unfinished upload sessions. |
CELERY_BROKER_URL / CELERY_RESULT_BACKEND | redis://redis:6379/0 | Celery broker + result store. |
CACHE_URL | redis://redis:6379/1 | Redis cache backing the search-reindex lock. |
DRF_THROTTLE_ANON_RATE / DRF_THROTTLE_USER_RATE | 3000/hour / 30000/hour | DRF rate limits. |
SEARCH_AUTO_SYNC | True | Push each model change to the search indexes. Set false to pause during bulk imports, then just reindex. |
SECURE_SSL_REDIRECT | True | HTTPS redirect when DEBUG is off. Set False when TLS ends upstream (e.g. Cloudflare Tunnel). |
Dataset vocabulary choices (HISTORICAL_ITEM_TYPES, HISTORICAL_ITEM_HAIR_TYPES, REPOSITORY_TYPES, CHARACTER_ITEM_TYPES) and SITE_NAME are also set here, as comma-separated lists. Model display labels are not env vars: superusers edit them in the backoffice, and they are stored in the database.
SECRET_KEY="<long-random-string>"DEBUG=falseDATABASE_URL="postgresql://postgres:<pass>@postgres:5432/local"ALLOWED_HOSTS=archetype.example.org,host.docker.internalCORS_ALLOWED_ORIGINS=https://archetype.example.orgCSRF_TRUSTED_ORIGINS=https://archetype.example.orgMEILISEARCH_URL=http://meilisearch:7700MEILISEARCH_API_KEY=<meili-master-key>IIIF_HOST=https://archetype.example.org/sipi
DisallowedHost/ALLOWED_HOSTS. Every hostname that reaches Django — public domain, plushost.docker.internalfor the containerized dev frontend orapiin the infrastructure stack — must be listed, or requests 400 withDisallowedHost. In dev,MEDIA_ROOTisstorage/media/underapi/.
Infrastructure (infrastructure/env_file)
Section titled “Infrastructure (infrastructure/env_file)”The full-stack compose (nginx, Postgres, Meilisearch, Redis, Sipi, api, frontend, celery) passes a single env_file to its services. Copy infrastructure/env_file.example. It carries the backend and frontend values above plus deployment-only knobs. Variables that compose.yaml itself interpolates — the Postgres credentials, MEILI_ENV, REDIS_PASSWORD, MEDIA_HOST_PATH and the image pins — are read from a .env file beside compose.yaml, so put them there too:
| Variable | Purpose |
|---|---|
DOMAIN | Public domain nginx serves (localhost for local). |
POSTGRES_DB / POSTGRES_USER / POSTGRES_PASSWORD | Postgres container credentials (.env) — keep aligned with DATABASE_URL. |
MEILI_ENV / MEILI_MASTER_KEY | development (keyless) or production with a random key (≥16 bytes). MEILISEARCH_API_KEY must match the master key. Meilisearch is not exposed on the host. |
REDIS_PASSWORD | Redis auth; the celery service builds its broker URLs from it, but the api reads CELERY_BROKER_URL, CELERY_RESULT_BACKEND and CACHE_URL from env_file, so include the password there. |
SIPI_JWT_SECRET (42 chars) / SIPI_ADMIN_USER / SIPI_ADMIN_PASSWORD | Sipi hardening — override the vendored demo defaults in production. |
BACKEND_IMAGE / FRONTEND_IMAGE | Pin container images to an immutable tag (:sha-<commit>) or @sha256: digest (.env). |
MEDIA_HOST_PATH | Host path of the image corpus, mounted into api, celery, Sipi and nginx (default ./storage/media). |
INTERNAL_API_URL | http://api, with api added to ALLOWED_HOSTS. |
DOMAIN=archetype.example.orgPOSTGRES_PASSWORD=<pass>DATABASE_URL="postgresql://postgres:<pass>@postgres:5432/local"MEILI_ENV=productionMEILI_MASTER_KEY=<meili-master-key>MEILISEARCH_API_KEY=<meili-master-key>IIIF_HOST=https://archetype.example.org/sipiALLOWED_HOSTS=archetype.example.org,apiINTERNAL_API_URL=http://apiNEXT_PUBLIC_API_URL=https://archetype.example.orgAfter changing
IIIF_HOST, runjust reindexfrominfrastructure/so stored thumbnail links pick up the new host.
Next: Production deployment · Troubleshooting · The API · For contributors.