Listen on port 8080.
Docs / Configuration & operations
Configuration & operations v0.2.0
picturium reads an optional config.toml at startup and then applies any PICTURIUM__
environment variables on top. This page covers what matters most when you run it:
caching, concurrency, CORS and logs. Every single key is listed in the
config key reference, and settings that affect responses are
explained in Output format & quality.
Config file
- picturium loads
config.tomlfrom the working directory, or whichever filePICTURIUM_CONFIGpoints to. The file is optional; every key has a default. - Settings are layered: built-in defaults, then
config.toml, thenPICTURIUM__environment variables, each overriding the one before. - You only need the keys you want to change. Anything left out keeps its default.
- Unknown keys are rejected. A typo in the file or in an environment variable stops startup, and the error names the key.
- Values are checked at startup too. Bad cache sizes, quality curves, encoder effort, watermark settings and the like stop picturium from starting.
- The startup log prints the bind address, directories, cache setup, CORS origins, whether signing and health details are on, the libvips settings, and the worker and queue counts.
- Changing any setting changes every cache key and
ETag. Previously cached output is no longer used and gets rendered again on the next request.
Sections
[server] [security] [cors] [vips] [data] [cache]: [cache.memory], [cache.disk] [svg] [pdf] [image] [watermark]: [watermark.image],[watermark.text] [output]: [output.quality_curves], [output.quality_curves.<format>], [output.encoder.<format>] [office] [vector] [video]
The config key reference lists every key with its type, default and
what it does. The picturium repository also includes config.toml.example with comments.
A minimal file
Only include what you want to change:
# config.toml
[server]
port = 8080
log_level = "info"
[cache.disk]
limit = 4096
Environment overrides
- To set a key from the environment, uppercase it, add the
PICTURIUM__prefix, and put a double underscore between levels. - Environment variables win over both the defaults and
config.toml, which makes them handy for tweaking one deployment without touching the file. - Values are parsed, so
true,falseand numbers work as you’d expect. - Lists are comma-separated. That applies to
cors.allowed_origins,data.serve,output.format_priorityandoutput.metadata. - Only use double underscores between levels. Single underscores inside a key name stay
as they are:
output.quality_curves.jpeg.minbecomesPICTURIUM__OUTPUT__QUALITY_CURVES__JPEG__MIN. - Unknown keys fail here too:
PICTURIUM__SERVER__PROT=8080won’t start. PICTURIUM_CONFIG(single underscore) isn’t a config key. It only tells picturium which file to load.
Enter a configuration key to convert it to the environment variable name picturium expects.
Allow 8 images to render at once instead of using twice the CPU count.
Disk-only cache.
Use a 4 GiB disk cache.
Two explicit origins (comma-separated list).
Keep single underscores inside key names; use double underscores only between levels.
10-bit AVIF output.
Cache tuning
Both cache tiers under [cache] are on by default. picturium checks memory first, then
disk, and a result too big for either is served without being cached. The same tiers also
hold Office, SVG, vector and video intermediates.
Directory used by the disk cache. Only one picturium process can use it at a time.
Sent verbatim on every 200. A force=true response sends no-store instead.
Checks the memory cache before the disk cache.
MiB of total memory held by the memory cache. Entries are charged their own size.
MiB. A render larger than this skips memory: to disk if enabled, otherwise served uncached.
Enables the disk cache and locks its directory for this process.
MiB of effective capacity, shared by finished responses and Office / SVG / video intermediates. foyer evicts and reclaims automatically.
MiB. Largest single response written to disk, and foyer's block size — the unit it reclaims in.
- One process per cache directory. With the disk cache on, picturium locks
.picturium-cache.lockinsidecache.dir. A second process pointed at the same directory won’t start, so give each instance its own. - A single disk entry can be at most
cache.disk.entry_limit(16 MiB by default). That value is also foyer’s block size, the unit it reclaims space in. A bigger value evicts more entries at once and keeps one block in memory per flusher. Responses too big for disk can still be cached in memory if they fitcache.memory.entry_limit; otherwise they aren’t cached. - Changing
cache.disk.entry_limitthrows away the disk cache. A cache written with one block size can’t be read with another, so picturium keeps a directory per block size and deletes the others at startup. - An enabled tier needs a
limitabove zero, and itsentry_limitcan’t be larger than itslimit. Setenabled = falseto switch a tier off. With both off, picturium takes no lock, doesn’t need a cache directory, and renders every request from scratch. - The memory tier holds at most
cache.memory.limitMiB (256 by default). Each entry counts with its real size, so the budget is the same whether it’s full of thumbnails or large renders. libvips needs working memory for each request in progress on top of that. - If picturium finds an old
intermediatefolder incache.dirat startup, it removes it. - A failed cache read counts as a miss. If writing to the cache fails, the response still goes out fine; the next request just renders again.
- Nothing expires on a timer. Entries go away through eviction,
force=true, or a config change that produces new keys. To empty the disk cache, stop picturium and deletecache.dir.
Caching & force explains how requests use the cache,
and the difference between cache= and force=true.
Concurrency & load
Maximum number of images rendered at once. 0 means twice the logical CPU count.
Maximum number of requests waiting for a worker. A full queue rejects new requests.
Threads libvips uses inside a single operation. Total threads ≈ workers × vips.concurrency.
- A request waiting for a worker holds a queue slot, and gives it up once a worker picks
it up. In total, picturium handles at most
workers + queue_sizerequests at a time. - Beyond that, new requests immediately get 503
Too many requestswithCache-Control: no-store. - Rendering is CPU-heavy. A good starting point is keeping
workers×vips.concurrencyclose to your number of CPU cores. More workers help with lots of requests at once; a highervips.concurrencycan speed up a single large image on an otherwise idle server. GET /healthreturns just astatus, always with HTTP 200, so a monitor that should alert onwarningorunhealthyneeds to read the body. Withsecurity.health_tokenset, sendingAuthorization: Bearer <token>(or?token=<token>, which ends up in access logs) addsname,version,total_workers,available_workers,queue_size,available_queue_sizeand amemoryobject with process RSS and peak RSS, libvips memory, allocations, open files and cached operations, and memory cache usage and capacity. A wrong token gets 401. See Health & capacity.- The worker and queue counts are printed at startup.
Normal operation.
Free queue slots have fallen below 10% of the worker count.
No free worker and no free queue slot.
CORS & logging
Use "*" to allow every origin, method, and header. Otherwise, list each allowed origin exactly. Invalid entries are ignored.
Sets log detail to debug, info, warn, or error. RUST_LOG overrides this value when set.
Turns on libvips leak checking and prints the objects still alive at shutdown. Diagnostics only; it adds overhead.
- If only certain sites should be able to read responses, list their origins:
PICTURIUM__CORS__ALLOWED_ORIGINS=https://app.example,https://cdn.example. - Logs are plain text on standard output.
debugis chatty, so useinfoorwarnin production. RUST_LOGaccepts the fulltracingfilter syntax and overridesserver.log_level, for exampleRUST_LOG=picturium=info,tower_http=warn.vips.debugis for troubleshooting only. Leave it off in production.- URL signing (
security.signature_enabled,security.signature_secret) is covered in Signed URLs.