Environment Variables
All configuration can be set via environment variables. A set variable overrides the same setting in the TOML config file; see Where saneless reads settings for the full order.
Naming Convention
Environment variables use the SANELESS_ prefix with double underscores (__) as the nesting delimiter:
SANELESS_{SECTION}__{FIELD}
For example, scanner.host in TOML becomes SANELESS_SCANNER__HOST.
Variable Reference
Scanner
| Variable | Config Path | Type | Example |
|---|---|---|---|
SANELESS_SCANNER__HOST |
scanner.host |
string | 192.168.1.50 |
SANELESS_SCANNER__DEVICE |
scanner.device |
string | net:192.168.1.50:pixma:MF740C |
Paperless
| Variable | Config Path | Type | Example |
|---|---|---|---|
SANELESS_PAPERLESS__URL |
paperless.url |
string | http://paperless:8000 |
SANELESS_PAPERLESS__TOKEN |
paperless.token |
string | your-api-token-here |
SANELESS_PAPERLESS__CONSUME_DIR |
paperless.consume_dir |
string | /consume |
Output
| Variable | Config Path | Type | Example |
|---|---|---|---|
SANELESS_OUTPUT__TMP_DIR |
output.tmp_dir |
string | /tmp/saneless-1000 |
SANELESS_OUTPUT__DATA_DIR |
output.data_dir |
string | /var/lib/saneless |
SANELESS_OUTPUT__LOG_FILE |
output.log_file |
string | /var/log/saneless.log |
SANELESS_OUTPUT__LOG_LEVEL |
output.log_level |
string | DEBUG |
SANELESS_OUTPUT__LOG_MAX_BYTES |
output.log_max_bytes |
int | 10485760 |
SANELESS_OUTPUT__LOG_BACKUP_COUNT |
output.log_backup_count |
int | 5 |
SANELESS_OUTPUT__HISTORY_RETENTION_DAYS |
output.history_retention_days |
int | 7 |
SANELESS_OUTPUT__HISTORY_MAX_ROWS |
output.history_max_rows |
int | 500 |
SANELESS_OUTPUT__PAPERLESS_TASK_TIMEOUT |
output.paperless_task_timeout |
int | 300 |
SANELESS_OUTPUT__PAPERLESS_CACHE_TTL_SECONDS |
output.paperless_cache_ttl_seconds |
int | 60 |
SANELESS_OUTPUT__OPERATOR_WAIT_TIMEOUT_SECONDS |
output.operator_wait_timeout_seconds |
int | 600 |
SANELESS_OUTPUT__MIN_FREE_SPACE_MB |
output.min_free_space_mb |
int | 500 |
SANELESS_OUTPUT__WEB_HOST |
output.web_host |
string | 0.0.0.0 (the default: all network interfaces) |
SANELESS_OUTPUT__WEB_PORT |
output.web_port |
int | 8080 |
SANELESS_OUTPUT__MIN_FREE_SPACE_MB is the free disk space saneless keeps in reserve for assembling the PDF. It is checked twice: once before a scan starts, and again before each page is written to disk, against that page's size plus this reserve. A scan that runs out of room fails naming the page number and the path, and the pages already scanned are preserved. See [output] for every field in this section.
SANELESS_OUTPUT__OPERATOR_WAIT_TIMEOUT_SECONDS was renamed from SANELESS_OUTPUT__FLIP_TIMEOUT_SECONDS, with no alias: the old variable is rejected at startup like any other unknown SANELESS_OUTPUT__ variable, and the error suggests operator_wait_timeout_seconds.
Web
| Variable | Config Path | Type | Example |
|---|---|---|---|
SANELESS_WEB__SHOW_TAGS |
web.show_tags |
bool | false |
SANELESS_WEB__SHOW_CORRESPONDENT |
web.show_correspondent |
bool | false |
SANELESS_WEB__ALLOWED_HOSTS |
web.allowed_hosts |
JSON list of strings | '["scan.example.com", ".home.example"]' |
The two SHOW_* variables default to true. Setting one to false hides that control on the scan form; the profile's default_tags and default_correspondent still apply, so hiding a control changes the form and never the scan.
The allowed-hosts variable must be a JSON list, even for one name: '["scan.example.com"]'. A bare scan.example.com is not valid JSON and stops saneless starting. The names it lists are added to the ones saneless always answers to; see Allowed host names.
The web server's bind address is not in this section: it is SANELESS_OUTPUT__WEB_HOST and SANELESS_OUTPUT__WEB_PORT above. See [web].
JSON values
A field that holds a list or a table -- default_tags, allowed_hosts, or a whole section such as SANELESS_PAPERLESS -- is read from its variable as JSON, for example SANELESS_PROFILES__DEFAULT__DEFAULT_TAGS='[3, 7]'. A comma-separated 3,7 is not JSON, and neither is a bare scan.example.com: either stops saneless at startup with exit code 2. The error gives the variable a line of its own, naming it and never echoing its value, and the config file's own errors are still listed with it:
environment variable 'SANELESS_PROFILES__DEFAULT__DEFAULT_TAGS': must be JSON (a list or table is written as JSON, for example [3, 7])
Not a saneless variable: TZ
| Variable | Type | Example |
|---|---|---|
TZ |
string | America/Chicago |
TZ is the standard POSIX/container timezone variable, not a SANELESS_ setting, and saneless never reads it directly -- Python's own local-time conversion does. It matters because saneless renders every user-facing timestamp in the server's local zone with the zone named: the web UI's job history and status area, the saneless jobs table, the status strip's Last checked line, and the Scan <date time> title a document gets in paperless-ngx when no title was given.
A container's clock reports UTC unless TZ is set, so without it every one of those timestamps is UTC, including the document title that ends up in paperless-ngx. Set it in your compose file's environment: block. A bare-metal install normally inherits the host's zone and needs nothing.
Not a saneless variable: SSL_CERT_FILE and SSL_CERT_DIR
| Variable | Type | Example |
|---|---|---|
SSL_CERT_FILE |
path | /etc/ssl/certs/my-ca.crt |
SSL_CERT_DIR |
path | /etc/ssl/my-ca-dir |
These are OpenSSL's own variables, not SANELESS_ settings, and saneless never reads them -- the TLS layer beneath its HTTP client does. They name the certificate authorities to trust when saneless connects to paperless-ngx over https://, and they replace the operating system's trust store rather than adding to it: once either is set, the OS trust store is not consulted at all, so the file or directory you name must carry every CA saneless needs -- not just the private one. A container that sets SSL_CERT_FILE to a single private CA therefore trusts exactly that one CA, so a paperless.url that is publicly signed -- today, or later when it moves behind Let's Encrypt -- fails inside the container while working everywhere else. With neither set, saneless trusts the operating system's trust store. Set one of these only when your paperless-ngx certificate is signed by a private or corporate CA that is not installed on this machine; installing that CA into the OS trust store is the better fix wherever you can do it. Troubleshoot a Failed Scan describes the failure they resolve, under TLS certificate not trusted.
Prefer SSL_CERT_FILE. It takes a single PEM file and needs nothing else. SSL_CERT_DIR takes a directory and carries a trap: OpenSSL reads only files named <8-hex-hash>.<n> in it, so dropping a bare .pem into the directory fails exactly as if you had set nothing at all, with no diagnostic anywhere to tell you why. Run c_rehash over the directory, or make the link yourself with ln -s my-ca.pem "$(openssl x509 -hash -noout -in my-ca.pem).0".
A path that does not exist, or one that is a directory where SSL_CERT_FILE expects a file, is not a silent fallback to the OS trust store either: saneless refuses at startup with Paperless error: Could not build the TLS trust store for Paperless at <url>: <OS error text>; check SSL_CERT_FILE and SSL_CERT_DIR. In Docker that is what a bind mount whose host file is missing produces, because Docker creates a directory at an absent bind-mount source. Create the file on the host and recreate the container.
Notes
-
Profile fields use
SANELESS_PROFILES__<NAME>__<FIELD>, for exampleSANELESS_PROFILES__RECEIPT__TITLE=Receiptfortitlein[profiles.receipt]. Variable names are case-insensitive, and profile names are lower-cased. Adefaultprofile must still exist: with no config file, set at least oneSANELESS_PROFILES__DEFAULT__<FIELD>too, or loading fails. -
Unknown variables are rejected. A
SANELESS_variable whose first segment after the prefix is notSCANNER,PAPERLESS,OUTPUT,WEBorPROFILESstops saneless at startup with exit code 2. The error names the variable and suggests a fix:SANELESS_PAPERLES__TOKENsuggestsSANELESS_PAPERLESS__TOKEN, and a single underscore such asSANELESS_OUTPUT_WEB_PORTsuggestsSANELESS_OUTPUT__WEB_PORT. An unknown field in a known section, such asSANELESS_SCANNER__HOSTNAME, is reported naming the variable, the same way as an unknown key in the TOML file (see Validation). Values are never printed. -
saneless logs where its settings came from. At startup it writes one INFO line naming the config file it loaded (or saying there was none) and the dotted names of the settings that came from environment variables, for example
paperless.url, paperless.token. Names only, never values. -
In a container, keep the paperless-ngx URL and token in the mounted
saneless.tomlrather than inSANELESS_PAPERLESS__URLandSANELESS_PAPERLESS__TOKEN: a set variable silently overrides the file (see Where saneless reads settings), so a token left in the compose file goes on winning after you change the one insaneless.toml.SANELESS_SCANNER__HOSTis the variable a container usually sets. -
A relative path follows the config file. A relative
SANELESS_OUTPUT__TMP_DIR,SANELESS_OUTPUT__DATA_DIR,SANELESS_OUTPUT__LOG_FILEorSANELESS_PAPERLESS__CONSUME_DIRis resolved against the directory of the config file that was loaded, exactly as the same value in the file would be, or against the working directory when no config file was loaded. The log andsaneless doctorshow the absolute result. See[output]. -
Multiple scanner hosts can be specified in
SANELESS_SCANNER__HOSTusing colon separation:192.168.1.50:192.168.1.51. -
SANELESS_OUTPUT__DATA_DIRis durable state,SANELESS_OUTPUT__TMP_DIRis not.data_dirholds the job database (saneless.db) and thefailed/directory of scans that could not be delivered to paperless-ngx; it defaults to$XDG_STATE_HOME/saneless(~/.local/state/sanelesswhenXDG_STATE_HOMEis unset) and must survive restarts.tmp_diris scratch space for the scan in progress and can be thrown away. The official container image does not set this variable. It setsXDG_STATE_HOME=/var/libinstead, so inside the containerdata_dirdefaults to/var/lib/saneless, the image's data volume, and you only need to setSANELESS_OUTPUT__DATA_DIRif you mount the volume somewhere else. Setting it still overrides that default, and[output] data_dirinsaneless.tomlwith it.