Skip to Content
ConfigurationConfiguration Sources & Priority

Configuration Sources & Priority

SeqDesk resolves configuration from four sources. This page states the order precisely, explains the two-stage merge that produces it, and — most usefully — lists which subsystem actually reads the resolved value, because several do not.

Priority order

From highest to lowest priority:

PrioritySourceLabel in the APIBest for
1 (highest)Environment variables (SEQDESK_*)envSecrets, per-deployment overrides, CI
2Config file (settings.json)fileReproducible non-secret deployment description
3Database (SiteSettings, edited in the Admin UI)databaseDay-to-day operational changes
4 (lowest)Built-in defaultsdefaultSensible starting values

How the merge actually runs

Resolution happens in two stages, and knowing that explains most of the odd behaviour operators run into.

Stage 1 — the file/env layer. The loader walks the working directory for the first config file it finds, parses it, reads the SEQDESK_* variables it knows about, and deep-merges:

defaults ← config file ← environment variables

Deep-merge means objects are combined key by key rather than replaced wholesale. Setting pipelines.execution.slurm.queue in the file leaves the other slurm.* defaults intact. Arrays are replaced, not merged: a file-level sequencingFiles.extensions overrides the whole default list.

Stage 2 — the database layer. The SiteSettings row is read and slotted underneath the result of stage 1. A database value is used only where stage 1 produced nothing but a default. Where stage 1 produced env or file, the database value is discarded and the source label stays env or file.

The final object plus its source map is cached in-process for 60 seconds. Saving through the Admin UI clears that cache for the process that handled the request; other worker processes pick the change up when their cache expires.

The source map only covers keys that have a default

Source labels are produced by walking the built-in default configuration and asking, for each leaf it finds, whether the file or the environment supplied a value. A key that does not exist in the defaults therefore never gets a source label at all.

This matters because pipeline execution deliberately ignores any value whose source is not file or env — that is how it keeps the Admin UI in charge of settings you have not pinned in a file. Combine the two rules and one key falls through the gap:

pipelines.execution.slurm.options has no built-in default, so it never receives a source label, so the pipeline executor drops it. Extra sbatch flags set in settings.json are silently ignored. There is also no SEQDESK_SLURM_OPTIONS variable. Set extra SLURM options in Settings → Infrastructure → Pipeline Runtime, or at install time with SEQDESK_EXEC_SLURM_OPTIONS, both of which write SiteSettings.extraSettings.pipelineExecution.slurmOptions.

Every other key documented in the Config File Reference that the executor reads (mode, runDirectory, slurm.enabled, slurm.queue, slurm.cores, slurm.memory, slurm.timeLimit, conda.path, conda.environment, conda.cacheDir, pipelines.databaseDirectory) does have a default and does work from a file or the environment.

Who reads the resolved configuration

This is the table to check first when a setting does not appear to do anything. “Resolved config” means the merged object described above; “SiteSettings” means the feature reads the database row directly and never consults the merge.

AreaRead fromNotes
Pipeline execution (mode, SLURM, Conda, run/database directories)Resolved config over SiteSettings.extraSettings.pipelineExecutionFile/env values win; anything else falls back to the admin-saved values
Which pipelines are offeredPipelineConfig rows, then SiteSettings.extraSettings.installProfilePipelineAllowlistpipelines.enabled in the config file is an install-time input, not this
site.dataBasePathResolved config if its source is env/file, else SiteSettings.dataBasePathSee Data Storage
Notifications (relay + in-app)Resolved configAlso requires the Email Notifications module to be enabled
TelemetrySiteSettings.extraSettings.telemetry over resolved configInverted precedence — see below
ENA credentials and test modeSiteSettings.enaUsername / enaPassword / enaTestModePasswords are encrypted at rest
Sequencing file discovery (extensions, scan depth, ignore patterns, auto-assign)SiteSettings.extraSettings.sequencingFiles
Registration policy (auth.allowRegistration)SiteSettings.extraSettings.auth
Access policy (access.*)SiteSettings.extraSettings and SiteSettings.postSubmissionInstructions
Optional module settings (moduleSettings.*)SiteSettings.extraSettings.accountValidationSettings / billingSettings
Runtime secrets (DATABASE_URL, NEXTAUTH_SECRET, …)process.env, seeded once at start from settings.json runtimeRestart required

For every row whose source is SiteSettings, the matching settings.json section is still meaningful — but as an install-time input. The installer, --reconfigure, a hosted install profile, and Settings → Infrastructure → Import settings.json all read those sections and write them into the database. After that, the database is authoritative.

The telemetry exception

Telemetry deliberately inverts the order: a value stored in SiteSettings.extraSettings.telemetry wins over telemetry.* from settings.json or SEQDESK_TELEMETRY_ENABLED. The intent is that a facility admin can always turn the heartbeat off from the UI without editing files.

Above all of that sits a kill switch: SEQDESK_TELEMETRY_DISABLED=true forces telemetry off before any source is consulted. See Operational Telemetry.

When to use each source

Environment variables

Use them for secrets (DATABASE_URL, NEXTAUTH_SECRET, SEQDESK_NOTIFICATION_RELAY_TOKEN), for values that differ between staging and production, and for anything your orchestration already injects. They win over everything else, which makes them the right tool for “this box is different” and the wrong tool for “this is how the facility works”.

Full list: Environment Variables.

The config file

Use settings.json for the reproducible description of a deployment: port, site identity, data paths, pipeline execution policy, and the install-time inputs that seed the database. It is the artefact you can put under version control (with secrets stripped) and hand to a colleague to rebuild the same facility.

Discovery order, in the working directory SeqDesk starts in:

  1. settings.json — canonical
  2. seqdesk.config.json — legacy/development fallback
  3. .seqdeskrc
  4. .seqdeskrc.json

The first file found wins; the others are not read. Full key reference: Config File Reference.

The database (Admin UI)

Use the Admin UI for day-to-day operational change: ENA credentials, file discovery rules, access policy, module toggles, pipeline runtime tuning. It is the only source a non-technical admin can edit, and for several features it is the only source that is read at all.

The trade-off is that a value pinned in settings.json or the environment makes the corresponding UI field cosmetic — it saves, but the pinned value keeps winning. See Runtime Settings.

Inspecting the active configuration

Facility admins can read the resolved configuration and its source map on the Platform Info page (sidebar Settings → Info, /admin/settings) under Configuration Sources, or directly:

GET /api/admin/config/status

The endpoint requires a FACILITY_ADMIN session and returns 401 otherwise. The response has four fields:

FieldMeaning
configThe resolved configuration, with secrets replaced by ********
sourcesMap of dotted config path to env, file, database, or default
filePathAbsolute path of the config file that was loaded, or absent if none was found
loadedAtWhen this resolution was performed

Masked fields are ena.password, notifications.relayToken, and the runtime secrets nextAuthSecret, anthropicApiKey, adminSecret, and blobReadWriteToken. A masked value tells you the secret is set, not what it is.

Worked example

A facility runs pipelines on SLURM, keeps the Webin password out of files, and lets admins tune the queue from the UI.

/opt/seqdesk/settings.json (mode 600):

{ "site": { "name": "HZI Sequencing Core", "dataBasePath": "/mnt/sequencing/data" }, "pipelines": { "enabled": true, "execution": { "mode": "slurm", "runDirectory": "/mnt/sequencing/pipeline_runs", "conda": { "path": "/net/software/conda", "environment": "seqdesk-pipelines", "cacheDir": "/net/software/conda-cache" } } } }

The systemd unit adds only secrets:

DATABASE_URL=postgresql://seqdesk:...@db.internal:5432/seqdesk?schema=public NEXTAUTH_SECRET=... SEQDESK_NOTIFICATION_RELAY_TOKEN=...

The resulting source map:

PathValueSourceEditable in the Admin UI?
site.nameHZI Sequencing CorefileNo — the file wins
site.dataBasePath/mnt/sequencing/datafileNo — the file wins
pipelines.execution.modeslurmfileNo — the file wins
pipelines.execution.slurm.queuecpudefaultYes — nothing pins it, so the admin-saved value applies
pipelines.execution.slurm.cores4defaultYes
ena.testModetruedefaultYes — and the UI value is what submissions use

The deliberate choice here is to pin the things that must not drift (paths, execution target) and leave queue sizing to the people watching the cluster.

When a setting appears to be ignored

Work through these in order.

1. Check the source label. GET /api/admin/config/status and look up the dotted path in sources. If it says env or file, the Admin UI is not the thing to change. If it says default when you expected file, the file is not being read — check filePath in the same response.

2. Check that the file is where SeqDesk looks. The loader searches the process working directory. In a release-layout install that is <root>/current, which holds a symlink to <root>/settings.json. A file you created at <root>/current/settings.json by hand replaces that symlink and will be lost at the next update.

3. Check the file parses. Invalid JSON is not fatal: the loader logs a warning to the server log and continues with environment variables, the database, and defaults. filePath will still be reported, so a config file that “is found but does nothing” is almost always a JSON syntax error. Validate with node -e "JSON.parse(require('fs').readFileSync('settings.json','utf8'))".

4. Check the 60-second cache. A database change made in one process is not visible in another until its cache expires. Wait a minute before concluding anything.

5. Check whether the feature reads the resolved config at all. Consult the consumer table above. ena.*, access.*, auth.*, sequencingFiles.* and moduleSettings.* in settings.json are install-time inputs; on a running server the corresponding feature reads the database. To push them into the database on an existing install, use Settings → Infrastructure → Import settings.json or re-run the installer with --reconfigure.

6. Check for a restart-only value. runtime.databaseUrl, runtime.directUrl, runtime.nextAuthUrl and runtime.nextAuthSecret are copied into process.env once at process start, and only if the corresponding variable is not already set. Editing them requires a restart, and an environment variable of the same name always wins.

Best practices

  • Keep secrets out of committed files. settings.json is written mode 600 by the installer; keep it that way.
  • Version-control a sanitised template, and generate the populated file from your deployment automation or a hosted install profile.
  • Pin only what must not drift. Every value you pin in a file removes a knob from the Admin UI.
  • Write down why you pinned something. JSON has no comments; keep the rationale in the automation that generates the file.