// OPENBOX DOCS

Configuration

Configure environment values, local state, and application settings.

OpenBoxGL reads configuration from three places, in order: the process environment, discovered .env files, and persisted application settings (the Settings dialog). This page documents each source, the exact variables, and the validation limits applied when settings are saved.

Configuration sources

SourceWhen it is readPrecedence
Process environmentAt startup and on demand (os.environ)Highest
Discovered .env filesbootstrap_env at Web UI startup, lazily before some lookupsMiddle; never overrides an existing environment value
Persisted settingslibrary.json -> settingsLowest for credentials; the only store for UI settings

A .env file sets an environment variable only when that variable is not already set, so the process environment always wins. Unreadable or non-UTF-8 .env files are skipped silently; an optional .env must never abort startup.

.env discovery order

env_config.discover_env_files checks these roots in order and loads every .env found:

  1. The OPENBOX_DATA_DIR you passed in (used as an extra root).
  2. The parent of the data directory.
  3. The current working directory.
  4. The application directory (next to web_app.py).
  5. Your home directory (~/.env).
  6. ~/.config/openbox-game-launcher/.env.

Values already in the environment are never overridden by .env. The template lives at .env.example in the repository. Put real secrets in ~/.env or ~/.config/openbox-game-launcher/.env only, never in a tracked file.

Environment variables

Data and process behavior

VariableMeaning
OPENBOX_DATA_DIRData directory. Read at import time, before .env bootstrap; must be exported in the shell, desktop entry, or systemd unit before launch. Defaults to ~/.local/share/openbox-game-launcher.
OPENBOX_SAFE_MODEAny non-empty value (conventionally 1) disables plugin execution and the webhook dispatcher for the whole process. Exposed as settings.safe_mode.
APPIMAGESet automatically when running from an AppImage; the updater refuses to install without it. Exposed as settings.appimage.
OPENBOX_ALLOW_HTTP_WEBHOOKSSet to 1 to allow plain-HTTP webhook URLs. Required only for trusted local test targets; HTTPS is the default and safer.

Credentials (all optional)

VariableUsed byAliases
RETROACHIEVEMENTS_USERNAMERetroAchievements matching and progressRA_USERNAME, OPENBOX_RA_USERNAME
RETROACHIEVEMENTS_API_KEYRetroAchievements web APIRA_API_KEY, RETROACHIEVEMENTS_KEY, OPENBOX_RA_API_KEY
EMUMOVIES_USERNAMEEmuMovies media downloadsOPENBOX_EMUMOVIES_USERNAME
EMUMOVIES_PASSWORDEmuMovies media downloadsOPENBOX_EMUMOVIES_PASSWORD
GITHUB_TOKENGitHub release API rate limit for update checksGH_TOKEN, OPENBOX_GITHUB_TOKEN
IGDB_CLIENT_IDIGDB metadata provider (Twitch developer app)
IGDB_CLIENT_SECRETIGDB metadata provider (Twitch developer app)

Each credential lookup checks the aliases in order using env_value() from env_config.py — it iterates through the listed names for a variable and uses the first non-empty value found. If an empty string is returned for any required variable, the route returns a specific 400 error naming exactly which variable is missing. For example, IGDB requires both IGDB_CLIENT_ID and IGDB_CLIENT_SECRET; without either the IGDB routes return 400 {"error":"Set IGDB_CLIENT_ID and IGDB_CLIENT_SECRET in ~/.env to use IGDB."}. Without RetroAchievements credentials, the /api/ra/* routes return 400 {"error":"Configure RetroAchievements first."}.

Credentials supplied through the Settings dialog are persisted in the data directory (retroachievements.json, emumovies.json, settings.json for Gameyfin) with owner-only permissions (0o600 via secure_text_write). The API and diagnostic log redact the values, but the files themselves are plaintext.

Persisted settings

The Settings dialog saves into library.json under settings. The save handler (web_app._save_settings_locked) validates each field before committing; an invalid value aborts the whole save with a 400 error. Keys must exist in the settings_schema.py registry: unknown keys are dropped with a diagnostic log warning rather than persisted. The full key list is exposed by GET /api/settings. Notable validated limits:

SettingDefaultValidation
watch_folders[]List of at most 50 absolute, existing directories; duplicates removed
screensaver_seconds900 (off) or between 30 and 3600; values 1-29 are rejected
controller_map{}Actions limited to play, back, favorite, random, page_left, page_right, pause, menu; button numbers 0–31
progress_automation_enabledfalseBoolean; enables/disables automatic progress changes
progress_automation_play_minutes300 to 100,000 — minutes before marking Playing
progress_automation_idle_days300 to 3,650 — days before marking Paused
progress_on_first_play"Playing"Must be a known progress status
welcome_completedfalseBoolean — hides the welcome wizard
image_group"cover"One of cover, background, screenshot, clear_logo, fanart, banner, icon, box_back, box_spine, box_3d, title_screen
badge_visibilityMost badges shownSubset of favorite, installed, missing_media, saves, documents, versions, storefront, achievements, highscores, progress, rating, broken, portable, controller
cloud_folder""Absolute, existing path for mounted-folder statistics sync
storefront_auto_importAll offObject with boolean keys: steam, heroic, lutris, gameyfin
auto_import_media_typesAll typesSubset of cover, background, screenshots — applied during import-time media jobs
media_download_limit0 (unlimited)0 to 10,000
region_priority(default list)Non-empty ordered list — ranks which regional media to prefer
video_prioritysnap, theme, trailer, recordingSubset of video_snap, video_theme, video_trailer, video_recording, video
library_music""Path to existing audio file; empty disables Big Box library BGM
video_bgm_mixfalseBoolean — lower music volume when mixing with video audio
bigbox_mode"stage"One of stage, hybrid, coverflow
attract_mode_seconds90Seconds of idle before screensaver/attract mode triggers
bigbox_startup_video""Empty string (disabled) or path to startup video file
bigbox_shutdown_commands[]At most 25 commands; run on entering Big Box (see note below)
startup_commands[]At most 25 commands; run after server binds
shutdown_commands[]At most 25 commands; run on graceful exit
track_session_historytrueBoolean — when false, sessions still track but history isn't recorded
backup_on_closefalseBoolean — creates save backups when session ends
save_backup_limit100 to 500 — oldest archives trimmed after each backup
tracking_mode"default"One of default, process, original_process, folder, process_name
tracking_delay00 to 600 seconds before tracking starts after spawn
tracking_frequency2.00.5 to 60 seconds between poll checks
apply_perf"auto"One of off, auto, always — whether TDP limits apply
auto_close_store_clientsfalseBoolean — close Steam/Heroic/Lutris clients after a session ends
obs_auto_attachtrueBoolean — auto-attaches latest OBS recording to game
obs_recording_path""Absolute, existing path override; empty uses discovery
dynamic_play_buttontrueBoolean — shows animated PLAY state
custom_field_defs[]Up to 20 fields; each 50 options max
platform_categoriesBuilt-in mappingPlatform-to-category overrides (Nintendo, Sony, Microsoft, Computer, Arcade, Adventure, Other)
list_columnsDefaultsCapped at 12 columns per platform or global view
library_view"grid"Current persistent view preference
locale"en""en" only in 0.9.0; localization returns in a future release. Unknown locales fall back to English
hidden_sidebar_sections[]Capped at 20 entries
tray_enabledfalseBoolean — shows system tray icon
minimize_to_trayfalseBoolean — minimizes to tray instead of closing
show_playlist_actionstrueBoolean — show add/remove playlist buttons
gameyfin_url""Prepends http:// when no scheme present; must resolve
gameyfin_username""Stored as plain text alongside url and password
gameyfin_password""Empty value leaves stored password unchanged; otherwise persisted with 0o600
gameyfin_install_dir""Absolute path; created if missing, rejected if symlink or non-existent. Empty by default; the UI shows a ~/Games/Gameyfin placeholder
gameyfin_provider""Provider label; falls back to first available
ludusavi_backup_path""Optional absolute path for Ludusavi JSON output
ui_window"app"One of app, browser — whether the UI opens chrome-less or in a browser tab; overridden by --app-window / --no-app-window

Two naming details worth knowing:

  • bigbox_shutdown_commands is misnamed: the commands run when Big Box is entered, not when it is left. The entering: false signal from the UI is ignored by the handler.
  • attract_mode_seconds is the screensaver delay the Big Box screensaver actually reads; it falls back to screensaver_seconds when unset, and the Settings dialog keeps both fields with the same fallback.

Partial saves merge with existing settings: keys you omit are preserved, and concurrent partial saves of different keys do not lose updates (covered by the API sweep tests). An empty gameyfin_password in a save leaves the stored password unchanged.

Values consumed at startup

These are read once during process startup and require a restart to change:

  • OPENBOX_DATA_DIR (data directory and state store path)
  • server.token / server.port (per-launch, deleted on exit)
  • .env bootstrap (performed once per process)
  • startup_commands (run after the server binds, via run_configured_commands)
  • Profile merging from emulator definition packs (merge_profiles_from_definitions) at startup

shutdown_commands run on graceful exit. Both command lists execute with shlex.split and a new session; failures are logged and skipped, never fatal.

Security notes

  • Public examples must use placeholders. Never commit .env, server.token, retroachievements.json, emumovies.json, or library exports.
  • Keep server.token, provider credentials, and webhook secrets private. The diagnostic log redacts values matching token/password/secret/API-key/authorization patterns, but a .env file is plaintext.
  • Restart OpenBoxGL after changing values consumed at process startup.

The authoritative sources are .env.example, env_config.py, the settings validation in web_app.py, and the Settings dialog.