Repository navigation
Releases: different-name/steam-config-nix
Release list
v0.6.0
Apps can now publish a systemd user target that is active while they run, Source engine console configs can be patched, and Steam's per-app download behaviour can be set.
Systemd integration
systemd.enable publishes a user target that is active while the app runs, so units can be started and stopped with it:
programs.steam.config.apps."VRChat" = {
id = 438100;
systemd.enable = true;
};
systemd.user.services.oscleash = {
Unit.PartOf = [ "steam-app-vrchat.target" ];
Service.ExecStart = lib.getExe pkgs.oscleash;
Install.WantedBy = [ "steam-app-vrchat.target" ];
};A shared steam-app.target is active while any app with systemd.enable runs, and systemd.scope.properties sets properties on the scope the app runs in, for resource control.
See Systemd Integration.
Source engine config patching
files.<game|prefix>.patch gained a sourceConvars format, for an autoexec.cfg or any other console config, shared across GoldSrc, Source and Source 2 games:
programs.steam.config.apps."Some Game" = {
id = 1234560;
files.game.patch."cfg/autoexec.cfg" = {
format = "sourceConvars";
content.fps_max = 400;
};
};See Game Files & Mods.
Download behaviour while an app runs
allowDownloadsWhileRunning sets whether Steam may download other apps while this one runs:
programs.steam.config.apps."Some Game" = {
id = 1234560;
allowDownloadsWhileRunning = "never";
};See Steam App Settings.
v0.5.0
Files can now be placed, removed, and patched in a game's install directory or Proton prefix, launch options moved to the app root, and per-app dllOverrides compile into a single WINEDLLOVERRIDES. The documentation moved to a dedicated site.
Important
This release contains breaking changes. See Migrating from 0.4.x below.
Patching config files
apps.<name>.files.<game|prefix>.patch merges keys into a config file the game generates, leaving keys you did not set untouched. Five formats are supported: ini, json, registry (a Wine system.reg/user.reg), keyvalue (a Valve KeyValues/VDF file), and unityPrefs (Unity PlayerPrefs in a Wine user.reg, with each key hashed the way Unity does).
programs.steam.config.apps."Some Game" = {
id = 1234560;
files.game.patch."Engine/Config/BaseEngine.ini" = {
format = "ini";
content.SystemSettings."r.Tonemapper.Sharpen" = 2;
};
files.prefix.patch."user.reg" = {
format = "unityPrefs";
content."Software/ExampleCo/ExampleGame".MasterVolume = 0.8;
};
};- The file is read, merged, and written back at activation, so it works with files that only exist after the game has run once.
- A patch waits for the game to create the file by default (retrying on the next activation). Set
createIfMissing = trueto write it with just your keys. - Registry key paths accept
/or\\as the separator, so"Software/ExampleCo/ExampleGame"and"Software\\ExampleCo\\ExampleGame"are equivalent. - Each patch is backed up and restored when you remove it.
Game files rework
files and removeFiles are replaced by a single location-first namespace, apps.<name>.files.<game|prefix>.<place|patch|remove>, where game is the install directory and prefix is the Proton prefix.
files.game.place = {
"BepInEx/plugins/MoreCompany.dll".source = ./MoreCompany.dll;
"config/user.cfg" = { source = ./user.cfg; mode = "seed"; };
};
files.game.remove = [ "movies/intro.bik" ];- Each
placeentry takes amode:enforce(re-apply every activation, the default),seed(write once then leave it editable), orlock(write it and make it read-only). This replacesoverwriteChanges. - File operations always wait for the running game to exit before applying, and never close Steam.
onSteamRunningnow governs only Steam's own config writes.
Flattened launch options
The launch knobs moved from the launchOptions submodule to the app root, and launchOptionsStr became rawLaunchOptions, which now composes with the structured knobs instead of replacing them.
apps."Cyberpunk 2077" = {
id = 1091500;
args = [ "--launcher-skip" ];
dllOverrides = { winmm = "n,b"; version = "n,b"; };
};Per-app dllOverrides is an attrset of dll -> mode that compiles into a single WINEDLLOVERRIDES, so several sources merge without clobbering each other.
Other changes
displayRatesAsBitsreports Steam download rates in bits instead of bytes.- Compat tools that nest
compatibilitytool.vdfunderbin/(such as chaotic's proton-cachyos) now resolve correctly. artwork.iconis available on non-Steam apps only.- Documentation moved to a dedicated mdBook site, linked from the readme, replacing the in-repo
options.md.
Internals (no configuration impact)
- The patcher's Python was modernised (PEP 695 type aliases and generics, Python 3.13) and gained a mypy flake check.
- Patch renderers live in their own
formatspackage. - Added CI running
nix flake checkwith a fast formatting job, plus a workflow that updates flake inputs.
Migrating from 0.4.x
Most renames still work with a deprecation warning this release and will be removed in the next one. A few changes need manual action.
Removed (no longer accepted):
closeSteam->onSteamRunninglaunchOptions.extraConfig->preHookusers-> per-app configuration underapps
files.prefix placement has no alias and must be updated by hand (it reports an unknown option):
# before
files.prefix."drive_c/users/steamuser/AppData/mod.xml".source = ./mod.xml;
# after
files.prefix.place."drive_c/users/steamuser/AppData/mod.xml".source = ./mod.xml;Renamed (the old form still works with a warning):
# launch options
launchOptions.env.FOO = "bar"; -> env.FOO = "bar";
launchOptions.args = [ ... ]; -> args = [ ... ];
launchOptionsStr = "..."; -> rawLaunchOptions = "...";
# files
files.install."x".source = ...; -> files.game.place."x".source = ...;
removeFiles.install = [ ... ]; -> files.game.remove = [ ... ];
removeFiles.prefix = [ ... ]; -> files.prefix.remove = [ ... ];
# place mode (overwriteChanges = false is now mode = "seed")
files.install."x" = { source = ...; overwriteChanges = false; };
-> files.game.place."x" = { source = ...; mode = "seed"; };v0.4.0
v0.4.0
Game file management
New apps.<name>.files and apps.<name>.removeFiles options to declaratively place and remove files in a game's install directory or Proton prefix, for mods, asset replacements, loaders, and config.
programs.steam.config.apps."Lethal Company" = {
id = 1966720;
files.install = {
"BepInEx/plugins/MoreCompany.dll".source = ./MoreCompany.dll;
"movies/intro.bik".text = ""; # stub a file
"BepInEx/config/mod.cfg" = { source = ./mod.cfg; overwriteChanges = false; };
};
removeFiles.install = [ "unwanted.dll" ];
};- A
sourcemay be a file or a directory (copied recursively and merged with what is already there). - Everything is tracked and reversible: removing an entry restores replaced and removed files from a backup, and deletes files that were newly created.
overwriteChanges = falsewrites a file once and then leaves it alone, for configs the game rewrites in place.files.prefix/removeFiles.prefixtarget the Proton prefix (compatdata/<id>/pfx).- File operations respect
onSteamRunningfor a running game (wait, close, force-close, skip) and never close Steam on their own.
See the Game Files & Mods section of the readme for the full behavior and caveats.
Other changes
- Desktop entries now use each app's Steam library icon by default instead of the generic Steam icon.
- The generated launch script now includes the game's name for readability.
shortcuts.vdfis now created when missing, so non-Steam apps can be added to a fresh Steam install.
v0.3.0
v0.3.0
A large release that reworks the internals and adds a lot of new configuration surface. The Steam config files are now handled by an in-repo VDF implementation, and per-app configuration has grown well beyond launch options and compat tools.
Important
This release contains breaking changes. See Migrating from 0.2.x below.
New features
Compatibility tools as packages
compatTool and defaultCompatTool now accept a package as well as an internal tool name, and the package is installed for you (NixOS via programs.steam.extraCompatPackages, Home Manager via a link into compatibilitytools.d). Proton versions that aren't packaged can be pulled straight from their release archives with fetchzip.
compatTool = pkgs.proton-ge-bin;Library artwork
Set Steam library artwork per app: cover, header, hero, logo for any app, plus icon for non-Steam apps. Sources can be local files or fetched (e.g. from SteamGridDB).
Desktop entries
Generate .desktop launchers that start a game through Steam, per app (desktopEntry.enable) or for everything at once (desktopEntries = true).
Winetricks
Install winetricks verbs into an app's Proton prefix with winetricks = [ "vcrun2022" "corefonts" ]. Applied at launch via protontricks, guarded so it only runs when the verb list changes. Since the first apply can take a while, a desktop notification is shown while it runs (toggle with notifications).
More per-app Steam settings
betaBranch: opt an app into a beta branchlanguage: set an app's languageupdateBehavior:"always","onLaunch"or"highPriority"enable: turn an app off (and revert what was applied), handy for shared multi-host modules
Steam lifecycle: onSteamRunning
Replaces closeSteam with a strategy: "wait" (the default, which applies changes once Steam exits), "close" (close Steam, waiting for any running game first), "force-close", or "skip". Changes now apply automatically when Steam exits instead of being silently skipped.
Fixes & robustness
- Binary VDF (
shortcuts.vdf) is now written atomically, so a crash mid-write can't corrupt it. - Configuration that is removed is reliably reverted, tracked via a per-user state manifest.
- Duplicate app IDs and conflicting launch-option styles now fail with clear assertions.
- Unknown keys in the patcher input are rejected, so the module and patcher can't silently drift.
Internals (no configuration impact)
- Replaced the
srctoolsand patchedvdfdependencies with an in-repo text/binary KeyValues implementation. - Centralised Steam-close/wait handling and generalised patching + cleanup onto a single managed-key model.
- Added a pytest suite, flake checks (module eval + patcher integration), and CI-friendly tests.
Migrating from 0.2.x
closeSteam → onSteamRunning
closeSteam still works (mapped, with a deprecation warning) but should be replaced:
# before
closeSteam = true;
# after
onSteamRunning = "close";Note the default changed: previously changes were skipped while Steam ran; now they are applied when Steam exits (onSteamRunning = "wait").
Steam apps now require an explicit id
A numeric attribute name is no longer used as the app id. The attribute name is now just a label (it also names the desktop entry), so a readable name reads better.
# before
apps."620".launchOptionsStr = "-vulkan";
# after
apps."Portal 2" = { id = 620; launchOptionsStr = "-vulkan"; };Non-Steam icon moved under artwork
# before
icon = ./icon.png;
# after
artwork.icon = ./icon.png;Legacy launchOptions forms removed
launchOptions is now only the structured form (env / wrappers / args / preHook).
- A raw string → use
launchOptionsStr. - A wrapper derivation → use
launchOptions.preHook.