Skip to content

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.

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.

VariableRequiredPurpose
NEXT_PUBLIC_API_URLyesPublic (browser-facing) backend base URL. No trailing slash. Used for data fetches, media, and API rewrites.
NEXT_PUBLIC_IIIF_UPSTREAMyesBase 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_URLyesCanonical public site URL for metadata, sitemap, and robots.
CORS_ALLOWED_ORIGINSyesComma-separated origins allowed on the frontend’s own /api/* routes.
INTERNAL_API_URLnoServer-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.
frontend/.env
NEXT_PUBLIC_API_URL=https://api.archetype.example.org
NEXT_PUBLIC_IIIF_UPSTREAM=https://archetype.example.org
NEXT_PUBLIC_SITE_URL=https://archetype.example.org
CORS_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://api

Server-vs-client URL split. Inside a container, localhost is the container itself, so browser-facing NEXT_PUBLIC_API_URL is unreachable from the Next server. Set INTERNAL_API_URL to point server-side fetches at a container-reachable host: http://host.docker.internal:8000 for the dev container (already set in frontend/compose.yml), http://api in the infrastructure stack. On host-run pnpm dev, leave it unset.

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.

VariableDefaultPurpose
SECRET_KEYinsecure placeholderSigns sessions/tokens. Boot fails if unset or left at the django-insecure… default while DEBUG=False.
DEBUGFalseDebug mode. Off in production (enables HTTPS hardening, secure cookies, HSTS).
DATABASE_URLsqlite fallbackPostgres DSN, e.g. postgresql://user:pass@postgres:5432/db.
ALLOWED_HOSTSlocalhost,127.0.0.1Comma-separated hostnames Django will serve.
CORS_ALLOWED_ORIGINSlocalhost:3000/8000Origins allowed to call the API (scheme + host).
CSRF_TRUSTED_ORIGINSlocalhost:3000/8000Trusted origins for CSRF (admin + frontend).
MEILISEARCH_URLhttp://localhost:7700Meilisearch endpoint (compose-internal: http://meilisearch:7700).
MEILISEARCH_API_KEYemptyMaster key; required in production.
MEILISEARCH_INDEX_PREFIXemptyPrefix to namespace indexes on a shared Meilisearch.
IIIF_HOSThttp://localhost:8182/Browser-facing Sipi base URL the API embeds in IIIF/thumbnail links.
IIIF_INTERNAL_HOSTIIIF_HOSTServer-to-server Sipi address for the API’s own info.json probes (compose sets http://image_server:1024/sipi in production).
UPLOADS_SIPI_BASE_URLIIIF_HOSTSipi address the Celery worker uses to tile-check a converted upload (compose sets http://image_server:1024/sipi in production).
UPLOADS_STALE_AFTER_DAYS7Age at which cleanup_stale_uploads reaps unfinished upload sessions.
CELERY_BROKER_URL / CELERY_RESULT_BACKENDredis://redis:6379/0Celery broker + result store.
CACHE_URLredis://redis:6379/1Redis cache backing the search-reindex lock.
DRF_THROTTLE_ANON_RATE / DRF_THROTTLE_USER_RATE3000/hour / 30000/hourDRF rate limits.
SEARCH_AUTO_SYNCTruePush each model change to the search indexes. Set false to pause during bulk imports, then just reindex.
SECURE_SSL_REDIRECTTrueHTTPS 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.

api/config/.env
SECRET_KEY="<long-random-string>"
DEBUG=false
DATABASE_URL="postgresql://postgres:<pass>@postgres:5432/local"
ALLOWED_HOSTS=archetype.example.org,host.docker.internal
CORS_ALLOWED_ORIGINS=https://archetype.example.org
CSRF_TRUSTED_ORIGINS=https://archetype.example.org
MEILISEARCH_URL=http://meilisearch:7700
MEILISEARCH_API_KEY=<meili-master-key>
IIIF_HOST=https://archetype.example.org/sipi

DisallowedHost / ALLOWED_HOSTS. Every hostname that reaches Django — public domain, plus host.docker.internal for the containerized dev frontend or api in the infrastructure stack — must be listed, or requests 400 with DisallowedHost. In dev, MEDIA_ROOT is storage/media/ under api/.

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:

VariablePurpose
DOMAINPublic domain nginx serves (localhost for local).
POSTGRES_DB / POSTGRES_USER / POSTGRES_PASSWORDPostgres container credentials (.env) — keep aligned with DATABASE_URL.
MEILI_ENV / MEILI_MASTER_KEYdevelopment (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_PASSWORDRedis 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_PASSWORDSipi hardening — override the vendored demo defaults in production.
BACKEND_IMAGE / FRONTEND_IMAGEPin container images to an immutable tag (:sha-<commit>) or @sha256: digest (.env).
MEDIA_HOST_PATHHost path of the image corpus, mounted into api, celery, Sipi and nginx (default ./storage/media).
INTERNAL_API_URLhttp://api, with api added to ALLOWED_HOSTS.
infrastructure/env_file
DOMAIN=archetype.example.org
POSTGRES_PASSWORD=<pass>
DATABASE_URL="postgresql://postgres:<pass>@postgres:5432/local"
MEILI_ENV=production
MEILI_MASTER_KEY=<meili-master-key>
MEILISEARCH_API_KEY=<meili-master-key>
IIIF_HOST=https://archetype.example.org/sipi
ALLOWED_HOSTS=archetype.example.org,api
INTERNAL_API_URL=http://api
NEXT_PUBLIC_API_URL=https://archetype.example.org

After changing IIIF_HOST, run just reindex from infrastructure/ so stored thumbnail links pick up the new host.

Next: Production deployment · Troubleshooting · The API · For contributors.