Skip to contents

Every EPISODIC_* variable is optional; each entry point works fine with the equivalent argument passed explicitly instead. Environment variables exist so an operator can configure a running instance (a systemd unit, a Docker container) without editing R code - set them once where the instance runs, and every entry point that falls back to them picks the setting up automatically.

Variable Used by Meaning
EPISODIC_DB episodic_run_app(), episodic_add_user() (db_path argument) Path to the instance’s SQLite database, or a mysql:// DSN pointing at a MariaDB/MySQL database instead - see “Database backend” above.
EPISODIC_LANGUAGE every lang argument - episodic_run_app(), episodic_demo(), and every renderer beneath them Dashboard/report language: en, ar, nl, fr, de, hi, zh, or es, or one of the regional variants en-US and es-419 (Latin American Spanish). en is British English and es is Spain’s Spanish, so en-GB and es-ES name those exactly and are accepted as such. Case and separator do not matter (en_us is en-US). Defaults to en if unset. A region EpiSODIC does not ship falls back to the language itself, with a warning saying so once (nl-BE renders in Dutch); anything else falls back to en with a warning, once, not on every render. Also sets the document’s lang and dir, so Arabic renders right to left. Fixed for the whole running app - there is no in-app language switcher.
EPISODIC_CONFIG episodic_run_cron() (episodic_config_path argument), episodic_run_app() Path to an instance override of configuration (pathogen thresholds, same_place/rare_trigger/Farrington settings, notification channels, and access:
  require_login
), overlaid key-by-key on the shipped inst/config/episodic_default_config.yaml. Set to a path that does not exist, or to a file that is not valid YAML, this stops with an error rather than falling back to the defaults; every key it sets is checked against the shipped defaults, so a misspelled key or a value of the wrong type is named rather than silently ignored. See Tuning detection for your catchment for the complete reference table of tunable keys, and Deployment’s “Opening the app to anonymous visitors” for access:
  require_login
.
EPISODIC_PATHOGEN_CONFIG episodic_run_cron() (pathogen_config_path argument) Path to an instance override of per-pathogen parameters (episode length, case-free days, severity weight, MEM mode, serial interval, and the rest), overlaid row-by-row on the shipped inst/config/episodic_default_pathogen_config.csv. Write a CSV with a pathogen column and only the columns you want to change; non-empty values override the shipped default for that pathogen and column, empty cells keep it, and pathogens not in your file keep their shipped row entirely. New pathogens are added. Set to a path that does not exist, this stops with an error. See Tuning detection for your catchment for the column reference.
EPISODIC_STYLE episodic_palette() (palette_config_path argument) Path to an instance override of the UI colour palette (including the five severity_* roles that colour verdicts and MEM intensity bands alike) and typography (font, font_size_base), overlaid key-by-key on the shipped inst/config/episodic_default_style.yaml. The same palette styles the outbreak reports and the emails EpiSODIC sends. Each value is checked: a colour must be a quoted hex colour, font a CSS font-family stack and font_size_base a CSS length. A value that fails keeps the shipped value for that role only, and a path that does not exist or a file that is not valid YAML keeps the whole shipped palette - neither stops the dashboard, a report or an email, and both are shown on the Info screen, written to every run’s log and raised once as a warning. Deliberately separate from EPISODIC_CONFIG: colour and typography are display concerns, never part of the detection-reproducibility guarantee.
EPISODIC_GEO_DATA episodic_geo_source_resolve() (path argument) Path to an .rds file holding your own geographic reference data (an sf object with pc/geometry columns) for the choropleth map - any country, any area unit. There is no default: with this unset the dashboard shows its bar-chart fallback instead of a map, since a default map is a default country. EpiSODIC does bundle one worked example (Dutch postcodes) that episodic_demo() points this at. See Getting your data in’s “Geographic reference data” section.
EPISODIC_GEO_DATA_OVERLAY episodic_geo_overlay_resolve() (path argument) Path to an .rds file holding an optional region-outline overlay (an sf object with just a geometry column), drawn on top of the choropleth. No default. See Getting your data in’s “Geographic reference data” section.
EPISODIC_PC_PROVINCE_MAP episodic_pc_to_province() (path argument) Path to a CSV (pc, province_code columns) mapping your postcodes to provinces/states/regions, for L4 stream detection and for naming the province beside each postcode in the dashboard. Unset, no postcode resolves to a province and L4 simply has nothing to detect on - there is no built-in rule, because deriving a province from a postcode is country-specific; set but unusable (no such file, unreadable, no rows, wrong columns, or a repeated pc) stops the run and says which of those it is, rather than falling back. See Getting your data in’s “Geographic reference data” section.
EPISODIC_QUARTO_REPORT episodic_report_render() (qmd_path argument) Path to an operator’s own Quarto report template, overriding the shipped inst/report/episodic_default_report.qmd. Set to a path that does not exist, this stops with an error rather than silently sending reports out under EpiSODIC’s own layout instead of yours. See “Custom report templates” above.
EPISODIC_MAIL_TEMPLATE_NEW_CLUSTERS episodic_run_cron(), episodic_mail_template_preview() Path to an operator’s own Quarto (.qmd) template for the body of the email sent when a run detects new clusters. Unset, the built-in body is used. Only the email channels (smtp, sendmail, microsoft365) are affected; ntfy, Teams and Slack keep their own messages. A template that cannot be used (no such file, not a .qmd, Quarto not installed, or a render that fails) never costs an alert: the email goes out with the built-in body, and the run log, the Info screen and episodic_notify_test() say why. See Notifications’s “Your own email layout”.
EPISODIC_MAIL_TEMPLATE_REPORT episodic_run_cron(), episodic_mail_template_preview() The same, for the body of the email a scheduled outbreak report is attached to. See Scheduled reports.

None of these need to be set to run the demo - episodic_demo() uses a temporary SQLite file and the shipped defaults, and writes its own geography and an illustrative pathogen overlay beside it. See Deployment for EPISODIC_DB and EPISODIC_CONFIG/EPISODIC_STYLE in context (database backend, custom report templates), and Getting your data in’s “Geographic reference data” section for the detail behind EPISODIC_GEO_DATA and EPISODIC_GEO_DATA_OVERLAY.

Checking what they actually delivered

Setting a variable and seeing nothing change on screen has, until now, been indistinguishable from setting it wrongly. The dashboard’s Info screen carries a “Reference data” panel that resolves each of these live and reports, per variable, whether the instance’s own file is in use, the shipped default is standing in, nothing is configured, the file was configured and could not be used (with the reason), or an optional package the feature needs is missing - alongside what it put into the app: how many areas the map geometry holds, how many postcodes the province mapping maps and to how many provinces, and how many of the distinct postcodes in your own case data resolve to a province.

That last figure is the one worth reading first when a province is not appearing where you expect: a mapping read successfully but matching none of your postcodes looks, on every other screen, exactly like a mapping that was never read.

The resolved file paths are shown only to a signed-in user. By default the Info screen itself needs no sign-in, so the statuses and counts - the part that answers “is my file being used” - are readable without one, while the paths, which say more about the machine than a visitor needs, are not. On an instance with access.require_login set the whole screen is behind the sign-in like every other, so a signed-in reader sees the paths there too.