// 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
| Source | When it is read | Precedence |
|---|---|---|
| Process environment | At startup and on demand (os.environ) | Highest |
Discovered .env files | bootstrap_env at Web UI startup, lazily before some lookups | Middle; never overrides an existing environment value |
| Persisted settings | library.json -> settings | Lowest 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:
- An explicit
OPENBOX_ENV_FILEpath, if set (read before the extra roots). - The data directory (the extra root passed to
bootstrap_env). - The parent of the data directory.
- Your home directory (
~/.env). ~/.config/openbox-game-launcher/.env.
The current working directory and the application directory are not searched. Each .env file must be an owner-only regular file (mode 0o600, no group or other permission bits), must not be a symlink, and must be under 1 MiB; anything else is skipped silently. Windows has no POSIX mode bits, so there the check rejects reparse points instead and otherwise relies on the per-user profile ACL — keep .env inside your own profile directory.
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
| Variable | Meaning |
|---|---|
OPENBOX_DATA_DIR | Data 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, and to %LOCALAPPDATA%\openbox-game-launcher on Windows. |
OPENBOX_SAFE_MODE | Any non-empty value (conventionally 1) disables plugin execution and the webhook dispatcher for the whole process. Exposed as settings.safe_mode. |
APPIMAGE | Set automatically when running from an AppImage; the Linux updater refuses to install without it. Exposed as settings.appimage. Windows updates use the verified OpenBox-<arch>-windows.zip path instead. |
OPENBOX_ENV_FILE | Explicit path to a single .env file, checked first before the data-directory roots. Read directly from the process environment; must point at an owner-only regular file (not a symlink) or it is skipped. |
OPENBOX_ALLOW_HTTP_WEBHOOKS | Set to 1 to allow plain-HTTP webhook URLs. Required only for trusted local test targets; HTTPS is the default and safer. |
OPENBOX_ALLOW_HTTP_GAMEYFIN | Set to 1 to allow plain-HTTP Gameyfin URLs. HTTPS is the default; loopback (localhost) addresses are always allowed. |
OPENBOX_ALLOW_UNSANDBOXED_PLUGINS | Set to 1 in the process shell to allow unsandboxed plugin execution when bubblewrap is unavailable. Read directly from the process environment (not .env; it is not in ENV_KEYS). |
OPENBOX_MEDIA_ROOTS | Colon-separated list (os.pathsep) of additional absolute directories approved for scanning and media storage. Up to 32 roots. |
OPENBOX_EXECUTABLE | Optional explicit path to the emulator/launcher executable recorded when importing Steam titles for Steam Bridge. Read from the process environment at scan time; when unset, the importer uses the Steam-reported executable. |
OPENBOX_ENABLE_DMABUF | Set to 1 to enable WebKitGTK DMA-BUF rendering in the Linux native window. Disabled by default to prevent silent blank windows on AMD GPUs (including Steam Deck). Windows uses WebView2 instead and ignores this. |
OPENBOX_WEBKIT_HARDWARE_ACCELERATION | WebKitGTK hardware acceleration policy in the Linux native window (always or on-demand; default is on-demand). |
OPENBOX_SNAPSHOT_DEBOUNCE | Debounce delay in seconds (float) for background library state snapshot writes (defaults to 0.0). |
OPENBOX_INSTALL_DIR | Custom installation directory used by install.sh (defaults to ~/.local/bin). On Windows the same variable, or install.ps1 -InstallDir, defaults to %LOCALAPPDATA%\OpenBox; the runtime lands in <InstallDir>\share\openbox and the previous tree is kept at <InstallDir>\share\openbox.previous. |
OPENBOX_RELEASE_TAG | Pins a specific GitHub release tag (e.g. v1.13.0) during install.sh execution. |
OPENBOX_PYTHON | Path to the Python interpreter invoked by the native host (defaults to python3; on Windows the launchers locate python.exe or py.exe on PATH unless this is set). |
OPENBOX_WEB_APP | Path to web_app.py invoked by the native host. |
OPENBOX_NATIVE_HOST | Path override for the native host binary used by openbox-native.sh (defaults to native_host beside the app). On Windows it overrides the native_host.exe binary that openbox-native.ps1 runs. |
OPENBOX_SHARE | Runtime directory override used by the Windows launchers (openbox.ps1, openbox-native.ps1) to point at the extracted runtime tree. |
OPENBOX_BUNDLED_LIB_PATH | LD_LIBRARY_PATH value used by the AppImage wrapper for bundled libraries. |
OPENBOX_ARCH | Overrides uname -m architecture detection in install.sh and build_appimage.sh (x86_64 or aarch64). |
OPENBOX_UPDATE_INFORMATION | zsync update-metadata line embedded by build_appimage.sh for the verified updater (packaging-time, not user-facing). |
OPENBOX_RELEASE_BASE | Base URL the release assets are served from, read by scripts/install.ps1 (or pass -ReleaseBase). Overrides the default GitHub release download host. |
OPENBOX_ENABLE_SQLITE_READ | Set to 1 to enable the SQLite read model (pkg/state/sqlite_readmodel.py) for accelerated search and facets on large libraries. It uses stdlib sqlite3 with FTS5 full-text search (LIKE fallback if FTS5 is unavailable). JSON remains the source of truth; SQLite is a read-only projection. Release evidence covers 10k and 20k libraries; larger collections are exploratory. Since v1.12.0 the read model also self-enables at 5,000+ games (should_auto_enable(), latched per process); an explicit 0/false/no opt-out is never overridden. (v1.7.2+) |
Windows launchers
The Windows runtime tree ships three entry points:
| Entry point | What it does |
|---|---|
openbox.cmd | Double-clickable wrapper |
openbox.ps1 | Runs the ladder: the native WebView2 window first, the browser app window second. --web forces the browser app window. |
openbox-native.ps1 | Runs native_host.exe, or falls back when it is absent |
Python is located as python.exe or py.exe on PATH, or through OPENBOX_PYTHON; OPENBOX_SHARE points the launchers at the runtime directory. native_host.exe ships compiled in the released portable install; rebuilding it from a source checkout is powershell -File scripts/build_native_host_windows.ps1 (MSVC toolchain; WebView2 SDK from the NuGet cache or nuget.org), and without native_host.exe the launchers open the same UI in a browser app window. See Windows for the install layout.
Credentials (all optional)
| Variable | Used by | Aliases |
|---|---|---|
RETROACHIEVEMENTS_USERNAME | RetroAchievements matching and progress | RA_USERNAME, OPENBOX_RA_USERNAME |
RETROACHIEVEMENTS_API_KEY | RetroAchievements web API | RA_API_KEY, RETROACHIEVEMENTS_KEY, OPENBOX_RA_API_KEY |
EMUMOVIES_USERNAME | EmuMovies media downloads | OPENBOX_EMUMOVIES_USERNAME |
EMUMOVIES_PASSWORD | EmuMovies media downloads | OPENBOX_EMUMOVIES_PASSWORD |
GITHUB_TOKEN | GitHub release API rate limit for update checks (GITHUB_TOKEN, GH_TOKEN, OPENBOX_GITHUB_TOKEN all accepted) | GH_TOKEN, OPENBOX_GITHUB_TOKEN |
IGDB_CLIENT_ID | IGDB metadata provider (Twitch developer app) | , |
IGDB_CLIENT_SECRET | IGDB metadata provider (Twitch developer app) | , |
STEAMGRIDDB_API_KEY | SteamGridDB artwork provider: search, apply, and bulk matching | OPENBOX_STEAMGRIDDB_API_KEY |
SCREENSCRAPER_USER | ScreenScraper per-ROM-hash scraping (required for that provider) | OPENBOX_SCREENSCRAPER_USER |
SCREENSCRAPER_PASSWORD | ScreenScraper per-ROM-hash scraping (required for that provider) | OPENBOX_SCREENSCRAPER_PASSWORD |
SCREENSCRAPER_DEV_ID | ScreenScraper developer credentials (optional) | OPENBOX_SCREENSCRAPER_DEV_ID |
SCREENSCRAPER_DEV_PASSWORD | ScreenScraper developer credentials (optional) | OPENBOX_SCREENSCRAPER_DEV_PASSWORD |
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. Windows has no POSIX mode bits, so there these files rely on the per-user profile ACL instead.
Persisted settings
The Settings dialog saves into library.json under settings. The save handler (SettingsHandlers._save_settings_locked in handlers/settings.py:646) 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. GET /api/settings returns the public projection of the settings, which is a curated subset of the registry (a few internal and separately-owned keys are not part of it). Notable validated limits:
| Setting | Default | Validation |
|---|---|---|
watch_folders | [] | List of at most 50 absolute, existing directories; duplicates removed |
screensaver_seconds | 90 | 0 (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 |
gamescope_preset | "" | Selected gamescope preset name (one of: deck, deck_hd, 1080p, 1440p, 4k, integer, stretch, borderless, or a custom preset name). Empty string = no preset. (v1.7.2+) |
mangohud_enabled | false | Boolean; when true, MANGOHUD=1 is set on game launch to enable the MangoHud performance overlay. (v1.7.2+) |
gamescope_custom_presets | [] | Up to 16 user-defined gamescope presets (unique names, bounded integer args). A custom name shadows a stock preset; a per-game gamescope_preset override wins over the global choice. (v1.8.0+) |
list_sort | "" | List-view sort column key (e.g. title, rating); empty = default order. (v1.8.0+) |
list_sort_dir | "" | List-view sort direction: "" (ascending) or "reversed". (v1.8.0+) |
show_insights | true | Boolean; toggles the Play Insights panel in the library pane. (v1.8.0+) |
locale | "en" | Interface language code (one of: en, es, de, fr, pt). (v1.7.2+) |
progress_automation_enabled | false | Boolean; enables/disables automatic progress changes |
progress_automation_play_minutes | 30 | 0 to 100,000, minutes before marking Playing |
progress_automation_idle_days | 30 | 0 to 3,650, days before marking Paused |
progress_on_first_play | "Playing" | Must be a known progress status |
welcome_completed | false | Boolean, suppresses opening the Library Setup Center on empty library launch |
backlog_progress_suggest | true | Boolean; kill switch for the one-time "Playing?" suggestion offered when launching an unplayed game (v1.14.0+) |
image_group | "cover" | One of cover, background, screenshot, clear_logo, fanart, banner, icon, box_back, box_spine, box_3d, title_screen, cart_front, cart_back, disc, advertisement, manual |
badge_visibility | favorite, installed, saves, documents, progress, storefront, achievements, rating | Subset 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 and optional catalog sync |
library_sync_enabled | false | Explicit opt-in for causal catalog synchronization; it does not enable the separate statistics sync automatically |
storefront_auto_import | All off | Object with boolean keys: steam, heroic, lutris, gameyfin |
auto_import_media_types | ["background", "cover", "screenshots"] | Subset of MEDIA_TYPES_ALL (18 types including cover, background, screenshots, clear_logo, box_back, manual, video, etc.) |
media_download_limit | 0 (unlimited) | 0 to 10,000 |
region_priority | (default list) | Non-empty ordered list, ranks which regional media to prefer |
video_priority | snap, theme, trailer, recording | Subset 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_mix | false | Boolean, lower music volume when mixing with video audio |
bigbox_mode | "stage" | One of stage, hybrid, coverflow |
bigbox_start_at_launch | false | Boolean; when true the UI opens straight into Big Box on startup (or unless a ?deeplink=bigbox deeplink overrides it) (v1.14.0+) |
attract_mode_seconds | 90 | Seconds 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_history | true | Boolean, when false, sessions still track but history isn't recorded |
backup_on_close | false | Boolean, creates save backups when session ends |
save_backup_limit | 10 | 0 to 500, oldest archives trimmed after each backup |
backup_auto_enabled | false | Boolean; opt-in weekly automatic library backup, checked on an hourly tick (auto_backup_due()) (v1.12.0+) |
backup_auto_keep | 4 | 1 to 52 automatic backup archives retained (v1.12.0+) |
last_auto_backup | "" | Internal timestamp of the last automatic backup; missing or unparsable counts as due — do not edit (v1.12.0+) |
quick_resume_enabled | true | Boolean; enables Quick Resume state capture and restore on state-capable adapters (v1.11.0+) |
session_recap_enabled | true | Boolean; shows the session recap card after a session ends (v1.11.0+) |
moments_autocapture | true | Boolean; automatic Moment capture on qualifying session events (v1.11.0+) |
state_retention | 1 | 1 to 20 Quick Resume states kept per game (v1.11.0+) |
memories_import_enabled | false | Boolean; opt-in import of external media into the Memories gallery (v1.11.0+) |
memories_import_roots | [] | At most 32 absolute, existing directories allowed as Memories import sources (v1.11.0+) |
steamgrid_enabled | true | Boolean; enables the SteamGridDB artwork provider (still requires STEAMGRIDDB_API_KEY) (v1.11.0+) |
scrape_after_import | true | Master toggle for the automatic post-import scrape. Owned by the metadata routes rather than the Settings dialog: read/written through GET/POST /api/v2/metadata/scrape-settings |
scrape_screenscraper_enabled | false | ScreenScraper opt-in for the post-import scrape. Requires SCREENSCRAPER_USER/SCREENSCRAPER_PASSWORD |
scrape_igdb_enabled | false | IGDB opt-in for the post-import scrape. Requires IGDB_CLIENT_ID/IGDB_CLIENT_SECRET |
scrape_steamgrid_enabled | false | SteamGridDB opt-in for the post-import scrape. Requires STEAMGRIDDB_API_KEY |
steamgrid_key_configured | (derived) | Read-only projection in public_settings: true when a SteamGridDB API key is configured (pkg/state/cache.py:488); not writable via Settings |
obs_replay_enabled | false | Boolean; enables Record That clip capture via the OBS replay buffer (v1.11.0+) |
obs_websocket_url | "" | OBS WebSocket endpoint for replay-buffer capture (v1.11.0+) |
obs_websocket_timeout | 5.0 | 0.1 to 30 seconds for OBS WebSocket calls (v1.11.0+) |
obs_websocket_password | "" | OBS WebSocket password; empty leaves the stored value unchanged (v1.11.0+) |
museum_kiosk_enabled | false | Boolean; Museum/kiosk mode with reduced interaction (v1.11.0+) |
health_rescan | "weekly" | Library health rescan cadence: daily, weekly, on_startup, or off. on_startup runs once at boot when no game session is active; off disables the hourly tick. (v1.14.0+) |
museum_kiosk_pin_hash | "" | Salted PIN hash for the kiosk convenience boundary — not API authentication (v1.11.0+) |
mood_match_enabled | false | Boolean; enables adaptive cover theming (Mood Match) from the selected artwork. (v1.9.0+) |
mood_match_bigbox | false | Boolean; extends Mood Match to the Big Box background and cover ring. Only effective while mood_match_enabled is true. (v1.9.0+) |
household_stats_sharing | false | Boolean; opt-in sharing of play statistics with Household members (v1.11.0+) |
party_queue | [] | Game Night queue of up to 50 unique game ids (v1.9.0+) |
party_players | 2 | 2 to 8 Game Night players (v1.9.0+) |
party_index | 0 | Current position in the Game Night queue (v1.9.0+) |
tracking_mode | "default" | One of default, process, original_process, folder, process_name |
tracking_delay | 0 | 0 to 600 seconds before tracking starts after spawn |
tracking_frequency | 2.0 | 0.5 to 60 seconds between poll checks |
apply_perf | "auto" | One of off, auto, always, whether TDP limits apply |
auto_close_store_clients | false | Boolean, close Steam/Heroic/Lutris clients after a session ends |
obs_auto_attach | true | Boolean, auto-attaches latest OBS recording to game |
obs_recording_path | "" | Absolute, existing path override; empty uses discovery |
dynamic_play_button | true | Boolean, shows animated PLAY state |
custom_field_defs | [] | Up to 20 fields; each 50 options max |
platform_categories | Built-in mapping | Platform-to-category overrides (Nintendo, Sony, Microsoft, Computer, Arcade, Adventure, Other) |
list_columns | Defaults | Capped at 12 columns per platform or global view |
library_view | "grid" | Current persistent view preference |
hidden_sidebar_sections | [] | Capped at 20 entries |
tray_enabled | false | Boolean, shows system tray icon |
minimize_to_tray | false | Boolean, minimizes to tray instead of closing |
show_playlist_actions | true | Boolean, 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 |
cover_grouping | "shape" | String; shape used to group covers in the library |
image_group_by_platform | {} | Object; per-platform image group overrides |
image_group_by_playlist | {} | Object; per-playlist image group overrides |
sidebar_sections | ["search", "view", "platforms", "playlists", "filters"] | List of strings; valid section names: search, view, categories, esrb, platforms, playlists, presets, explorer |
platform_documents | {} | Object; per-platform manual/document lists |
filter_presets | [] | List of objects; named filter rules, each needs at least one rule |
import_exclusions | [] | List of objects with source (steam, heroic, lutris, gameyfin) and external_id |
emulator_scan_configs | [] | List of objects; per-emulator scan configuration |
tracking_process_name | "" | String; process name used when tracking_mode is process_name |
webhook_attempts | 3 | 1 to 5 delivery retries per webhook |
webhook_timeout | 5 | 1 to 15 seconds before delivery times out |
webhooks | [] | At most 32 configs; each needs a URL and at least one event |
theme | "" | String; global theme name, empty uses the stock theme |
theme_by_platform | {} | Object; platform to theme name mappings |
bigbox_quick | (auto-managed) | Derived list; presets flagged as Big Box quick actions, capped at 8 |
controller_prompt_pack | "xbox" | String; active controller prompt pack (xbox, playstation, nintendo) |
controller_prompt_hint | false | Boolean; toggles on-screen controller button prompts in Big Box |
active_media_packs | [] | List of strings; media pack ids, appended when a pack is applied |
last_cloud_sync | "" | Internal, auto-managed timestamp; do not edit |
last_update_check | "" | Internal, auto-managed timestamp; do not edit |
gameyfin_password_set | false | Internal, boolean indicator of whether password is set |
gamescope_guest | false | Internal, auto-detected guest mode under Gamescope |
Two naming details worth knowing:
bigbox_shutdown_commandsis misnamed: the commands run when Big Box is entered, not when it is left.attract_mode_secondsis the screensaver delay the Big Box screensaver actually reads; it falls back toscreensaver_secondswhen 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).envbootstrap (performed once per process)startup_commands(run after the server binds, viarun_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.envfile is plaintext. - Restart OpenBoxGL after changing values consumed at process startup.
The authoritative sources are .env.example, env_config.py, handlers/settings.py, settings_schema.py, and the Settings dialog.