Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Options

All options live under steamix.*. Anything not listed here is configured through upstream nixpkgs options (programs.steam.*, programs.gamescope.*).

This page is the curated tour; the option reference is the complete listing, generated from the module so it cannot drift.

Every package Steamix installs has a package option (steamix.heroic.package, steamix.losslessScaling.package, steamix.library.boilr.package, …), so any of them can come from another nixpkgs, an overlay or an override. Steam and gamescope themselves are set through the upstream programs.steam.package and programs.gamescope.package. For example, lsfg-vk 2.x on a NixOS 26.05 machine:

steamix.losslessScaling = {
  package = unstable.lsfg-vk;       # `unstable` being a nixos-unstable package set
  ui.package = unstable.lsfg-vk-ui; # same major version as the layer
};

Core

steamix.enable

Type bool, default false. Master switch. Enables programs.steam and programs.gamescope, and registers this module’s own Gaming Mode session.

It also sets programs.steam.gamescopeSession.enable = false — nixpkgs’ session and this one both register steam.desktop, and this one is a superset (see How it works).

steamix.user

Type null or str, default null. The user the Gaming Mode session runs as. Required when autoStart is on — that user is logged in without a password prompt.

steamix.autoStart

Type bool, default true. Boot straight into Gaming Mode by owning the login path with a greetd session loop. Cannot be combined with a regular display manager (greetd aliases display-manager.service), so turn off the one your desktop configuration enables, for example services.displayManager.gdm.enable = false.

With autoStart = false the module only registers the “Steam” session: pick it from your own display manager’s session chooser; session switching (which relies on the loop) is not available.

steamix.desktopSession

Type null or str, default null, example "gnome". The Wayland session started by “Switch to Desktop” — a name from services.displayManager.sessionData.sessionNames (the basename of a wayland-sessions/*.desktop file). An unknown name is an eval-time error listing the valid ones; null keeps you in Gaming Mode (with a warning).

The session itself must be enabled through your normal configuration (services.desktopManager.gnome.enable, plasma6.enable, …) — this option only selects it.

steamix.steamArgs

Type list of str, default [ "-gamepadui" "-steamos3" "-steampal" "-steamdeck" "-pipewire-dmabuf" ]. Arguments Steam is launched with inside Gaming Mode — the set Valve’s own steam-launcher uses, plus -pipewire-dmabuf for Remote Play.

Leave -steamos3 in place: it is what ties Steam into gamescope’s focus handling and puts “Switch to Desktop” in the power menu.

Display features

Each of these does two things: passes the gamescope flag, and sets the STEAM_GAMESCOPE_* variable that makes the matching toggle appear in Gaming Mode’s settings. Steam reads those once at startup and simply believes them, so they are tied to the flag rather than exposed separately.

OptionDefaultgamescope flagSteam capability
steamix.hdr.enabletrue--hdr-enabledSTEAM_GAMESCOPE_HDR_SUPPORTED
steamix.vrr.enabletrue--adaptive-syncSTEAM_GAMESCOPE_VRR_SUPPORTED
steamix.tearing.enablefalse--immediate-flipsSTEAM_GAMESCOPE_TEARING_SUPPORTED
steamix.mangoapp.enabletrue--mangoappSTEAM_USE_MANGOAPP
  • HDR is safe to leave on for SDR displays — gamescope only drives HDR when the connected output advertises it. It needs the WSI layer below for games to hand HDR swapchains through; without it, HDR content is tonemapped to SDR. Turn it on per game in Gaming Mode’s display settings.
  • VRR is likewise ignored when the output does not support FreeSync.
  • Tearing is off by default: it trades visible tearing for latency, and VRR is the better answer on displays that have it.
  • mangoapp is what backs Steam’s built-in “Performance Overlay” levels; it is not the same thing as running MangoHud on a game yourself.

steamix.mangoapp.fontScale

Type null or float/int, default null. Multiplier for the performance overlay’s size — MangoHud’s font_scale, which scales the panel as well as the text.

MangoHud draws the overlay at a fixed size chosen for the Steam Deck’s 1280x800 panel, so it shrinks to an unreadable speck on a big television. The default scales it automatically against the tallest connected output (about 1.8x at 1440p, 2.7x at 2160p), which keeps it roughly Deck-sized. Set a number to pin it, or 1 for MangoHud’s own sizing.

The sizing is applied through MANGOHUD_PRESETSFILE — the module defines all five overlay levels itself and bakes the scale into them — because the two obvious routes both fail: Steam rewrites the config file on every level change, and MANGOHUD_CONFIG makes MangoHud apply the preset twice, drawing every element double. Levels 2–4 additionally get a narrower scale than level 1 where needed, computed so the widest table row still fits the display; at full scale on a 4K screen the dense levels would run off both edges.

steamix.mangoapp.pciDev

Type null or str, default null, example "0000:03:00.0". PCI address of the GPU the overlay reports on (MangoHud’s pci_dev, written as lspci -D prints it).

Only meaningful on a machine with more than one GPU: MangoHud enumerates every GPU and picks one itself, which need not be the one gamescope renders with — a desktop with an idle iGPU can get an overlay reporting that chip’s load while the games run on the discrete card. null leaves the choice to MangoHud.

steamix.mangoapp.package

The mangohud package that supplies mangoapp. It talks to gamescope over X atoms and a message queue, so when programs.gamescope.package is overridden to a much newer gamescope, override this to match.

steamix.mangoapp.backgroundAlpha

Type null or float/int, default 0.8. Opacity of the performance overlay’s backdrop, from 0 (invisible) to 1 (solid). MangoHud defaults to 0.5, which is legible on a monitor an arm’s length away and washes out into the game from across a living room. null leaves MangoHud’s own default alone.

steamix.realtime.enable

Type bool, default false. Adds --rt and gives gamescope cap_sys_nice through programs.gamescope.capSysNice, letting it renice itself for smoother frame pacing.

Off by default because the capability puts gamescope in secure-execution mode, where the loader ignores LD_LIBRARY_PATH/LD_PRELOAD — which has historically broken launching it out of Steam’s FHS environment. Turn it on, reboot, and confirm Gaming Mode still starts before keeping it.

gamescope

steamix.gamescope.args

Type list of str, default [ ]. Extra arguments appended to the gamescope command line, after the ones derived from the options above. One list element per argv entry:

steamix.gamescope.args = [
  "--output-width"
  "3840"
  "--output-height"
  "2160"
  "--nested-refresh"
  "120"
];

steamix.gamescope.env

Type attrs of str, default { }. Extra environment for the session, exported last so it overrides the module’s own defaults.

steamix.gamescope.wsi.enable

Type bool, default true. Installs the gamescope WSI Vulkan layer (VK_LAYER_FROG_gamescope_wsi) into hardware.graphics.extraPackages and extraPackages32, and sets ENABLE_GAMESCOPE_WSI=1 for the session.

The layer is how games present through gamescope instead of through plain Xwayland WSI; it carries frame pacing, the framerate limiter, and HDR swapchains. Requires hardware.graphics.enable32Bit (asserted).

steamix.gamescope.wsi.package / .package32

Defaulting to pkgs.gamescope-wsi and pkgs.pkgsi686Linux.gamescope-wsi. The layer speaks a versioned protocol to gamescope, so override these to keep it in step with a non-default programs.gamescope.package:

# `newer` being a newer nixpkgs, for example an unstable input
programs.gamescope.package = newer.gamescope;
steamix.gamescope.wsi = {
  package = newer.gamescope-wsi;
  package32 = newer.pkgsi686Linux.gamescope-wsi;
};

steamix.binaryCache.enable

Type bool, default true. Downloads SteamOS Manager, Decky Loader and the 32-bit gamescope WSI layer from steamix.cachix.org instead of compiling them, by adding the cache and its key to nix.settings next to cache.nixos.org. No trust beyond what importing the module already asks for: the module runs as root and the key is pinned. It helps only when the machine’s nixpkgs matches a revision CI built against; see Binary cache.

steamix.tweaks.enable

Type bool, default true. SteamOS’ opinionated system tuning, minus everything specific to Valve’s hardware. Every setting below is a mkDefault, so an individual one can be overridden without turning the group off.

SettingWhy
zramSwap — zstd, 50% of RAM, priority 100A console has no swap partition to fall back on, and shader compilation, Proton and a browser-based UI will happily use everything.
services.earlyoom — 5% free memory and swapUnder pressure the kernel’s own OOM killer arrives long after the machine stopped responding, which on a controller-driven box means reaching for the power button.
net.ipv4.tcp_mtu_probing = 1Some storefronts and matchmaking services sit behind PMTU black holes.
net.ipv4.tcp_fin_timeout = 5A game killed and relaunched cannot rebind its port while the old socket lingers, and the default timeout is far longer than a relaunch takes.

What else the module sets

Beyond the session itself:

SettingWhy
security.pam.loginLimits — hard nice of -8 for steamix.userProton runs some threads at negative niceness. Scoped to the account the session logs in as, not system-wide.
A polkit rule for NetworkManager, when steamix.user is set and networking.networkmanager.enable is onGaming Mode’s Wi-Fi settings write system connections, which normally needs the networkmanager group — impossible to grant mid-setup with only a controller in hand. Scoped to that one user’s local, active session; Jovian grants it to everyone in users.

Controller access is deliberately not handled here. programs.steam turns on hardware.steam-hardware, which installs Valve’s steam-devices rules — those already tag /dev/uinput for the active session and carry uaccess rules for every controller Steam supports. A blanket KERNEL=="hidraw*", TAG+="uaccess" would hand the session every HID device on the machine, which is more than Valve grants on a Deck. If you have a pad that Valve’s list misses, add a rule for that device.

steamix.heroic

Heroic Games Launcher for Epic, GOG and Amazon Prime Gaming libraries.

OptionTypeDefaultPurpose
.enableboolfalseInstall Heroic.
.packagepackagepkgs.heroicWhat to install.

This installs Heroic as a normal application, so it is there in Desktop Mode. Gaming Mode only shows what is in Steam’s library: use Heroic’s own per-game “Add to Steam” action, or add Heroic itself as a non-Steam game. Adding library entries declaratively is on the roadmap.

nixpkgs’ heroic runs the launcher in an FHS environment with the libraries its games and Wine builds expect; pkgs.heroic.override { extraPkgs = pkgs: [ ... ]; } adds more.

steamix.emulation

RetroArch with a set of libretro cores. It does not need the rest of Steamix (steamix.enable): it also serves machines Steam does not run on, such as a Raspberry Pi 4.

OptionTypeDefaultPurpose
.enableboolfalseInstall RetroArch with the cores.
.retroarch.packagepackagepkgs.retroarch-bareThe frontend the cores and settings are wrapped around.
.retroarch.coreslist of packageNestopia, bsnes, mGBA, PCSX ReARMedThe cores, from pkgs.libretro.
.retroarch.settingsattrs of string{ }retroarch.cfg settings applied over the user’s own on every start.
.retroarch.finalPackagepackageread-onlyThe RetroArch that is installed.
.kiosk.enableboolfalseBoot straight into RetroArch, full screen in cage, started again when it quits. Not together with Gaming Mode’s autoStart.
.kiosk.usernull or stringsteamix.userWho RetroArch runs as in the kiosk.

The default cores are free software and cover NES, SNES, Game Boy / Color / Advance and PlayStation. The fastest SNES and Mega Drive cores, Snes9x, Genesis Plus GX and PicoDrive, are unfree (non-commercial use only), so adding them needs unfree packages allowed; on small ARM boards they are the ones to use:

steamix.emulation.retroarch.cores = with pkgs.libretro; [
  nestopia snes9x genesis-plus-gx mgba pcsx-rearmed
];

Games go in ~/ROMs, where RetroArch’s file browser starts. With Gaming Mode, RetroArch is an application in Desktop Mode; add it as a non-Steam game to reach it from Gaming Mode.

steamix.library.boilr

BoilR puts games from other launchers in Steam’s library, so Gaming Mode can start them: Heroic (Epic and GOG), Legendary, Lutris, itch.io, Bottles, Flatpaks, MiniGalaxy and more. It runs on every login, inside the Gaming Mode session right before Steam starts, because it needs Steam closed. A game installed in Desktop Mode appears the next time Gaming Mode starts.

OptionTypeDefaultPurpose
.enableboolfalseSync on every login.
.packagepackagepkgs.boilrWhat to run.
.settingsTOML attrs{ }BoilR’s configuration, written to ~/.config/boilr/config.toml on every login.
.steamGridDbKeyFilenull or strnullA file with a SteamGridDB API key, read at login, for artwork.
.timeoutpositive int120Seconds a sync may take before Steam starts anyway.
steamix.library.boilr = {
  enable = true;
  steamGridDbKeyFile = "/run/secrets/steamgriddb"; # e.g. from sops-nix
  settings.steam.create_collections = true;
};

Repeating the sync is safe: BoilR removes only the shortcuts it added itself, and shortcuts you added by hand stay. Heroic’s games launch through Heroic by default, which keeps Epic sign-in and cloud saves working.

settings is the source of truth. Anything left out keeps BoilR’s default, which detects most launchers by itself; Steamix only sets steam.optimize_for_big_picture = true and enables SteamGridDB when a key file is given. Changes made in BoilR’s own window do not survive the next login. The key never enters the Nix store: the config is written with a placeholder and the key is filled in at login, into a file only the user can read.

steamix.losslessScaling

Lossless Scaling’s frame generation for Steam’s games, through lsfg-vk, a Vulkan layer that runs it with the DLL from your own copy of Lossless Scaling.

You need Lossless Scaling installed from Steam. lsfg-vk 2.x additionally needs its “lsfg-vk” beta branch: in Steam, Lossless Scaling > Properties > Game Versions & Betas. lsfg-vk finds the DLL in the usual Steam libraries; dll points elsewhere.

OptionTypeDefaultPurpose
.enableboolfalseInstall the layer for Steam’s games.
.packagepackagepkgs.lsfg-vkWhat to install.
.support32Bit.enableboolfalseAlso install the 32-bit layer, for 32-bit games.
.dllnull or strnullWhere Lossless Scaling’s DLL is, if lsfg-vk does not find it.
.allowHalfPrecisionbooltrueFP16 frame generation, 2 to 3 times faster where supported (2.x).
.profilesattrs of profiles{ }Declarative profiles; see below.
.ui.enableboolfalselsfg-vk’s configuration UI, for editing profiles in Desktop Mode.
.ui.packagepackagepkgs.lsfg-vk-uiWhich UI; keep it on the layer’s major version (Steamix warns otherwise).
.deckyPlugin.enableboolsee belowManage lsfg-vk from Gaming Mode with the Decky LSFG-VK plugin.
.deckyPlugin.packagenull or packagepkgs.deckyPlugins.decky-lsfg-vk or nullWhich plugin.

Three ways to run it.

  • From Gaming Mode, with Decky. When Decky Loader is on and profiles is empty, Steamix adds the Decky LSFG-VK plugin instead of installing the layer itself: per-game frame generation settings in the Quick Access menu, the way it works on SteamOS. Press its “Install lsfg-vk” button once; it installs its own lsfg-vk into your home. The plugin needs Decky to run plugins as the Steam user, as SteamOS does, because that is whose home it installs into:

    steamix.decky-loader.user = config.steamix.user;
    

    Without that, Steamix installs its own layer instead and warns why the plugin was left out. The plugin’s own lsfg-vk build is made for ordinary distributions and does not run on NixOS, so Steamix’s package repairs it: the plugin validates settings with a NixOS build of its lsfg-vk-cli, and its UI launcher starts a NixOS build of its UI. The layers games load stay as upstream built them. It carries lsfg-vk’s build, so it is unfree. After a NixOS update that changed the plugin, press “Reinstall” in it, so the copies in your home match.

  • Declaratively, with profiles set:

steamix.losslessScaling = {
  enable = true;
  profiles.elden-ring = {
    activeIn = [ "1245620" ]; # Steam App ID, executable or process name
    multiplier = 2;
    flowScale = 0.75;
  };
};

Steamix installs the layer, writes the whole config to /etc/lsfg-vk/conf.toml and points every session at it, read-only; the Decky plugin is left out, since it could not change it.

  • Interactively without Decky: with profiles empty and no Decky, the config is ~/.config/lsfg-vk/conf.toml, created with defaults the first time a game loads the layer and edited with lsfg-vk’s UI (ui.enable).

A profile has activeIn (Steam App IDs, executables, Windows .exe names or process names; lsfg-vk 1.x matches only executables and process names, and Steamix warns about App IDs there), multiplier (2 doubles the frame rate), flowScale (0.25 to 1, the resolution of the motion estimation), performanceMode, pacingMode (2.x) and hdrMode (1.x).

Versions and licence. nixpkgs has shipped two major versions with different config formats. NixOS 26.05 has the MIT-licensed 1.0; nixos-unstable has 2.0, licensed CC BY-NC-ND 4.0, which nixpkgs treats as unfree. Steamix writes whichever format the installed version reads, and the variable it reads the path from (LSFG_CONFIG or LSFGVK_CONFIG). For 2.0, nixpkgs.config.allowUnfree (or a predicate allowing lsfg-vk) has to permit it. Its licence also forbids redistribution, so no binary cache carries it, Steamix’s included, and it builds on your machine.

steamix.proton

GE-Proton, and AMD FSR 4 through it.

OptionTypeDefaultPurpose
.ge.enableboolfalseInstall GE-Proton as a compatibility tool.
.ge.packagepackagepkgs.proton-ge-binWhich GE-Proton; also what the FSR 4 variant is built from.
.fsr4.enableboolfalseAdd a “GE-Proton (FSR 4)” compatibility tool.
.fsr4.versionnull or strnullThe FSR 4 DLL version to ask for, e.g. "4.1.1"; null takes GE-Proton’s default.
.fsr4.indicatorboolfalseDraw AMD’s FSR watermark, to see whether FSR 4 is active.

FSR 4 is AMD’s machine-learning upscaler. GE-Proton can swap it in for the FSR 3.1 a game ships: with PROTON_FSR4_UPGRADE set, it downloads AMD’s FSR 4 DLL when the game starts and uses it instead. It needs an AMD RDNA 3 or RDNA 4 GPU and a game with FSR 3.1, and a network connection the first time. The DLL is GE-Proton’s download, not something Nix manages.

fsr4.enable adds GE-Proton with that flag in front of it, as its own compatibility tool: “GE-Proton (FSR 4)”, internal name GE-Proton-FSR4. It sits next to a plain GE-Proton instead of replacing it, so the two never clash. Pick it per game in Steam (Properties > Compatibility), or make it Steam’s default compatibility tool to use it everywhere. A game’s launch options still win: PROTON_FSR4_UPGRADE=0 %command% turns it off for that game, and PROTON_FSR4_UPGRADE=4.1.1 %command% pins a version.

steamix.proton.fsr4 = {
  enable = true;
  indicator = true; # until you have seen it work
};

FSR 1 is a different thing and needs nothing: gamescope’s FSR 1 upscaler is the “Scaling Filter” in Gaming Mode’s Quick Access menu, under Performance, for games that render below the display’s resolution.

steamix.decky-loader

Decky Loader injects a plugin menu into Steam’s Gaming Mode UI.

OptionTypeDefaultPurpose
.enableboolfalseRun the loader.
.packagenull or packagepkgs.decky-loader or nullWhat to run.
.userstr"decky"Unprivileged user plugins run as; created automatically when left at the default.
.stateDirpath/var/lib/decky-loaderInstalled plugins and their data.
.extraPackageslist of package[ ]Extra tools on the loader’s PATH, for plugins that shell out.
.extraPythonPackagesfunction_: [ ]Extra Python modules importable by plugins, as a function of the interpreter’s package set.

nixpkgs does not package Decky Loader. package therefore defaults to pkgs.decky-loader only when something has provided it, and to null otherwise — in which case the module configures nothing and emits a warning rather than failing to evaluate. That keeps the module usable against a bare nixpkgs, which is the same constraint the rest of it follows. Steamix builds one in packages/decky-loader, and its overlays.default provides it as pkgs.decky-loader; without the overlay, supply your own:

steamix.decky-loader = {
  enable = true;
  package = inputs.somewhere.packages.${pkgs.system}.decky-loader;
  extraPythonPackages = ps: [ ps.hid ];
};

The service runs as root and drops to user for plugins. That is upstream’s design, not an oversight — running the loader unprivileged is unsupported and breaks.

What the module does for Decky beyond running it

Decky does not draw its own window — it injects into Steam’s UI over the CEF debugger that steamwebhelper opens on 127.0.0.1:8080, and Steam only opens that when .cef-enable-remote-debugging exists in its data directory at startup. Without it the loader starts, serves happily on port 1337, and is simply never visible in Gaming Mode.

So the module creates that file in steamix.user’s Steam directory, through that user’s own tmpfiles instance at login, so every directory it creates on the way belongs to the user. Steam has to be restarted afterwards — switching the configuration is not enough, since Steam only reads the flag when it launches.

Be aware of what that implies: an unauthenticated debugger into the Steam client, bound to loopback, for as long as Decky is enabled. That is inherent to how Decky works, not something this module adds on top.

The service also gets lsof and systemctl on its PATH — the loader uses the first to find that CEF socket and the second to manage its own unit.

steamix.decky-loader.plugins

Type list of package, default [ ]. Plugins installed declaratively:

steamix.decky-loader.plugins = with pkgs.deckyPlugins; [
  hltb-for-deck
  protondb-decky
];

Each is symlinked into the plugin directory. Decky scans that directory for subdirectories containing a plugin.json and resolves symlinks on the way, so store paths load exactly like installed ones.

This composes with the in-game store rather than replacing it. Plugins installed through Decky’s UI are ordinary directories sitting alongside these symlinks, and keep working and updating as normal — so you can declare the ones you rely on and still try something from the store without touching your configuration.

What a declared plugin gives up is Decky’s own updater: its version is whatever the package pins, so updating means bumping the package. Uninstalling one from the UI removes only the symlink, which the next boot restores — declarative wins, which is the point, but it is a behaviour change worth knowing.

Per-plugin settings and data are unaffected either way. Decky keeps those in settings/<plugin> and data/<plugin> beside the plugin directory, never inside it, which is exactly what makes read-only store paths workable here.

Plugins are packaged in packages/deckyPlugins/, built by buildDeckyPlugin from the prebuilt tarball each plugin publishes per release. Adding one is a handful of lines — pname, version, owner, hash.

steamix.manager

SteamOS Manager is the system daemon Steam queries for things it cannot reach itself. Steam probes the interface at startup and shows only the settings the daemon can actually back, so on hardware that is not a Steam Deck most of it simply does not appear.

OptionTypeDefaultPurpose
.enableboolfalseRun the daemon.
.packagenull or packagepkgs.steamos-manager or nullWhat to run.

Like Decky’s, the package is not in nixpkgs; Steamix builds one in packages/steamos-manager and provides it through overlays.default. Without the overlay the option resolves to null and the module stays inert with a warning.

Why it is off by default

Enabling it on a greetd login path breaks “Switch to Desktop.” The daemon advertises SessionManagement1, which Steam prefers over the steamos-session-select script this module ships — and upstream implements session switching by writing an SDDM autologin drop-in:

#![allow(unused)]
fn main() {
const CONFIG_PREFIX: &str = "/etc/sddm.conf.d";
write(paths.temp_config(), format!("[Autologin]\nSession={session}\n"))
}

Nothing reads that under greetd, and it asks for gamescope-wayland.desktop by name where this module registers steam. The result is a stop of graphical-session.target followed by greetd putting you straight back into Gaming Mode. The module warns when it sees both options on.

That assumption is not unreasonable of Valve — SteamOS ships KDE and SDDM — but it is baked into the daemon rather than abstracted behind the display manager.

What you actually get on a desktop

Plausibly working: TdpLimit1, GpuPerformanceLevel1, GpuPowerProfile1, CpuScaling1, CpuBoost1, PerformanceProfile1 (generic amdgpu and cpufreq sysfs) and Storage1 (external drive formatting).

Not available: FanControl1, BatteryChargeLimit1, AmbientLightSensor1, UpdateBios1 and UpdateDock1 are Steam Deck hardware; HdmiCec1/HdmiCec2 need Valve’s cecd, which this tree does not ship.

It does not fix Gaming Mode’s resolution list. That was the original reason to reach for it, and the interface specification has no display or resolution interface at all — Steam sets the game resolution directly through gamescope’s GAMESCOPE_XWAYLAND_MODE_CONTROL atom, from a list it builds itself.

steamix.loginManager

Type enum [ "greetd" "sddm" ], default "greetd". Which display manager owns the login path when steamix.autoStart is on. The difference is not cosmetic.

greetdsddm
Needs anything outside nixpkgsnoyes — steamos-manager
logind sessionclass greeter, type tty, corrected by a pam_env ruleclass user, type wayland natively
Respawn loopgreetd’s greeter slot, used kiosk-stylesddm.autoLogin.relogin
Session switchingsteamos-session-select writes a state file the launcher consumesSteamOS Manager’s SessionManagement1, which Steam drives directly
ClosuresmallQt, mitigated by sddm.wayland.enable

greetd is the default because it is self-contained: nothing here needs a package nixpkgs does not have. The price is that greetd registers its sessions as class greeter, type tty, which desktop compositors refuse to start on, so the module corrects it with a pam_env rule — a fix that works but that upstream never intended.

sddm is what SteamOS itself runs, so Valve’s software finds the shape it expects and nothing needs correcting. It also unlocks steamix.manager, which this path enables for you: switching goes through the interface Steam prefers, rather than through a script emulating it.

Two things the module arranges on the SDDM path so it actually works:

  • Gaming Mode is registered under gamescope-wayland as well as steam. SteamOS Manager asks for gamescope-wayland.desktop by name when Steam requests game mode and that string is not configurable, so without the alias you could leave Gaming Mode but never return to it.
  • steamos-session-select keeps its name but delegates to steamosctl switch-to-game-mode/switch-to-desktop-mode, so the desktop’s “Return to Gaming Mode” entry works the same on both paths.

Picking sddm without a steamos-manager package is an assertion, not a silent no-op — nothing else can write SDDM’s autologin drop-in, so the module refuses rather than leaving you with a switch that does nothing.