Skip to main content

Configuration

Who it’s for
Operators running the host from source or in production, and anyone who needs to know why the production deployment looks the way it does.
What it covers
Every key the host and the inference worker bind, grouped by area, with its default in code, its value in the shipped appsettings.json and its value in the Spark deployment.
Why it exists
Most settings live in the portal. The ones here are the few the installer fixes at deploy time, and they are easier to reason about when each default is written down once.

How configuration is read​

The host is an ASP.NET Core app. It reads appsettings.json, then appsettings.<Environment>.json, then environment variables. In an environment variable, __ stands for :, so HostPlatform__ApiKey sets HostPlatform:ApiKey. Indexed lists use numbers: HostAuthentication__TrustedProxyNetworks__0.

You rarely edit these by hand on the Spark. The installer writes the Compose project for the host from src/Lucia.Homelab.Identity.AppHost/AppHost.cs, and it takes its values from the per-installation settings in ~/.local/share/lucia/host. Secrets are generated files under the installation's secrets directory. They reach the container as environment variables or as read-only mounted files, and never go into appsettings.json.

Day-to-day settings are not configuration. The domain, AdGuard, UniFi, storage, registries and assistant permissions are stored in the host's own state and edited in the portal under Settings.

Three columns recur below:

Options that fail validation stop the host at startup, with the key named in the message.

HostPlatform​

Models, inference and the owner API. Bound and validated at startup (ValidateOnStart).

KeyCodeShippedSparkRange and notes
ApiKeynone, requirednonegenerated secret32 or more characters. Owner-level bearer key.
InferenceApiKeynonenonegenerated secret32 or more characters, and different from ApiKey. Inference-only.
ModelDirectorydata/modelsdata/models/modelsModel library and active-models.json
HuggingFaceExecutablehfhf/opt/hf/bin/hfCLI used for downloads
HuggingFaceHomeDirectorynonenone/data/huggingfaceHF_HOME for the download child. Without it, a .huggingface folder under ModelDirectory.
HuggingFaceTokennonenonenot setFallback token. A token saved on the Models page replaces it.
Backendggml_cudaggml_cudaggml_cudaOne of cpu, ggml_cpu, ggml_cuda, cuda
ContextTokens32768327688192 on a fresh install256 to 1,048,576. Context used when a load names no size.
MaxOutputTokens2048204820481 to 32,768. Upper bound for max_tokens in requests.
SreThinkingtruetruetrueWhether the assistant's local model reasons before it answers
MemoryBudgetGiBnone, detectednonenot set1 to 8,192. Required for CUDA on anything but Linux arm64.
OsReserveGiB4880 to 1,024
ServicesReserveGiB3880 to 1,024
VoiceReserveGiB888 (installer record)8 to 1,024. Held for a voice pipeline that does not exist yet.
RuntimeReserveGiB3881 to 1,024
WeightMemoryMultiplier1221 to 8. Calibration knob for the weight estimate.
RequireTensorSharptruetruetrueSet false when another engine serves the library
LlamaCachenonenonenot setLocal directory that Ready GGUFs are copied to, with llama-models.ini
LlamaLoadOnStartupnonenonenot setComma-separated llama.cpp model names to preload
ChatModelId, EmbeddingModelIdnonenonenot setModel GUIDs to load at start when no selection is saved

The memory model that these reserves feed is in Local AI. The environment variable KV_CACHE_DTYPE=f32 doubles the KV estimate from 2 to 4 bytes per element. The host sets TS_VRAM_HEADROOM_MB itself, to the larger of its existing value and the sum of the four reserves.

HF_TOKEN and HUGGING_FACE_HUB_TOKEN in the host's own environment are removed from the download child on purpose. Use the Models page or HostPlatform__HuggingFaceToken.

HostAuthentication​

Portal sign-in through Authentik (OpenID Connect with PKCE). Defaults to off in code. Outside Development, Enabled must be true or the host will not start.

KeyCodeSparkRule
EnabledfalsetrueRequired true outside Development
AuthorityemptyAuthentik's lucia application URLAbsolute HTTPS URL, no query or fragment
ClientIdemptyfrom identity provisioningRequired
ClientSecretFileempty/run/secrets/host-oidc-client-secretAbsolute path to a file. The secret is never an environment value.
PublicOriginemptythe portal's originHTTPS origin, no path
AdditionalPublicOriginsemptynot setAt most 4 exact HTTPS origins. No wildcards.
AuthorityUsesSystemTrustfalsenot setTrust Authentik through the system store instead of the CA file
CaCertificatePathempty/trust/lucia-root-ca.crtPublic certificate of a valid CA. A PEM with a private key is rejected.
DataProtectionKeysDirectoryempty/data/data-protectionAbsolute. Back it up with /data.
TrustedProxyNetworksemptythe identity network's subnetsAt least one private CIDR (10/8, 172.16/12, 192.168/16 or fc00::/7)

Sessions last 8 hours and do not slide. Cookies are __Host- prefixed, secure and same-site. The host trusts X-Forwarded-* headers only from TrustedProxyNetworks, and only for the hosts in PublicOrigin and AdditionalPublicOrigins. See Security model and Identity.

Storage directories​

Each store has its own key. In production they all sit under the container's /data volume, which is ~/.local/share/lucia/host/data on the Spark.

KeyCode defaultSpark
Assistant:Directorylocal app data Lucia/data/assistant/data/assistant
InferenceKeys:Directorylocal app data Lucia/inference-keys/data/inference-keys
HuggingFaceManagement:CredentialsDirectorylocal app data Lucia/provider-credentials/data/provider-credentials
AdGuardManagement:CredentialsDirectorylocal app data Lucia/network-credentials/data/network-credentials
CloudflareDomains:CredentialsDirectorylocal app data Lucia/network-credentials/data/network-credentials
DomainOnboarding:StateDirectorylocal app data Lucia/data/domains/data/domains
HardwareOnboarding:StateDirectorylocal app data Lucia/onboarding/data/onboarding
PackageUpdates:Directorylocal app data Lucia/data/packages/data/packages
Telemetry:RelayDirectorynone/data/telemetry

"Local app data" is %LOCALAPPDATA% on Windows and ~/.local/share on Linux. Directories must be absolute, and the host rejects symbolic links in the path for credential stores. PackageUpdates:Directory is shared with the root worker. See Spark updates. Setting Telemetry:RelayDirectory makes the controller write the Spark's relay configuration there. It also requires HostAuthentication:PublicOrigin.

Assistant​

KeyCodeNotes
Directoryas abovePer-user settings, jobs, run history, push devices and encrypted sign-ins
GitHubClientIdLucia's public GitHub App idClient id for GitHub's device flow. No secret is involved.
ModelnoneModel used when the chat bar picks none. Null lets the runtime choose.

See Assistant.

DomainOnboarding​

KeyCodeSparkNotes
StateDirectoryas above/data/domains
GatewayDirectoryempty/domain-gatewayWhere the Traefik dynamic configuration is written
CertificateGatewayDirectory/domain-certificatesnot set
InstallationIdnonefrom the installerTies domain state to this installation
IngressAddressnonethe boot network's bind addressAddress the local DNS rewrites point at

HardwareOnboarding and Boot​

Set only when the boot service was provisioned. The qualification flags decide what the Devices page may do. See Nodes and Installer.

KeyCodeSpark
HardwareOnboarding:DiscoveryNetworkCidrnonethe boot LAN, a private IPv4 subnet
HardwareOnboarding:BootBaseUrlnonethe portal origin. HTTPS, no path.
HardwareOnboarding:DiscoveryAdapterQualifiedfalsefrom boot provisioning
HardwareOnboarding:InstallationEnabledfalsetrue only when installation is qualified
HardwareOnboarding:BootArtifactsQualifiedfalsesame as above
HardwareOnboarding:EnrollmentQualifiedfalsesame as above
HardwareOnboarding:EnrollmentStatusFilenone/data/nodes/enrollment-worker.json
Boot:Enabledfalsetrue
Boot:ControlDirectoryempty/data/boot-control
Boot:AllowedNetworksemptythe boot LAN CIDR, as Boot__AllowedNetworks__0

Boot:AllowedNetworks takes private IPv4 subnets only.

SparkTelemetry​

KeyCodeSparkNotes
EnabledfalsetrueNeeds Linux and absolute paths below
ProcDirectory/host-metrics/host-metricsRead-only bind mounts of the host's stat, meminfo, loadavg, uptime, net/dev and net/route
StoragePath/data/dataVolume whose free space is reported

The sampler also runs nvidia-smi inside the container, so the container needs the GPU device. See Spark telemetry.

Other keys​

KeyWhereNotes
ConnectionStrings:cachehostA Redis connection for output caching. Without it, the host uses an in-memory cache.
OTEL_EXPORTER_OTLP_ENDPOINThostWhen set, the standard OTLP exporter is used. When empty, telemetry goes to the Observability app through the controller's forwarder.
OTEL_SERVICE_NAMEhostDefaults to lucia-controller
OTEL_RESOURCE_ATTRIBUTESSparkSet to host.name and lucia.node, both the machine's lowercase name
ASPNETCORE_ENVIRONMENTSparkProduction
ASPNETCORE_HTTP_PORTSSpark8080. The gateway terminates TLS.
HF_HOME, HOMESpark/data/huggingface and /data
Worker:LibraryOnlyinference workertrue beside vLLM or llama.cpp: keep the library, serve nothing

The Spark container runs as the setup account's uid and gid with all capabilities dropped, no-new-privileges, the NVIDIA GPU device, and a health check on /health/live.

Inference worker defaults​

Lucia.Inference.Worker inserts these defaults ahead of its own configuration, so Compose can leave them out:

KeyDefault
HostPlatform:Backendggml_cuda
HostPlatform:ModelDirectory/models
HostPlatform:HuggingFaceExecutable/opt/hf/bin/hf
HostPlatform:OsReserveGiB, ServicesReserveGiB, VoiceReserveGiB0
HostPlatform:RuntimeReserveGiB2
HostPlatform:WeightMemoryMultiplier1

It requires ApiKey, InferenceApiKey (distinct, 32 or more characters) and a positive MemoryBudgetGiB.

Example appsettings​

This is the shape of the Spark deployment, written as JSON. In production the same values arrive as environment variables, and the three secrets never appear in a file.

appsettings.Production.json (illustrative)
{
"HostPlatform": {
"ModelDirectory": "/models",
"HuggingFaceExecutable": "/opt/hf/bin/hf",
"HuggingFaceHomeDirectory": "/data/huggingface",
"Backend": "ggml_cuda",
"ContextTokens": 8192,
"VoiceReserveGiB": 8
},
"HostAuthentication": {
"Enabled": true,
"Authority": "https://spark.example.com:9443/application/o/lucia/",
"ClientId": "<from identity provisioning>",
"ClientSecretFile": "/run/secrets/host-oidc-client-secret",
"PublicOrigin": "https://lucia.example.com",
"CaCertificatePath": "/trust/lucia-root-ca.crt",
"DataProtectionKeysDirectory": "/data/data-protection",
"TrustedProxyNetworks": [ "172.18.0.0/16" ]
},
"PackageUpdates": { "Directory": "/data/packages" },
"SparkTelemetry": { "Enabled": true, "ProcDirectory": "/host-metrics", "StoragePath": "/data" },
"InferenceKeys": { "Directory": "/data/inference-keys" },
"Assistant": { "Directory": "/data/assistant" }
}

HostPlatform__ApiKey and HostPlatform__InferenceApiKey come from files in the installation's secrets directory.

Running from source​

src/Lucia.Homelab.AppHost runs the server, Redis and the Vite frontend with Aspire. It reads three parameters, set as Aspire parameters or user secrets. Use your own values.

ParameterSetsRequired
host-api-keyHostPlatform__ApiKeyYes, 32 or more characters
inference-api-keyHostPlatform__InferenceApiKey and the Playground's key in developmentNo
huggingface-tokenHostPlatform__HuggingFaceTokenNo
dotnet user-secrets set "Parameters:host-api-key" "<owner-key-at-least-32-characters>" --project src\Lucia.Homelab.AppHost
aspire start --non-interactive

appsettings.Development.json sets HostAuthentication:Enabled to false. In Development only, the host serves its OpenAPI document and a Scalar explorer at /scalar/v1, and / redirects there.

The Identity AppHost (src/Lucia.Homelab.Identity.AppHost) reads LUCIA_IDENTITY_STATE, the path to the installation's state directory produced by tools/identity/provision.py. See Install from source.