* feat(version): include OS and arch in the outbound User-Agent
Registries and galleries already receive LocalAI/<version>; adding the
platform follows ordinary client convention and discloses nothing a
registry cannot infer from the manifest it is asked for.
Updates the User-Agent note in docs/content/getting-started/models.md,
which documented the old format.
Assisted-by: Claude:claude-opus-5 [go vet] [go test]
* feat(downloader): identify LocalAI on outbound requests
pkg/oci has always sent a User-Agent; the downloader sent none, so gallery
reads, model-file downloads, resume probes, content-length probes and the
HuggingFace safety scan all went out as a bare Go HTTP client, unattributable
to LocalAI by the hosts serving them.
HuggingFaceScan moves off the client's Get shorthand to an explicit request
for the same reason — the shorthand gives no place to hang a header.
Extends the User-Agent note in docs/content/getting-started/models.md, which
claimed the header was sent only to Ollama and OCI registries.
Assisted-by: Claude:claude-opus-5 [go vet] [go test]
* feat(gallery): add a mirrors list to gallery configuration
Mirrors are an availability fallback, tried in order only after the primary
URL fails. omitempty keeps existing configurations byte-identical.
The slice makes config.Gallery non-comparable with ==, which broke the two
slices.Equal callers in the runtime settings registry. Replace them with an
explicit Gallery.Equal / GalleriesEqual so a gallery list that differs from
the baseline only by its mirrors still counts as env/CLI-set. Equal compares
the Verification block by value; == compared it by pointer identity, which
called two structurally identical policies different.
Assisted-by: Claude:claude-opus-5 [go vet] [go test]
* fix(downloader): treat an HTTP error status as a failed read
ReadWithCallback handed the response body to its callback whatever the
status was, so a 404 page or a 502 from a CDN arrived as if it were a
gallery index or a model config: it parsed to nothing, got cached for an
hour, and no caller could tell the source had been down. DownloadFile has
always checked the status; this path never did.
Mirror fallback depends on it — a source that answers with an error page
has to count as unreachable, or the next candidate is never tried.
Assisted-by: Claude:claude-opus-5 [go vet] [go test]
* feat(gallery): fall back to mirrors when the primary source fails
Candidates are tried primary-first with a bounded timeout each, and a
source that just failed is skipped for a cooldown so a dead host is not
re-dialled on every listing. When every candidate is in cooldown they are
all tried anyway: refusing to serve a gallery we might be able to reach is
worse than one slow request.
The one-hour index cache is untouched and stays keyed on the gallery's own
identity, so a mirror-served fetch fills the entry the primary would have.
No SSRF validation is applied to the candidates. validateGalleryConfigURL
guards GetGalleryConfigFromURL because that URL arrives in a request body;
mirrors come from the operator's gallery configuration, the same place the
primary has always come from, and the index fetch has never validated the
primary. Validating mirrors while the primary goes unchecked would buy
nothing and would break the deployment mirrors exist for — an index served
from a host on the LAN.
Assisted-by: Claude:claude-opus-5 [go vet] [go test]
* fix(gallery): loosen the mirror fetch timeout and stop blaming the caller
The downloader only ever bounded response headers, never the body, so the
per-attempt deadline added with mirror fallback was the first whole-transfer
timeout this path has had. At 30s the default 2.2 MB index demanded ~75 KB/s
sustained: a rural-DSL, mobile or satellite user who used to wait 60s and
succeed would now fail, and then eat a 10-minute cooldown on a source that
was perfectly healthy. Raised to 120s (~19 KB/s), which no link that could
go on to download a model will miss, and made it a var so a test can shorten
it and prove a hanging candidate is actually abandoned.
Caller cancellation is no longer recorded as a failure of the source.
Unreachable today since getGalleryElements passes context.Background(), but
once a request context is wired through, a browser disconnect would have
blackholed every candidate for ten minutes over something the sources had
no part in.
Also document that mirrors do not cover a .ref gallery URL: the reference is
resolved before mirrors are considered, so a .ref that cannot be fetched
fails the gallery outright. Routing .ref resolution through the candidate
list needs a per-candidate resolve-and-fetch and a decision about cache
identity, which is more than this change should carry.
Assisted-by: Claude:claude-opus-5 [go vet] [go test]
* feat(gallery): serve the last known good index when everything is offline
A successful fetch is cached alongside the models directory and served when
no source is reachable, so an offline or airgapped machine can still list
its gallery. Entries may be stale in that state, and the fallback is logged.
The copy is deliberately kept out of the models directory, where a <name>.yaml
file is read as an installed model's configuration, and is named after a digest
of the gallery URL so the model and backend galleries cannot collide. Writing
it is best effort: a read-only or full disk must not fail a fetch that
otherwise succeeded.
Also corrects the mirror scheme list in the docs: the HuggingFace prefixes are
huggingface://, hf:// and hf.co/, not huggingface:.
Assisted-by: Claude:claude-opus-5 [go vet] [go test]
* fix(gallery): only cache a response that is really a gallery index
The last known good copy was written on any 2xx, before anything looked
at the bytes: the parse only happens later, in getGalleryElements. A
captive portal, a corporate proxy or a CDN error page all answer HTTP 200
with HTML, so any of them could overwrite a good copy. The listing fails
then and there, and the next offline start — the one case this cache
exists for — serves the interception page instead of the gallery it
already had.
Probe the body before persisting it: unmarshal into a []any and keep the
older copy unless the result is a non-empty sequence. An empty document
is rejected too. It parses fine, so a parse-only check would still let a
blank response replace a populated index with one that lists nothing,
which from the user's side is the same outage; and an empty index is
worth nothing offline, so there is no case where caching it beats keeping
what came before. The live body is still returned to the caller — the
probe gates persistence only, and getGalleryElements remains the thing
that reports a real parse failure.
Also in this pass:
- The empty-basePath guard only caught exact "". galleryCachePath(".")
and galleryCachePath("models") still resolved the cache sibling against
the process working directory, which is what the guard was written to
prevent. Reject any non-absolute base.
- The docs claimed the offline cache "applies to every gallery, with or
without mirrors". Not true for a .ref URL: the reference is resolved
before the cache is consulted, so a .ref gallery fails offline even
after a successful earlier fetch, and the cache file it writes can
never be read. Extend the .ref warning and qualify the sentence.
- pkg/oci's UserAgent comment never mentioned the platform component
added earlier on this branch.
- resetGalleryFailures and expireGalleryFailure had no non-test callers;
move them into the test file.
- The all-candidates-failed error reported len(attempt), so a three
mirror gallery with two sources in cooldown said "all 1 source(s)
failed" — which reads as a misconfiguration. Report how many were
configured and how many were skipped.
- Give the package's tests their own TMPDIR. The cache is a sibling of
the models directory, which is right in production, but specs that
build a models directory directly under /tmp made the sibling resolve
to /tmp/cache and left it behind after every run.
Assisted-by: Claude:claude-opus-5 [go vet] [go test]
* fix(gallery): convert the new tests to Ginkgo and clear the lint gate
.agents/coding-style.md requires Ginkgo v2 + Gomega for every Go test and
has forbidigo enforce it; the stdlib-style tests still in the tree are tech
debt, not a pattern. Every test file this branch added was written in the
forbidden style, which is what turned CI red.
Convert all five of them. internal had no suite bootstrap, so add one;
core/config, core/gallery and pkg/downloader already have theirs and are
reused, so no package mixes styles. pkg/downloader/useragent_test.go and
read_status_test.go were not in CI's forbidigo list but used the same
forbidden calls, so they are converted too.
The one conversion with a trap in it is core/gallery. Go's t.TempDir()
yields $TMPDIR/<TestName>NNNN/001, so the gallery cache — a sibling of the
models directory — was isolated per test. GinkgoT().TempDir() yields a flat
$TMPDIR/ginkgoNNNN, which would put every spec's cache in one shared
directory and break the specs that count files in it. tempModelsDir()
restores the original isolation.
Also make the deliberate cleanup-path ignores explicit with `_ =`, drop the
gallery cache directory to 0750 (nothing outside the server's own user and
group reads it), and justify the cache read with a #nosec G304 comment in
the form already used elsewhere in the tree: the path is a hex sha256 under
a fixed directory with a non-absolute base already rejected, so no
caller-supplied text reaches it.
Re-ran the mutations these specs were verified against — dropping the
platform suffix from UserAgent, making Gallery.Equal ignore Mirrors and
ignore Name, removing persistGalleryIndex's validity probe, removing the
!filepath.IsAbs guard, not skipping a cooled-down candidate, and dropping
the per-attempt timeout. All seven still fail the converted specs.
Assisted-by: Claude:claude-opus-5 [go vet] [go test] [golangci-lint] [gosec]
---------
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
10 KiB
+++ disableToc = false title = "Runtime Settings" weight = 82 url = '/features/runtime-settings' +++
LocalAI provides a web-based interface for managing application settings at runtime. These settings can be configured through the web UI and are automatically persisted to a configuration file, allowing changes to take effect immediately without requiring a restart.
Accessing Runtime Settings
Navigate to the Settings page from the management interface at http://localhost:8080/manage. The settings page provides a comprehensive interface for configuring various aspects of LocalAI.
Available Settings
Watchdog Settings
The watchdog monitors backend activity and can automatically stop idle or overly busy models to free up resources.
- Watchdog Enabled: Master switch to enable/disable the watchdog
- Watchdog Idle Enabled: Enable stopping backends that are idle longer than the idle timeout
- Watchdog Busy Enabled: Enable stopping backends that are busy longer than the busy timeout
- Watchdog Idle Timeout: Duration threshold for idle backends (default:
15m) - Watchdog Busy Timeout: Duration threshold for busy backends (default:
5m)
Changes to watchdog settings are applied immediately by restarting the watchdog service.
Backend Configuration
- Max Active Backends: Maximum number of active backends (loaded models). When exceeded, the least recently used model is automatically evicted. Set to
0for unlimited,1for single-backend mode - Force Eviction When Busy: Allow evicting models even when they have active API calls (default: disabled for safety). Warning: Enabling this can interrupt active requests
- LRU Eviction Max Retries: Maximum number of retries when waiting for busy models to become idle before eviction (default: 30)
- LRU Eviction Retry Interval: Interval between retries when waiting for busy models (default:
1s)
Note: The "Single Backend" setting is deprecated. Use "Max Active Backends" set to
1for single-backend behavior.
LRU Eviction Behavior
By default, LocalAI will skip evicting models that have active API calls to prevent interrupting ongoing requests. When all models are busy and eviction is needed:
- The system will wait for models to become idle
- It will retry eviction up to the configured maximum number of retries
- The retry interval determines how long to wait between attempts
- If all retries are exhausted, the system will proceed (which may cause out-of-memory errors if resources are truly exhausted)
You can configure these settings via the web UI or through environment variables. See [VRAM Management]({{%relref "advanced/vram-management" %}}) for more details.
Performance Settings
- Threads: Number of threads used for parallel computation (recommended: number of physical cores)
- Context Size: Default context size for models (default:
512) - F16: Enable GPU acceleration using 16-bit floating point
- VRAM Budget: Cap on VRAM used for model allocation (for example
80%or12GB; empty means no cap). See [VRAM Management]({{%relref "advanced/vram-management" %}})
Debug and Logging
- Debug Mode: Enable debug logging (deprecated, use log-level instead)
API Security
- CORS: Enable Cross-Origin Resource Sharing
- CORS Allow Origins: Comma-separated list of allowed CORS origins
- CSRF: Enable CSRF protection middleware
- API Keys: Manage API keys for authentication (one per line or comma-separated)
For multi-user authentication with roles, OAuth, and usage tracking, see [Authentication & Authorization]({{%relref "features/authentication" %}}).
P2P Settings
Configure peer-to-peer networking for distributed inference:
- P2P Token: Authentication token for P2P network
- P2P Network ID: Network identifier for P2P connections
- Federated Mode: Enable federated mode for P2P network
Changes to P2P settings automatically restart the P2P stack with the new configuration.
Gallery Settings
Manage model and backend galleries:
- Model Galleries: JSON array of gallery objects with
urlandnamefields, plus an optionalmirrorslist of fallback URLs (see [Gallery mirrors]({{%relref "features/model-gallery#gallery-mirrors" %}})) - Backend Galleries: JSON array of backend gallery objects, which accept the same
mirrorskey - Autoload Galleries: Automatically load model galleries on startup
- Autoload Backend Galleries: Automatically load backend galleries on startup
Agent Pool Settings
Configure the built-in agent platform (see [Agents]({{%relref "features/agents" %}}) for full documentation):
- Agent Pool Enabled: Enable or disable the agent pool feature
- Default Model: Default LLM model for new agents
- Embedding Model: Model used for knowledge base embeddings (default:
granite-embedding-107m-multilingual) - Max Chunking Size: Maximum chunk size for document ingestion (default:
400) - Chunk Overlap: Overlap between document chunks (default:
0) - Enable Logs: Enable detailed agent logging
- Collection DB Path: Custom path for the collections database
Note: Most agent pool settings require a restart to take effect.
Configuration Persistence
All settings are automatically saved to runtime_settings.json in the LOCALAI_CONFIG_DIR directory (default: BASEPATH/configuration). This file is watched for changes, so modifications made directly to the file will also be applied at runtime.
Settings Precedence
Every runtime setting follows a single precedence rule:
- Environment variables and CLI flags (highest priority)
- Configuration files (
runtime_settings.json,api_keys.json) - Default values (lowest priority)
The same rule is applied identically in all three places where settings can change:
- At boot: values persisted in
runtime_settings.jsonfill in every setting that was not explicitly set via an environment variable or CLI flag. - On
POST /api/settings(the web UI Settings page): if a setting is controlled by an environment variable, it cannot be modified through the web interface. The settings page will indicate when a setting is controlled by an environment variable. - On manual file edits: the configuration watcher hot-applies edits to
runtime_settings.jsonwith the same env-over-file semantics as boot, so editing the file by hand behaves like restarting with that file. For example, hand-editingvram_budgetinstalls the new VRAM allocation cap live, without a restart.
Known Limitations
- An environment variable explicitly set to its default value is indistinguishable from an unset one, so the value from
runtime_settings.jsonwins in that case. In particular, an explicitLOCALAI_THREADS=0behaves like an unsetLOCALAI_THREADS. - A field previously changed via the API (or the web UI) looks environment-set to the file watcher, so a manual file edit of that same field is not hot-applied: it takes effect on the next restart.
Example Configuration
The runtime_settings.json file follows this structure:
{
"watchdog_enabled": true,
"watchdog_idle_enabled": true,
"watchdog_busy_enabled": false,
"watchdog_idle_timeout": "15m",
"watchdog_busy_timeout": "5m",
"max_active_backends": 0,
"force_eviction_when_busy": false,
"lru_eviction_max_retries": 30,
"lru_eviction_retry_interval": "1s",
"threads": 8,
"context_size": 2048,
"f16": false,
"debug": false,
"cors": true,
"csrf": false,
"cors_allow_origins": "*",
"p2p_token": "",
"p2p_network_id": "",
"federated": false,
"galleries": [
{
"url": "github:mudler/LocalAI/gallery/index.yaml@master",
"name": "localai"
}
],
"backend_galleries": [
{
"url": "github:mudler/LocalAI/backend/index.yaml@master",
"name": "localai"
}
],
"autoload_galleries": true,
"autoload_backend_galleries": true,
"api_keys": []
}
API Keys Management
API keys can be managed through the runtime settings interface. Keys can be entered one per line or comma-separated.
Important Notes:
- API keys from environment variables are always included and cannot be removed via the UI
- Runtime API keys are stored in
runtime_settings.json - For backward compatibility, API keys can also be managed via
api_keys.json - Empty arrays will clear all runtime API keys (but preserve environment variable keys)
Dynamic Configuration
The runtime settings system supports dynamic configuration file watching. When LOCALAI_CONFIG_DIR is set, LocalAI monitors the following files for changes:
runtime_settings.json- Unified runtime settingsapi_keys.json- API keys (for backward compatibility)external_backends.json- External backend configurations
Changes to these files are automatically detected and applied without requiring a restart, using the same precedence rule described in Settings Precedence.
Best Practices
-
Use Environment Variables for Production: For production deployments, use environment variables for critical settings to ensure they cannot be accidentally changed via the web UI.
-
Backup Configuration Files: Before making significant changes, consider backing up your
runtime_settings.jsonfile. -
Monitor Resource Usage: When enabling watchdog features, monitor your system to ensure the timeout values are appropriate for your workload.
-
Secure API Keys: API keys are sensitive information. Ensure proper file permissions on configuration files (they should be readable only by the LocalAI process).
-
Test Changes: Some settings (like watchdog timeouts) may require testing to find optimal values for your specific use case.
Troubleshooting
Settings Not Applying
If settings are not being applied:
- Check if the setting is controlled by an environment variable
- Verify the
LOCALAI_CONFIG_DIRis set correctly - Check file permissions on
runtime_settings.json - Review application logs for configuration errors
Watchdog Not Working
If the watchdog is not functioning:
- Ensure "Watchdog Enabled" is turned on
- Verify at least one of the idle or busy watchdogs is enabled
- Check that timeout values are reasonable for your workload
- Review logs for watchdog-related messages
P2P Not Starting
If P2P is not starting:
- Verify the P2P token is set (non-empty)
- Check network connectivity
- Ensure the P2P network ID matches across nodes (if using federated mode)
- Review logs for P2P-related errors