Skip to content

Settings Reference

Django-Bolt settings are configured in your Django settings.py file.

Bolt reads its settings at startup. A missing setting gives its default. A setting with the wrong type stops startup with ImproperlyConfigured. The error names the setting:

  • A size or a count must be an int, such as 10 * 1024 * 1024 or FileSize.MB_10. A str such as "10485760" is not an int. A bool is not an int.
  • A flag such as CORS_ALLOW_CREDENTIALS must be a bool.
  • DEBUG must be a bool or an int, as Django reads it: DEBUG = 1 is on, and DEBUG = 0 is off. A str such as "False" stops startup, because Django reads a str that is not empty as on.
  • A list setting such as CORS_ALLOWED_ORIGINS must be a list of str. One str is not a list.

Convert a value that you read from an environment variable:

import os

BOLT_MAX_UPLOAD_SIZE = int(os.environ["BOLT_MAX_UPLOAD_SIZE"])

CORS settings

CORS_ALLOWED_ORIGINS

List of allowed origins for CORS.

CORS_ALLOWED_ORIGINS = [
    "https://example.com",
    "https://app.example.com",
]

CORS_ALLOWED_ORIGIN_REGEXES

List of regular expressions for allowed origins. An origin is allowed when it is in CORS_ALLOWED_ORIGINS or when a pattern matches it. A pattern can match a part of the origin, so start each pattern with ^ and end it with $. Bolt ignores an invalid pattern and writes a warning at startup. A route with @cors uses its own origins and patterns in place of CORS_ALLOWED_ORIGINS and these patterns.

CORS_ALLOWED_ORIGIN_REGEXES = [
    r"^https://\w+\.example\.com$",
]

CORS_ALLOW_ALL_ORIGINS

Allow all origins (development only).

CORS_ALLOW_ALL_ORIGINS = True

CORS_ALLOW_CREDENTIALS

Allow credentials in CORS requests.

CORS_ALLOW_CREDENTIALS = True

CORS_ALLOW_METHODS

Allowed HTTP methods for CORS.

CORS_ALLOW_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"]

CORS_ALLOW_HEADERS

Allowed headers in CORS requests. The default is ["Content-Type", "Authorization"].

CORS_ALLOW_HEADERS = ["Content-Type", "Authorization", "X-Requested-With"]

CORS_EXPOSE_HEADERS

Headers exposed to the browser.

CORS_EXPOSE_HEADERS = ["X-Total-Count", "X-Page-Count"]

CORS_PREFLIGHT_MAX_AGE

Preflight cache duration in seconds. The default is 3600.

CORS_PREFLIGHT_MAX_AGE = 86400  # 24 hours

Rate limiting settings

BOLT_TRUSTED_PROXIES

Proxies that sit in front of Bolt, as addresses or CIDR blocks. The list is empty by default.

BOLT_TRUSTED_PROXIES = ["10.0.0.0/8"]

Bolt uses this list to find the canonical client address for @rate_limit(key="ip") and request.META["REMOTE_ADDR"]:

  • The list is empty. Bolt keys on the peer address of the connection and ignores every forwarding header.
  • The peer is not in the list. Bolt keys on the peer address. That peer is not a declared proxy, so its headers prove nothing.
  • The peer is in the list. Bolt reads X-Forwarded-For from the right and takes the first entry that is not a listed proxy. When every entry is a listed proxy, the client is inside your network and Bolt takes the leftmost entry. Without the header, Bolt falls back to the last X-Real-IP line when it is a valid address, then the peer address. A malformed chain also falls back to the peer.

Bolt uses the same list for the request scheme. Only a peer in the list can set the scheme with X-Forwarded-Proto. Bolt reads the rightmost value, because the proxy closest to Bolt adds it. A value other than http or https is ignored. From any other peer, the scheme is the scheme of the connection. Bolt does not end TLS, so that scheme is http. The scheme sets these values:

  • request.build_absolute_uri() and the default request.META["SERVER_PORT"] on Bolt routes.
  • The scheme key, and the port of the server key, of the ASGI scope of mount_asgi() and mount_django(). Django reads scope["scheme"] for request.is_secure().

Django's SECURE_PROXY_SSL_HEADER still applies on top of this scheme, in Django code and in request.build_absolute_uri(), as in Django. Django middleware on a Bolt route reads the scheme only from SECURE_PROXY_SSL_HEADER, as before. That setting trusts the header from every peer, so set it only when the proxy removes the header from client requests.

The origin check of cookie authentication reads X-Forwarded-Proto from every peer. That check passes only one origin, so a value that a client sends never makes it weaker than the scheme of the connection. See Cross-site request forgery protection.

Both an address and a block are accepted. 127.0.0.1 matches one address. 10.0.0.0/8 matches the block. An invalid entry raises ImproperlyConfigured at startup.

Set this when you run behind a proxy

A client sends X-Forwarded-For itself, and a proxy appends to it. Without this setting Bolt ignores the header, so every caller behind the proxy shares one bucket. With the setting, Bolt separates them again and a client cannot pick its own bucket. Behind a proxy that ends TLS, Bolt also needs this setting to see https.

BOLT_RATE_LIMIT_IPV6_PREFIX

The prefix length of the network that a rate limit counts an IPv6 client on. The value is an int from 0 to 128. The default is 64.

BOLT_RATE_LIMIT_IPV6_PREFIX = 64

A provider usually gives one client a /64 network or more. With a key of the full address, that client could send each request from a new address and get a new bucket. Thus @rate_limit(key="ip") counts an IPv6 client per /64 network. The address fallback of key="user" and key="api_key" does the same. An IPv4 client is counted per address.

Set 128 to count each IPv6 address. Do this when clients that are not related share one /64, for example virtual machines of a cloud provider. Set a smaller value, such as 56 or 48, to count a larger network as one client. The 429 log line names the network, for example 2001:db8:1:2::/64. request.META["REMOTE_ADDR"] keeps the full address. An invalid value raises ImproperlyConfigured at startup.

File upload settings

BOLT_MAX_UPLOAD_SIZE

Maximum file upload size in bytes. Requests exceeding this limit will be rejected with a 413 error before any per-view validation occurs.

BOLT_MAX_UPLOAD_SIZE = 10 * 1024 * 1024  # 10 MB

You can also use the FileSize enum for readability:

from django_bolt import FileSize

BOLT_MAX_UPLOAD_SIZE = FileSize.MB_10

Global vs Per-View Limits

BOLT_MAX_UPLOAD_SIZE is a global server limit. You cannot override it in individual views. If a file upload exceeds this value, the request is rejected before your view handler runs.

The max_size argument in the File() parameter is for per-view validation. It allows you to set a stricter limit for a specific endpoint, but it can never increase the global limit. For example:

# settings.py
BOLT_MAX_UPLOAD_SIZE = FileSize.MB_50  # 50 MB global limit

# In your view
file: Annotated[UploadFile, File(max_size=FileSize.MB_1)]  # 1 MB per-view limit
In this case, files over 1 MB are rejected by the view, and files over 50 MB are rejected by the server.

If you set File(max_size=...) higher than BOLT_MAX_UPLOAD_SIZE, the global limit always takes precedence.

This distinction is important for security and resource management. Always set BOLT_MAX_UPLOAD_SIZE to the maximum file size your server should ever accept, and use File(max_size=...) for endpoint-specific needs.

Native route settings

BOLT_NATIVE_ROUTE_TIMEOUT

Maximum time (seconds) to wait for a native route handler to complete a request. At the limit, Bolt sends 504 Gateway Timeout.

BOLT_NATIVE_ROUTE_TIMEOUT = 30

Default: 30

The value must be more than 0. An int or a float is valid.

ASGI mount settings

BOLT_ASGI_MOUNT_TIMEOUT

Maximum time (seconds) to wait for a mounted ASGI app to complete.

BOLT_ASGI_MOUNT_TIMEOUT = 30

Default: 30

The value must be more than 0. An int or a float is valid.

When exceeded, Django-Bolt returns 504 Gateway Timeout for the mounted request.

BOLT_MEMORY_SPOOL_THRESHOLD

Size threshold before file uploads are spooled to disk. Files smaller than this are kept in memory; larger files are written to a temporary file on disk.

BOLT_MEMORY_SPOOL_THRESHOLD = 5 * 1024 * 1024  # 5 MB

Default: 1048576 (1 MB)

This setting controls memory usage during file uploads:

  • Lower values reduce memory usage but increase disk I/O
  • Higher values improve performance for medium-sized files but use more memory

When to adjust

  • Set higher (e.g., 5-10 MB) if you frequently receive medium-sized files and have sufficient memory
  • Set lower (e.g., 256 KB) on memory-constrained systems or when handling many concurrent uploads

Runtime environment variables

Bolt reads its DJANGO_BOLT_* environment variables at startup. An unset or empty variable gives the default. A value that is not valid stops startup with ImproperlyConfigured. The error names the variable and the value, for example DJANGO_BOLT_ORM_THREADS must be an int of 1 or more, got '0'.

DJANGO_BOLT_MAX_PARAM_LENGTH

Maximum allowed size for path/query/form parameter values, in bytes. Requests that exceed this limit are rejected with HTTP 422.

export DJANGO_BOLT_MAX_PARAM_LENGTH=65536

Default: 8192

Maximum: 1048576 (1 MB). A larger value stops startup, so a misconfiguration can never disable the limit.

The value must be an int from 1 to 1048576.

DJANGO_BOLT_ORM_THREADS

Number of threads in the ORM pool of each process. Django-Bolt evaluates the QuerySets that async handlers return on this pool. await request.auser() also loads the user on this pool. Django's async ORM API in async handlers without Django middleware, such as await Model.objects.aget(), acount() and async for, runs on this pool too. A route with Django middleware runs these calls on its request lane instead. The pool size is the number of queries in flight per process.

export DJANGO_BOLT_ORM_THREADS=8

Default: 1 when every database is SQLite, otherwise 4.

Without a connection pool, each thread holds one database connection. With a pool, a thread holds a connection only while it has work. Increase the value when one free-threaded process runs many worker threads. See Size the database thread pools. The value must be an int of 1 or more.

DJANGO_BOLT_LANE_IDLE_SECONDS

Idle time, in seconds, after which a request lane closes its database connections and stops. Routes with Django middleware use request lanes. See Request lanes.

export DJANGO_BOLT_LANE_IDLE_SECONDS=30

Default: 10. A fraction such as 0.5 is valid. The value must be a number more than 0.

DJANGO_BOLT_EXECUTOR_THREADS

Number of threads in the shared executor pool of each process. Blocking sync handlers and sync_to_thread calls run on this pool.

export DJANGO_BOLT_EXECUTOR_THREADS=16

Default: CPU count + 4, maximum 32.

The value must be an int of 1 or more.

File serving settings

BOLT_ALLOWED_FILE_PATHS

Whitelist of directories for FileResponse.

BOLT_ALLOWED_FILE_PATHS = [
    "/var/app/uploads",
    "/var/app/public",
]

The setting is a list or a tuple of absolute paths. Each path is a str or a Path. One str is not a list, so it stops startup with ImproperlyConfigured. runbolt reads the setting at startup. Under TestClient, the first FileResponse reads it. override_settings changes the limit for the next FileResponse. A relative path, such as "" or "media", also stops startup. None or an empty list sets no limit.

When set, FileResponse only serves files within these directories. For a path outside them, it raises ResponseFileNotAllowed, and Bolt answers 403. When the lookup of a path fails, Bolt follows its links, as realpath does, to the point where the lookup stops. Nothing exists below that point, so Bolt completes the path from there as text. When that path is outside these directories, Bolt answers 403, also when the path names no file. Thus a client cannot learn which regular files exist there, on Linux, macOS and Windows. A path can leave these directories with .. and come back into them. Such a path can still show whether a directory, or a link into these directories, exists outside them.

Static and media file serving

Django-Bolt serves static and media files from Rust based on your standard Django settings — STATIC_URL / STATIC_ROOT / STATICFILES_DIRS for static, and MEDIA_URL / MEDIA_ROOT for media. The scopes mount automatically when those settings are configured.

See Static Files and Media Files for full documentation.

BOLT_STATIC_MAX_AGE

Cache-Control max-age (seconds) for successful static responses. Emits Cache-Control: public, max-age=N (static assets are identical for every user, so shared caches can cache them).

BOLT_STATIC_MAX_AGE = 31536000  # 1 year — safe for content-hashed assets

Default: unset (no Cache-Control header)

BOLT_MEDIA_MAX_AGE

Cache-Control max-age (seconds) for successful media responses. Emits Cache-Control: private, max-age=N — private so shared caches don't serve one user's upload to another.

BOLT_MEDIA_MAX_AGE = 3600  # 1 hour

Default: unset (no Cache-Control header)

Validation

Both settings only emit a header on 2xx responses. Missing, non-integer, boolean, or negative values are ignored with a startup warning. STATIC_URL / MEDIA_URL must be literal prefixes — values containing {/} are refused at startup.

Authentication settings

Django-Bolt uses Django's SECRET_KEY for JWT signing by default.

SECRET_KEY = "your-secret-key"

Override per-backend:

JWTAuthentication(secret="custom-jwt-secret")

BOLT_AUTHENTICATION_CLASSES

Default authentication backends applied to all endpoints.

Default: [] (no authentication)

BOLT_DEFAULT_PERMISSION_CLASSES

Default permission guards applied to all endpoints.

Default: [AllowAny()] (all endpoints publicly accessible)

See Global authentication for usage and examples.

Logging settings

Django-Bolt integrates with Django's logging system.

LOGGING = {
    "version": 1,
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
        },
    },
    "loggers": {
        "django_bolt": {
            "handlers": ["console"],
            "level": "INFO",
        },
    },
}

BOLT_LOG_QUEUE_SIZE

Django-Bolt logs through a bounded, non-blocking queue (a background thread forwards records to the console). This sets the queue's maximum depth. When the queue is full — e.g. during an extreme burst of error logging — new records are dropped rather than accumulating in memory, preventing unbounded RAM growth under load. Drops are counted and surfaced with a throttled notice to stderr.

BOLT_LOG_QUEUE_SIZE = 10000

Default: 10000

Django signals settings

BOLT_EMIT_SIGNALS

Enable Django request_started and request_finished signal emission.

BOLT_EMIT_SIGNALS = True

Default: False

Django-Bolt disables signals by default for maximum performance. Enable this setting when:

  • Using third-party packages that depend on request signals (e.g. django-debug-toolbar)
  • Implementing custom signal receivers for request lifecycle events

CONN_MAX_AGE and CONN_HEALTH_CHECKS do not need signals. Bolt runs Django's connection check on the executor thread before each call when a database sets a positive CONN_MAX_AGE or CONN_HEALTH_CHECKS. With neither set, Bolt runs the check only after a call raises. A connection that the database closed then fails one request. The next request reconnects, and the other threads reconnect before their next call. See Database Connection Management.

See Django Signals for detailed documentation.

Dev reload settings

runbolt --dev watches two things and restarts the server when a .py or .html file changes in either:

  • The project root (BASE_DIR, falling back to the working directory), recursively — so newly created files, modules, and packages reload without restarting the dev server. Virtualenvs and common build/cache directories (.git, __pycache__, node_modules, target, …) are excluded.
  • The app's import graph — every module loaded by Django setup plus your discovered API modules and everything they transitively import, along with template directories and manage.py. This covers code living outside the project root, such as apps on PYTHONPATH or editable-installed packages.

To watch extra locations outside both — for example a shared library next to the project that isn't imported at startup — pass --reload-dir (repeatable, same shape as uvicorn's flag):

python manage.py runbolt --dev --reload-dir ../shared-lib

Each entry is a directory (watched recursively) or a single file, resolved relative to the working directory. Entries that don't exist are skipped with a warning.

BOLT_DEV_FORCE_POLLING

Force the --dev auto-reloader to use file-system polling instead of native OS file events.

BOLT_DEV_FORCE_POLLING = True

Default: False

Django-Bolt's dev reloader uses the notify crate's recommended watcher (inotify on Linux, FSEvents on macOS, ReadDirectoryChangesW on Windows). These backends are fast but rely on kernel events, which do not propagate across some bind-mount boundaries — most notably a Linux container running on a Windows host where the source tree is mounted from the host filesystem.

Enable polling when:

  • Running runbolt --dev inside a Docker container on a Windows host with the project bind-mounted from the host
  • Working on a remote / network filesystem that does not deliver native change events
  • Native file events appear to fire inconsistently for your editor or sync tool

Polling is checked every 500 ms. It uses more CPU than native events on large source trees but is the only reliable option when kernel events are unavailable.

This setting only affects runbolt --dev; production servers do not watch files.

runbolt command options

The runbolt management command accepts these options:

Option Default Description
--host 0.0.0.0 Bind address: an IP address, an IPv6 address in brackets, or a host name. A host name binds its first IPv4 address, so localhost binds 127.0.0.1. A host name that does not resolve stops startup.
--port 8000 Bind port
--workers 1, or the CPU count on free-threaded Python Actix worker threads per process. Each thread runs Python handlers.
--processes 1 Number of processes
--dev off Enable auto-reload
--no-admin off Disable admin integration
--backlog 1024 Socket listen backlog
--keep-alive OS default HTTP keep-alive timeout
--skip-checks off Skip the Django system checks at startup

At startup, runbolt runs the Django system checks and the migration check, like runserver. A check error stops startup. The unapplied-migration warning prints before the banner. In --dev mode, each reload runs the checks again. Multi-process mode runs them once, in the parent process. --skip-checks skips the system checks; the migration check still runs.

Examples

# Development with auto-reload
python manage.py runbolt --dev

# Production with scaling
python manage.py runbolt --processes 4 --workers 2

# Custom bind address
python manage.py runbolt --host 127.0.0.1 --port 3000

The dev reloader follows a Django-style source watch list: Python modules and HTML templates are reloaded automatically, while runtime files like logs, databases, and other generated artifacts do not trigger reloads.

OpenAPI settings

Configure via OpenAPIConfig in your api.py:

from django_bolt import BoltAPI
from django_bolt.openapi import OpenAPIConfig

api = BoltAPI(
    openapi_config=OpenAPIConfig(
        title="My API",
        version="1.0.0",
        description="API description",
        enabled=True,
        path="/docs",
    )
)

Content Security Policy (CSP) settings

Django-Bolt applies CSP headers to static files served by the Rust server using Django 6.0+'s native SECURE_CSP setting.

SECURE_CSP

from django.utils.csp import CSP

SECURE_CSP = {
    "default-src": [CSP.SELF],
    "script-src": [CSP.SELF],
    "style-src": [CSP.SELF, CSP.UNSAFE_INLINE],
    "img-src": [CSP.SELF, "data:"],
}

See Static Files - Content Security Policy for full documentation.

Compression settings

Configure via CompressionConfig:

from django_bolt import BoltAPI, CompressionConfig

api = BoltAPI(
    compression=CompressionConfig(
        backend="brotli",      # "brotli", "gzip", or "zstd"
        minimum_size=1000,     # Minimum size to compress (bytes)
        gzip_fallback=True,    # Fall back to gzip
    )
)

All settings reference

Setting Type Default Description
CORS_ALLOWED_ORIGINS list[str] [] Allowed CORS origins
CORS_ALLOWED_ORIGIN_REGEXES list[str] [] Patterns for allowed CORS origins
CORS_ALLOW_ALL_ORIGINS bool False Allow all origins
CORS_ALLOW_CREDENTIALS bool False Allow credentials
CORS_ALLOW_METHODS list[str] GET, POST, PUT, PATCH, DELETE, OPTIONS, QUERY Allowed methods
CORS_ALLOW_HEADERS list[str] Content-Type, Authorization Allowed headers
CORS_EXPOSE_HEADERS list[str] [] Exposed headers
CORS_PREFLIGHT_MAX_AGE int 3600 Preflight cache (seconds)
BOLT_RATE_LIMIT_IPV6_PREFIX int 64 Network prefix that a rate limit counts an IPv6 client on
BOLT_MAX_UPLOAD_SIZE int 1048576 Max upload size (bytes)
BOLT_MEMORY_SPOOL_THRESHOLD int 1048576 Memory threshold before disk spooling (bytes)
BOLT_ALLOWED_FILE_PATHS list[str \| Path] None File serving whitelist
BOLT_STATIC_MAX_AGE int None Cache-Control max-age for static (public)
BOLT_MEDIA_MAX_AGE int None Cache-Control max-age for media (private)
BOLT_LOG_QUEUE_SIZE int 10000 Max depth of the non-blocking log queue (records dropped when full)
BOLT_EMIT_SIGNALS bool False Enable Django request signals
BOLT_DEV_FORCE_POLLING bool False Force --dev reloader to use polling instead of native file events
SECURE_CSP dict None CSP directives for static files (Django 6.0+)
BOLT_AUTHENTICATION_CLASSES list [] Default authentication backends
BOLT_DEFAULT_PERMISSION_CLASSES list [AllowAny()] Default permission guards
DJANGO_BOLT_MAX_PARAM_LENGTH int (env var) 8192 Max path/query/form parameter size in bytes, from 1 to 1048576 (1 MB); requests over the limit return 422
DJANGO_BOLT_ORM_THREADS int (env var) 1 (SQLite) or 4 Threads in the ORM pool of each process
DJANGO_BOLT_LANE_IDLE_SECONDS float (env var) 10 Idle time after which a request lane stops
DJANGO_BOLT_EXECUTOR_THREADS int (env var) CPU count + 4 (max 32) Threads in the shared executor pool of each process