Skip to main content

Environment variable reference

Every Norn setting is an environment variable with the NORN_ prefix and uppercase nested keys, so postgres.dsn is NORN_POSTGRES_DSN and source_control.github_app_id is NORN_SOURCE_CONTROL_GITHUB_APP_ID.

Environment variables take precedence over an optional configuration file, which takes precedence over the defaults below. Durations are written the way Go writes them: 30s, 15m, 720h. Lists are comma-separated. Sizes are a plain number of bytes.

Nothing here is required in the sense of having no default, but a default is not always usable: the shipped values describe a development machine on loopback addresses. Start from required settings, and check settings that must agree before changing anything security-related.

Deployment methods own some of these on your behalf — see the Helm mapping or the Compose mapping.

Application

SettingDefaultPurpose
NORN_APP_NAMEnornService name attached to every log line
NORN_APP_ENVdevelopmentEnvironment name attached to every log line
NORN_APP_VERSIONdevReported by the health endpoint; the official images set it
NORN_APP_BASE_URLhttp://localhost:5173Public browser origin, such as https://norn.example.com
NORN_APP_LOG_LEVELinfoOne of debug, info, warn, error

Instance

SettingDefaultPurpose
NORN_INSTANCE_SIGNUPS_OPENtrueWhether anyone may create an account
NORN_INSTANCE_PASSWORD_AUTHtrueWhether password sign-in is offered
NORN_INSTANCE_SELF_HOSTEDfalseMarks the instance as self-hosted in the dashboard

HTTP server

SettingDefaultPurpose
NORN_HTTP_ADDR127.0.0.1:8080Listen address; a container needs 0.0.0.0:8080
NORN_HTTP_READ_HEADER_TIMEOUT5sTime allowed to receive request headers
NORN_HTTP_READ_TIMEOUT30sTime allowed to receive a request
NORN_HTTP_WRITE_TIMEOUT30sTime allowed to write a response
NORN_HTTP_IDLE_TIMEOUT120sKeep-alive idle time
NORN_HTTP_REQUEST_TIMEOUT25sPer-request budget for dashboard, SCIM, MCP, and SSO routes
NORN_HTTP_SHUTDOWN_TIMEOUT20sTime allowed to drain in-flight requests
NORN_HTTP_MAX_REQUEST_BYTES4194304Request body cap on dashboard routes
NORN_HTTP_CLIENT_IP_HEADERemptyForwarded client-address header, such as X-Forwarded-For
NORN_HTTP_TRUSTED_PROXIESemptyAddresses or CIDRs whose forwarded header is believed

Attachment transfers and inbound forge deliveries carry their own limits and are not bound by NORN_HTTP_MAX_REQUEST_BYTES.

Sessions

SettingDefaultPurpose
NORN_SESSION_COOKIE_NAMEnorn_sessionCookie name stem; each signed-in account gets its own slot
NORN_SESSION_COOKIE_PATH/Cookie path; must stay /
NORN_SESSION_DOMAINemptyCookie domain; must stay empty
NORN_SESSION_SECUREfalseMarks cookies secure and adds the __Host- prefix
NORN_SESSION_SAME_SITElaxlax or strict; none is rejected
NORN_SESSION_KEY_PREFIXsession:Valkey key prefix for stored sessions
NORN_SESSION_IDLE_TIMEOUT168hInactivity after which a session ends
NORN_SESSION_ABSOLUTE_LIFETIME720hMaximum session age regardless of use
NORN_SESSION_REFRESH_INTERVAL1mHow often an active session is extended
NORN_SESSION_MAX_PER_ACCOUNT20Concurrent sessions kept per account

Keeping the path / and the domain empty is what makes the __Host- cookie prefix valid. Changing either drops the cookie in the browser.

PostgreSQL

SettingDefaultPurpose
NORN_POSTGRES_DSNpostgres://norn:[email protected]:5433/norn?sslmode=disableConnection string; TLS is expressed inside it
NORN_POSTGRES_MAX_CONNS10Pool ceiling
NORN_POSTGRES_MIN_CONNS1Pool floor
NORN_POSTGRES_MAX_CONN_LIFETIME1hMaximum age of a pooled connection
NORN_POSTGRES_MAX_CONN_IDLE_TIME30mIdle time before a connection is closed
NORN_POSTGRES_CONNECT_TIMEOUT10sTime allowed to connect at startup

A password inside the connection string must be URL-safe or percent-encoded.

Valkey

Sessions, rate limits, single sign-on state, and authorisation policy synchronisation.

SettingDefaultPurpose
NORN_VALKEY_ADDR127.0.0.1:6381Host and port
NORN_VALKEY_USERNAMEemptyACL user
NORN_VALKEY_PASSWORDemptyACL password
NORN_VALKEY_DB0Database index
NORN_VALKEY_POOL_SIZE10Connection pool size
NORN_VALKEY_DIAL_TIMEOUT5sTime allowed to connect at startup
NORN_VALKEY_READ_TIMEOUT3sPer-command read timeout
NORN_VALKEY_WRITE_TIMEOUT3sPer-command write timeout

Norn connects without TLS, so Valkey must be on a trusted network. Eviction must be disabled: this is durable state, not a disposable cache.

Job queue

The same Valkey server, on a different database index.

SettingDefaultPurpose
NORN_ASYNQ_ADDR127.0.0.1:6381Host and port
NORN_ASYNQ_USERNAMEemptyACL user
NORN_ASYNQ_PASSWORDemptyACL password
NORN_ASYNQ_DB1Database index; kept apart from sessions
NORN_ASYNQ_CONCURRENCY10Jobs processed at once by one worker
NORN_ASYNQ_SHUTDOWN_TIMEOUT20sTime allowed to finish running jobs
NORN_ASYNQ_MAX_RETRY5Attempts before a job is archived

Queue priorities are a map rather than a scalar and cannot be expressed as an environment variable. Change them through a configuration file if you need to.

Worker

SettingDefaultPurpose
NORN_WORKER_HEALTH_ADDR127.0.0.1:8090Address serving /healthz and /readyz
NORN_WORKER_SHUTDOWN_TIMEOUT10sTime allowed to stop cleanly

Exactly one worker runs per instance. It holds the job scheduler without leader election, so a second worker double-fires every scheduled job.

Email

SettingDefaultPurpose
NORN_SMTP_HOSTemptySMTP host; empty disables mail
NORN_SMTP_PORT1025SMTP port
NORN_SMTP_USERNAMEemptySMTP user
NORN_SMTP_PASSWORDemptySMTP password
NORN_SMTP_AUTH_TYPEnonenone, plain, login, or cram-md5
NORN_SMTP_TLS_POLICYnonenone, opportunistic, or mandatory
NORN_SMTP_FROM_ADDRESSemptySender address, on a domain the provider has verified
NORN_SMTP_FROM_NAMENornSender display name
NORN_SMTP_TIMEOUT15sTime allowed per delivery

While mail is disabled, sign-up and invitation links are shown on screen instead of sent.

Storage

SettingDefaultPurpose
NORN_STORAGE_BACKENDfilesystemfilesystem or s3
NORN_STORAGE_ROOT./data/blobsDirectory holding attachments, with filesystem
NORN_STORAGE_ENDPOINThttp://127.0.0.1:3900S3 endpoint, which the browser must be able to reach
NORN_STORAGE_REGIONgarageS3 signing region
NORN_STORAGE_BUCKETnorn-localBucket name; it must already exist
NORN_STORAGE_ACCESS_KEY_IDemptyS3 access key
NORN_STORAGE_SECRET_ACCESS_KEYemptyS3 secret key
NORN_STORAGE_USE_PATH_STYLEtruePath-style addressing; false for virtual-hosted buckets
NORN_STORAGE_PUBLIC_BASE_URLemptyPublic object URL when it differs from the endpoint

With filesystem, attachment bytes travel through the API and no bucket, credentials, or CORS policy are involved. With s3, the browser transfers them directly, so the endpoint is part of the public surface.

Attachments

SettingDefaultPurpose
NORN_ATTACHMENTS_MAX_FILE_BYTES26214400Largest single file
NORN_ATTACHMENTS_MAX_WORKSPACE_BYTES0Per-workspace quota; 0 is unlimited
NORN_ATTACHMENTS_UPLOAD_TTL15mLifetime of an upload authorisation
NORN_ATTACHMENTS_LINK_TTL5mLifetime of a download link
NORN_ATTACHMENTS_TRANSFER_TIMEOUT10mTime allowed for one transfer
NORN_ATTACHMENTS_RECLAIM_SCHEDULE*/5 * * * *When abandoned uploads are swept
NORN_ATTACHMENTS_RECLAIM_BATCH200Uploads swept per run

Realtime and notifications

SettingDefaultPurpose
NORN_REALTIME_ENABLEDtrueWhether the event stream is served
NORN_REALTIME_MAX_PER_ACCOUNT12Concurrent streams per account, per API process
NORN_NOTIFICATIONS_FANOUT_SCHEDULE* * * * *When notifications are delivered
NORN_NOTIFICATIONS_DIGEST_SCHEDULE*/15 * * * *When digests are sent

Passwords

SettingDefaultPurpose
NORN_PASSWORD_BREACH_CHECK_ENABLEDtrueRefuse passwords found in public breach corpora
NORN_PASSWORD_BREACH_CHECK_ENDPOINThttps://api.pwnedpasswords.com/rangeRange API used for the check
NORN_PASSWORD_BREACH_CHECK_TIMEOUT5sTime allowed for the check

The check sends the first five characters of a password hash and nothing else. It needs outbound internet access; turn it off on an isolated network.

Security and authorisation

SettingDefaultPurpose
NORN_SECURITY_ENCRYPTION_KEYempty32 bytes of base64 encrypting stored credentials
NORN_CASBIN_TABLE_NAMEcasbin_ruleTable holding the authorisation policy
NORN_CASBIN_WATCHER_CHANNELcasbin:policyValkey channel announcing policy changes

Without an encryption key, Norn starts but refuses to store identity provider secrets, import credentials, source-control tokens, and webhook signing keys.

Single sign-on

SettingDefaultPurpose
NORN_OIDC_REQUEST_TIMEOUT10sTime allowed per discovery or token request
NORN_OIDC_MAX_RESPONSE_SIZE1048576Largest accepted provider response
NORN_OIDC_STATE_TTL10mLifetime of an in-flight sign-in
NORN_SAML_REQUEST_TIMEOUT10sTime allowed to fetch provider metadata
NORN_SAML_MAX_RESPONSE_SIZE1048576Largest accepted metadata document
NORN_SAML_STATE_TTL10mLifetime of an in-flight sign-in
NORN_SAML_REPLAY_TTL30mHow long a consumed assertion is remembered
NORN_SAML_MAX_CLOCK_SKEW3mClock difference tolerated in an assertion
NORN_SAML_MAX_ISSUE_DELAY90sAge at which an assertion is too old to accept
NORN_SAML_CERTIFICATE_SWEEP_SCHEDULE0 8 * * *When expiring certificates are reported

MCP

SettingDefaultPurpose
NORN_MCP_ENABLEDfalseWhether /mcp is served
NORN_MCP_REQUESTS_PER_WINDOW300Requests allowed per token per window
NORN_MCP_RATE_WINDOW1mLength of that window
NORN_API_TOKENS_EXPIRY_SWEEP_SCHEDULE0 9 * * *When expiring tokens are reported

Webhooks

SettingDefaultPurpose
NORN_WEBHOOKS_FAN_OUT_SCHEDULE* * * * *When pending deliveries are dispatched
NORN_WEBHOOKS_FAN_OUT_BATCH200Deliveries dispatched per run
NORN_WEBHOOKS_REQUEST_TIMEOUT10sTime allowed per delivery
NORN_WEBHOOKS_DIAL_TIMEOUT5sTime allowed to connect to a receiver
NORN_WEBHOOKS_MAX_RESPONSE_SIZE65536Response bytes read from a receiver
NORN_WEBHOOKS_SECRET_GRACE24hHow long a rotated signing secret stays valid
NORN_WEBHOOKS_RETENTION720hHow long delivery records are kept
NORN_WEBHOOKS_SWEEP_SCHEDULE0 5 * * *When old delivery records are removed
NORN_WEBHOOKS_SWEEP_BATCH5000Records removed per run
NORN_WEBHOOKS_ALLOWED_DESTINATIONSemptyCIDRs exempt from the private-address guard

Norn refuses to deliver to private addresses. Each allowed destination opens a hole in that guard, so list one only when a receiver genuinely sits on an internal network.

Imports

SettingDefaultPurpose
NORN_IMPORTS_CHUNK_SIZE25Records written per transaction
NORN_IMPORTS_PAGE_SIZE200Records read per source request
NORN_IMPORTS_SLICE_BUDGET45sWork done before a slice yields
NORN_IMPORTS_LEASE_TTL2mLease held on a slice; must exceed the budget
NORN_IMPORTS_RESCUE_SCHEDULE* * * * *When abandoned slices are picked up
NORN_IMPORTS_RESCUE_BATCH50Slices rescued per run
NORN_IMPORTS_MAX_ATTEMPTS20Attempts before a slice fails
NORN_IMPORTS_MIN_BACKOFF1sShortest retry delay
NORN_IMPORTS_MAX_BACKOFF15mLongest retry delay
NORN_IMPORTS_RECORD_RETENTION720hHow long import ledgers are kept
NORN_IMPORTS_MAX_ATTACHMENT_BYTES26214400Largest attachment an import may carry
NORN_IMPORTS_MAX_UPLOAD_BYTES4194304Largest file an import may be handed
NORN_LINEAR_ENDPOINThttps://api.linear.app/graphqlWhere a Linear import reads from
NORN_LINEAR_REQUEST_TIMEOUT30sTime allowed per request
NORN_LINEAR_MAX_RESPONSE_SIZE33554432Largest accepted response
NORN_LINEAR_PAGE_SIZE100Issues read per request, at most 250

Source control

SettingDefaultPurpose
NORN_SOURCE_CONTROL_GITHUB_APP_IDemptyGitHub App identifier
NORN_SOURCE_CONTROL_GITHUB_APP_SLUGemptyGitHub App URL slug
NORN_SOURCE_CONTROL_GITHUB_APP_CLIENT_IDemptyGitHub App client identifier
NORN_SOURCE_CONTROL_GITHUB_APP_CLIENT_SECRETemptyGitHub App client secret
NORN_SOURCE_CONTROL_GITHUB_APP_PRIVATE_KEYemptyGitHub App private key, in PEM form
NORN_SOURCE_CONTROL_GITHUB_APP_WEBHOOK_SECRETemptySecret signing inbound deliveries
NORN_SOURCE_CONTROL_GITHUB_ENDPOINThttps://api.github.comGitHub API base
NORN_SOURCE_CONTROL_GITLAB_ENDPOINThttps://gitlab.comGitLab base
NORN_SOURCE_CONTROL_APP_STATE_TTL10mLifetime of an in-flight app installation
NORN_SOURCE_CONTROL_REQUEST_TIMEOUT30sTime allowed per forge request
NORN_SOURCE_CONTROL_DIAL_TIMEOUT5sTime allowed to connect to a forge
NORN_SOURCE_CONTROL_MAX_RESPONSE_SIZE8388608Largest accepted forge response
NORN_SOURCE_CONTROL_MAX_DELIVERY_BYTES4194304Largest accepted inbound delivery
NORN_SOURCE_CONTROL_PAGE_SIZE100Records read per forge request
NORN_SOURCE_CONTROL_RECONCILE_SCHEDULE*/5 * * * *When repositories are reconciled
NORN_SOURCE_CONTROL_RECONCILE_BATCH20Repositories reconciled per run
NORN_SOURCE_CONTROL_CALLS_PER_CYCLE30Forge calls allowed per run
NORN_SOURCE_CONTROL_MAX_CATCH_UP168hHow far back a reconcile will read
NORN_SOURCE_CONTROL_MAX_ATTEMPTS5Attempts before a delivery fails
NORN_SOURCE_CONTROL_MIN_BACKOFF30sShortest retry delay
NORN_SOURCE_CONTROL_MAX_BACKOFF1hLongest retry delay
NORN_SOURCE_CONTROL_DELIVERY_RETENTION720hHow long delivery records are kept
NORN_SOURCE_CONTROL_ALLOWED_DESTINATIONSemptyCIDRs exempt from the private-address guard

Registering the GitHub App is covered in Configure the instance GitHub App.

Audit, checks, and retention

SettingDefaultPurpose
NORN_AUDIT_RETENTION8760hHow long audit records are kept
NORN_AUDIT_SWEEP_SCHEDULE0 4 * * *When expired audit records are removed
NORN_AUDIT_SWEEP_BATCH5000Records removed per run
NORN_CHECKS_EXPIRY_SWEEP_SCHEDULE17 * * * *When expiring checks are reported
NORN_CHECKS_SWEEP_BATCH500Checks examined per run
NORN_WORKSPACE_DELETION_GRACE_PERIOD720hHow long a deleted workspace can be restored
NORN_CYCLES_GENERATION_SCHEDULE5 0 * * *When upcoming cycles are created

Licence and geolocation

SettingDefaultPurpose
NORN_LICENCE_KEYemptyLicence enabling audit logs and directory sync
NORN_LICENCE_GRACE720hHow long features keep working after expiry
NORN_GEOIP_DATABASE_PATHemptyMaxMind database naming session locations

A geolocation database path that cannot be read stops the process from starting.

Settings that must agree

Norn validates its whole configuration before opening a listener and names the setting it rejected. These are the rules that involve more than one value:

RuleWhy
NORN_SESSION_SECURE must be true when NORN_APP_BASE_URL is httpsotherwise the session cookie travels over any plain-http request to the same host
NORN_SMTP_HOST and NORN_SMTP_FROM_ADDRESS are set together or neitherhalf-configured mail fails at the moment somebody needs a password reset
NORN_HTTP_CLIENT_IP_HEADER requires NORN_HTTP_TRUSTED_PROXIESwithout an allow-list anyone could forge the header and evade the sign-in throttle
The storage host must differ from the NORN_APP_BASE_URL host, with s3uploaded files would be served by an origin that holds the session cookie
NORN_IMPORTS_MAX_UPLOAD_BYTES must not exceed NORN_HTTP_MAX_REQUEST_BYTESthe promise would be broken by a limit that does not name itself
NORN_SOURCE_CONTROL_MAX_DELIVERY_BYTES must not exceed NORN_HTTP_MAX_REQUEST_BYTESthe same, for inbound forge deliveries
NORN_SESSION_REFRESH_INTERVAL must be shorter than NORN_SESSION_IDLE_TIMEOUTa session that refreshes less often than it expires can never stay alive
NORN_SESSION_IDLE_TIMEOUT must not exceed NORN_SESSION_ABSOLUTE_LIFETIMEthe shorter limit would never apply
NORN_IMPORTS_SLICE_BUDGET must be shorter than NORN_IMPORTS_LEASE_TTLa slice that outlives its lease is rescued while it is still running
NORN_ATTACHMENTS_MAX_WORKSPACE_BYTES, when set, must not be below NORN_ATTACHMENTS_MAX_FILE_BYTESno file could ever be stored