FabrikFabrik
FabrikDeployment

Environment variables

Every .env key Fabrik reads — what it controls, whether it's required, and what to set it to in production.

All configuration lives in one .env file at the repository root. docker-compose.yml loads it for every service; Django reads it through os.environ at startup. This page walks through every key grouped by concern.

Keys marked required use the ${VAR:?} syntax in compose — missing them fails docker compose up immediately with a clear error, which is intentional.

Security

Never reuse the placeholder values from .env.example. Generate fresh secrets per deployment. Committing .env to git is a data breach.

KeyRequiredPurpose
DJANGO_SECRET_KEYDjango's signing key — JWT, CSRF, session cookies. Rotating invalidates all sessions.
ENCRYPTION_KEYFernet key for APIC passwords, AWX tokens, and other credential fields. Rotating requires re-encrypting every stored credential.

Generate both:

python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"

Django

KeyDefaultPurpose
DEBUGfalseMust be false in production. true disables many security checks and exposes stack traces.
ALLOWED_HOSTS— (required)Comma-separated hostnames. Requests to other hosts get Django 400. Use the public hostname, not the container name.
CORS_ALLOWED_ORIGINSComma-separated URLs that can call the API from a browser. Usually matches the frontend origin. No wildcards in production.
SITE_URLCanonical URL Fabrik uses when it generates absolute links (email, password reset).
SECURE_SSL_REDIRECTtrueRedirects HTTP to HTTPS at the Django layer. Leave on if you terminate TLS at nginx.
SESSION_COOKIE_SECUREtrueSession cookies served over HTTPS only.
CSRF_COOKIE_SECUREtrueCSRF cookie served over HTTPS only.
SECURE_HSTS_SECONDS31536000HSTS max-age (1 year). Only enable once you're confident TLS is correct — browsers remember.

Timezone

KeyDefaultPurpose
TZUTCContainer timezone. Affects log timestamps, cron-style scheduled tasks, and the default if a user hasn't picked a timezone.
DEFAULT_USER_TIMEZONEUTCInitial display timezone for newly created users. They can change it in Settings → Appearance.

Scheduled tasks run in their configured timezone, not the server's — see Scheduled queries for the distinction.

PostgreSQL

KeyDefaultPurpose
POSTGRES_DBfabrikDatabase name.
POSTGRES_USERfabrikDatabase user.
POSTGRES_PASSWORD— (required)Database password.
POSTGRES_HOSTpostgresContainer hostname. Keep as postgres unless you use an external database.
POSTGRES_PORT5432Database port.
DATABASE_URL— (required)Full connection string: postgresql://user:pass@host:port/dbname.

DATABASE_URL is the source of truth, not POSTGRES_*. Django reads DATABASE_URL. The Postgres container, on the other hand, is initialised from POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DB. If the password embedded in DATABASE_URL doesn't match POSTGRES_PASSWORD, the database boots fine but Django fails with password authentication failed for user "fabrik" on first migration. Always change them together.

Neo4j

KeyDefaultPurpose
NEO4J_URIbolt://neo4j:7687Bolt protocol URL.
NEO4J_USERneo4jNeo4j username (can't be changed).
NEO4J_PASSWORD— (required)Neo4j password. Set once; rotating requires restarting the container.
NEO4J_HEAP_MAX_SIZE1GJVM heap ceiling. Raise for large MIM imports.
NEO4J_PAGECACHE_SIZE256MPage cache. Bigger = faster queries, more RAM used.

MIM registry

The MIM (Cisco ACI Managed Information Model) is not seeded from environment variables. After the stack is running, an admin loads the desired version through Settings → MIM Management → Cisco DevNet in the UI; ~17,500 classes stream from pubhub.devnetcloud.com directly into the local Neo4j on demand.

Redis and Celery

KeyDefaultPurpose
REDIS_URLredis://redis:6379Base Redis URL (used for Channels layer).
CELERY_BROKER_URL— (required)Celery task queue. Usually redis://redis:6379/0.
CELERY_RESULT_BACKEND— (required)Celery result storage. Usually redis://redis:6379/1.
CELERY_WORKER_CONCURRENCY2Parallel task slots per worker container. Raise if query execution backs up.

RabbitMQ

Used only for AWX event streaming. Separate from Celery to isolate high-frequency event traffic.

KeyDefaultPurpose
RABBITMQ_USERfabrikBroker user.
RABBITMQ_PASSWORD— (required)Broker password.
RABBITMQ_VHOSTfabrikVirtual host.
RABBITMQ_URL— (required)Full AMQP URL: amqp://user:pass@host:port/vhost.

Same gotcha as PostgreSQL: the AWX event consumer reads RABBITMQ_URL, while the broker container is initialised from RABBITMQ_USER / RABBITMQ_PASSWORD / RABBITMQ_VHOST. The password embedded in RABBITMQ_URL must match RABBITMQ_PASSWORD. Mismatch = the consumer logs ACCESS_REFUSED and AWX webhook events silently pile up unprocessed.

Email (SMTP)

KeyDefaultPurpose
EMAIL_ENABLEDtrueMaster switch. false silently drops every email attempt.
EMAIL_BACKENDdjango.core.mail.backends.smtp.EmailBackendKeep as SMTP; change only for testing.
EMAIL_HOSTSMTP hostname.
EMAIL_PORT587SMTP port.
EMAIL_USE_TLStrueSTARTTLS. Set to false and use port 465 for SMTPS.
EMAIL_HOST_USER / EMAIL_HOST_PASSWORDAuth.
DEFAULT_FROM_EMAILFrom: header on outbound mail.
NOTIFICATION_EMAIL_ENABLEDtrueSecondary switch — turns off only notification emails, leaves transactional (password reset, verification) on.

LDAP

KeyDefaultPurpose
LDAP_ENABLEDfalseMaster switch. Everything below is only read when this is true.
LDAP_SERVER_URIldap://host or ldaps://host.
LDAP_BIND_DN / LDAP_BIND_PASSWORDService account for directory lookups. Read-only access is enough.
LDAP_USER_DNSearch base for user lookups.
LDAP_GROUP_DNSearch base for group lookups.

See LDAP integration for the full attribute map and mirroring behavior.

AWX integration

KeyDefaultPurpose
AWX_WEBHOOK_SECRETShared secret AWX signs webhook callbacks with. Must match what you configure in AWX.
AWX_DEFAULT_EXECUTION_MODEbulkDefault execution mode for new automation templates.
AWX_JOB_SYNC_INTERVAL10Seconds between job status polls (fallback if webhook is slow).
AWX_USER_MAPPINGfalseMap Fabrik users to AWX users via matching username.

Platform

KeyDefaultPurpose
FABRIK_VERSION1.0.0Shown in UI footer and /api/health/. Don't change unless you're forking.
FABRIK_BASE_URLExternal URL Fabrik is reachable at. Used in email links. Usually matches SITE_URL.

Rate limiting

KeyDefaultPurpose
RATELIMIT_ENABLEtrueMaster switch for Django-level rate limits. nginx has its own limits on top.

Individual endpoint throttles live in REST_FRAMEWORK['DEFAULT_THROTTLE_RATES'] in backend/fabrik/settings.py. Changing them requires editing the file and restarting the backend — they aren't .env keys.

Final sanity check

Before docker compose up:

  1. No value is still change-me or your-*-here.
  2. DATABASE_URL and RABBITMQ_URL components match their individual fields.
  3. ALLOWED_HOSTS contains the hostname you'll actually reach Fabrik at.
  4. CORS_ALLOWED_ORIGINS uses https:// — not http://.
  5. DEBUG=false.