diff --git a/STEAM_DECK_JOVIAN_SETUP.md b/STEAM_DECK_JOVIAN_SETUP.md new file mode 100644 index 0000000..b1416a4 --- /dev/null +++ b/STEAM_DECK_JOVIAN_SETUP.md @@ -0,0 +1,280 @@ +# Steam Deck (nix-deck) Jovian-NixOS Setup Guide + +This document describes the setup for installing and maintaining NixOS with Jovian-NixOS on a Steam Deck. + +## Overview + +The `nix-deck` configuration provides: +- **Jovian-NixOS integration** for Steam Deck hardware support +- **Remote building** using `zix790prors` as the build host +- **SteamOS role** for easy Steam Deck UI configuration +- **Compatibility shim** for using Jovian on NixOS 25.05 stable + +## Architecture + +### Remote Building + +The setup uses distributed builds to avoid slow compilation on the Steam Deck: + +- **Build Host**: `zix790prors` (powerful desktop) + - Runs as a dedicated `nix-builder` user (not root) + - Accepts SSH connections from client machines + - Performs all heavy compilation work + +- **Build Clients**: `nix-book` and `nix-deck` + - Automatically offload builds to `zix790prors` + - Fall back to local building if remote builder is unavailable + - Steam Deck heavily prefers remote (speedFactor=4) + +### Jovian-NixOS Integration + +- **Jovian module**: Provides Steam Deck hardware support, drivers, and Steam UI +- **Compatibility layer**: `roles/jovian-compat.nix` provides `services.logind.settings` for NixOS 25.05 + - **IMPORTANT**: Remove this when upgrading to NixOS 25.11+ + - An assertion will fail the build if used on 25.11+ + +- **SteamOS role**: `roles.desktop.steamos` abstracts Jovian configuration + ```nix + roles.desktop.steamos = { + enable = true; + autoStart = false; # Set to true to boot directly to Steam UI + desktopSession = "plasmawayland"; + }; + ``` + +## Initial Installation + +### Prerequisites + +1. Steam Deck in recovery mode or booted to a live Linux environment +2. SSH access enabled on the Steam Deck +3. SSH key set up for passwordless authentication + +### Option 1: Using nixos-anywhere (Initial Install Only) + +```bash +# From your main machine +nix run github:nix-community/nixos-anywhere -- \ + --flake .#nix-deck \ + root@ +``` + +**Note**: This is only for the initial install. For updates, see below. + +### Option 2: Manual Installation + +1. Boot Steam Deck from NixOS installer USB +2. Partition and format the disk +3. Mount filesystems +4. Clone this repository +5. Generate hardware config: + ```bash + nixos-generate-config --show-hardware-config > /tmp/hw.nix + ``` +6. Copy the hardware config content to `machines/nix-deck/hardware-configuration.nix` +7. Keep the `jovian.devices.steamdeck` settings in the file +8. Install: + ```bash + nixos-install --flake .#nix-deck + ``` + +## Updates and Rebuilds + +### Method 1: Remote Build and Deploy (Recommended) + +Build on your main machine, deploy to Steam Deck: + +```bash +# From nix-book or zix790prors +nixos-rebuild switch \ + --flake .#nix-deck \ + --target-host root@nix-deck \ + --build-host localhost +``` + +### Method 2: On-Device Rebuild (Uses Remote Builder) + +The Steam Deck is configured to automatically use `zix790prors` as a remote builder: + +```bash +# SSH into the Steam Deck +ssh root@nix-deck + +# This will automatically build on zix790prors +nixos-rebuild switch --flake /path/to/nixos-configs#nix-deck +``` + +The build will automatically happen on `zix790prors` and be deployed locally. + +## Remote Builder Setup + +### On the Build Host (zix790prors) + +The configuration creates a `nix-builder` user that client machines connect to: + +```nix +roles.remote-build.enableBuilder = true; +``` + +### On Client Machines (nix-book, nix-deck) + +Configure the remote builder: + +```nix +roles.remote-build.builders = [{ + hostName = "zix790prors"; + maxJobs = 16; + speedFactor = 4; # Higher = prefer remote more +}]; +``` + +### SSH Key Setup + +1. Generate SSH key on the builder host for the `nix-builder` user: + ```bash + sudo -u nix-builder ssh-keygen -t ed25519 -f /var/lib/nix-builder/.ssh/id_ed25519 + ``` + +2. Copy the public key to client machines: + ```bash + # Add to /var/lib/nix-builder/.ssh/authorized_keys on zix790prors + ``` + +3. On client machines, ensure you can connect: + ```bash + ssh nix-builder@zix790prors + ``` + +## Configuration Files + +### Key Files Created/Modified + +- `flake.nix` - Added Jovian input and nix-deck configuration +- `roles/jovian-compat.nix` - Compatibility shim (remove in 25.11+) +- `roles/desktop/steamos.nix` - SteamOS/Jovian role abstraction +- `roles/remote-build/default.nix` - Remote builder role +- `machines/nix-deck/configuration.nix` - Steam Deck system config +- `machines/nix-deck/hardware-configuration.nix` - Hardware config (placeholder) + +### Example Configuration + +```nix +# machines/nix-deck/configuration.nix +{ + roles = { + desktop = { + enable = true; + wayland = true; + gaming.enable = true; + kde = true; + sddm = true; + steamos = { + enable = true; + autoStart = false; # or true to boot to Steam UI + desktopSession = "plasmawayland"; + }; + }; + remote-build.builders = [{ + hostName = "zix790prors"; + maxJobs = 16; + speedFactor = 4; + }]; + }; +} +``` + +## Jovian Features + +### Enabled by Default + +- Steam Deck hardware support (`jovian.devices.steamdeck.enable`) +- Steam UI (`jovian.steam.enable`) +- Decky Loader plugin system (`jovian.decky-loader.enable`) + +### Optional Features + +Set in the hardware-configuration.nix: + +```nix +jovian.devices.steamdeck = { + enable = true; + autoUpdate = false; # Auto-update BIOS/controller firmware +}; +``` + +### Manual Firmware Updates + +```bash +# BIOS update +sudo jupiter-biosupdate + +# Controller update +sudo jupiter-controller-update + +# Docking station (connect via USB-C first) +jupiter-dock-updater +``` + +## Troubleshooting + +### Remote Builds Not Working + +1. Check SSH connectivity: + ```bash + ssh nix-builder@zix790prors + ``` + +2. Verify builder is trusted: + ```bash + # On zix790prors + nix show-config | grep trusted-users + ``` + +3. Check build logs: + ```bash + journalctl -u nix-daemon -f + ``` + +### Jovian Not Working + +1. Ensure you're on NixOS 25.05 or the compatibility layer is removed for 25.11+ +2. Check Jovian is imported in flake.nix +3. Verify hardware config has `jovian.devices.steamdeck.enable = true` + +### Compatibility Layer Issues + +If you see an error about `jovian-compat.nix` being incompatible: + +1. You're running NixOS 25.11 or later +2. Remove `./roles/jovian-compat.nix` from `flake.nix` +3. Jovian should work natively on 25.11+ + +## Future Upgrades + +### Upgrading to NixOS 25.11 + +1. Update `nixpkgs` input in flake.nix to 25.11 +2. Remove `./roles/jovian-compat.nix` from flake.nix imports +3. The assertion in jovian-compat.nix will prevent accidental use +4. Test the build +5. Deploy + +### Switching to Unstable + +If you need Jovian to follow unstable nixpkgs: + +1. Edit `flake.nix`: + ```nix + jovian = { + url = "github:Jovian-Experiments/Jovian-NixOS"; + inputs.nixpkgs.follows = "nixpkgs-unstable"; + }; + ``` + +2. This only affects Jovian packages, not your base system + +## Additional Resources + +- [Jovian-NixOS Documentation](https://jovian-experiments.github.io/Jovian-NixOS/) +- [Jovian Steam Deck Guide](https://jovian-experiments.github.io/Jovian-NixOS/devices/valve-steam-deck/) +- [NixOS Remote Builds](https://nixos.org/manual/nix/stable/advanced-topics/distributed-builds.html) diff --git a/flake.lock b/flake.lock index cc86b16..e3c382e 100644 --- a/flake.lock +++ b/flake.lock @@ -57,6 +57,27 @@ "type": "github" } }, + "jovian": { + "inputs": { + "nix-github-actions": "nix-github-actions", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1763223001, + "narHash": "sha256-Hi6XxTJJjKsDrO+D0fYXS88ehCYzQkZlp9qxX1zoM1s=", + "owner": "Jovian-Experiments", + "repo": "Jovian-NixOS", + "rev": "68a1bcc019378272e601558719f82005a80ddab0", + "type": "github" + }, + "original": { + "owner": "Jovian-Experiments", + "repo": "Jovian-NixOS", + "type": "github" + } + }, "nix-darwin": { "inputs": { "nixpkgs": [ @@ -78,6 +99,28 @@ "type": "github" } }, + "nix-github-actions": { + "inputs": { + "nixpkgs": [ + "jovian", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1729697500, + "narHash": "sha256-VFTWrbzDlZyFHHb1AlKRiD/qqCJIripXKiCSFS8fAOY=", + "owner": "zhaofengli", + "repo": "nix-github-actions", + "rev": "e418aeb728b6aa5ca8c5c71974e7159c2df1d8cf", + "type": "github" + }, + "original": { + "owner": "zhaofengli", + "ref": "matrix-name", + "repo": "nix-github-actions", + "type": "github" + } + }, "nixos-wsl": { "inputs": { "flake-compat": "flake-compat", @@ -173,6 +216,7 @@ "inputs": { "google-cookie-retrieval": "google-cookie-retrieval", "home-manager": "home-manager", + "jovian": "jovian", "nix-darwin": "nix-darwin", "nixos-wsl": "nixos-wsl", "nixpkgs": "nixpkgs_2", diff --git a/flake.nix b/flake.nix index 0c81770..856081e 100644 --- a/flake.nix +++ b/flake.nix @@ -26,6 +26,11 @@ url = "git+https://git.johnogle.info/johno/google-cookie-retrieval.git"; inputs.nixpkgs.follows = "nixpkgs"; }; + + jovian = { + url = "github:Jovian-Experiments/Jovian-NixOS"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = { self, nixpkgs, nixpkgs-unstable, nixos-wsl, ... } @ inputs: let @@ -33,6 +38,8 @@ ./roles ] ++ [ inputs.home-manager.nixosModules.home-manager + ./roles/jovian-compat.nix # Compatibility shim for Jovian on stable NixOS + inputs.jovian.nixosModules.jovian { nixpkgs.overlays = [ (final: prev: { @@ -142,6 +149,18 @@ ]; }; + # Steam Deck configuration + nixosConfigurations.nix-deck = nixpkgs.lib.nixosSystem rec { + system = "x86_64-linux"; + modules = nixosModules ++ [ + ./machines/nix-deck/configuration.nix + { + home-manager.users.johno = import ./home/home-desktop.nix; + home-manager.extraSpecialArgs = { inherit system; }; + } + ]; + }; + # Darwin/macOS configurations darwinConfigurations."blkfv4yf49kt7" = inputs.nix-darwin.lib.darwinSystem rec { system = "aarch64-darwin"; diff --git a/machines/nix-book/configuration.nix b/machines/nix-book/configuration.nix index 7a3a553..550a49e 100644 --- a/machines/nix-book/configuration.nix +++ b/machines/nix-book/configuration.nix @@ -21,6 +21,11 @@ }; nfs-mounts.enable = true; printing.enable = true; + remote-build.builders = [{ + hostName = "zix790prors"; + maxJobs = 16; + speedFactor = 3; + }]; spotifyd.enable = true; users = { enable = true; diff --git a/machines/nix-deck/configuration.nix b/machines/nix-deck/configuration.nix new file mode 100644 index 0000000..b53fcf9 --- /dev/null +++ b/machines/nix-deck/configuration.nix @@ -0,0 +1,41 @@ +{ pkgs, ... }: +{ + imports = [ + ./hardware-configuration.nix + ]; + + roles = { + audio.enable = true; + bluetooth.enable = true; + desktop = { + enable = true; + wayland = true; + gaming.enable = true; + kde = true; + sddm = true; + steamos = { + enable = true; + autoStart = false; + desktopSession = "plasmawayland"; + }; + }; + remote-build.builders = [{ + hostName = "zix790prors"; + maxJobs = 16; + speedFactor = 4; # Prefer remote heavily on Steam Deck + }]; + users = { + enable = true; + extraGroups = [ "video" ]; + }; + }; + + # Bootloader + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + + networking.hostName = "nix-deck"; + networking.networkmanager.enable = true; + + system.stateVersion = "25.05"; +} diff --git a/machines/nix-deck/hardware-configuration.nix b/machines/nix-deck/hardware-configuration.nix new file mode 100644 index 0000000..32c3d19 --- /dev/null +++ b/machines/nix-deck/hardware-configuration.nix @@ -0,0 +1,37 @@ +# This is a placeholder hardware configuration for the Steam Deck. +# Run `nixos-generate-config --show-hardware-config` on the actual device +# to generate the real hardware configuration, then merge the jovian settings below. + +{ config, lib, pkgs, modulesPath, ... }: + +{ + imports = [ ]; + + # Steam Deck specific hardware configuration + jovian.devices.steamdeck = { + enable = true; + autoUpdate = false; # Set to true if you want automatic firmware updates + }; + + # TODO: Replace this with actual hardware configuration from the Steam Deck + # Run `nixos-generate-config --show-hardware-config` on the device and merge it here + + # Minimal placeholder configuration to allow flake to build + boot.initrd.availableKernelModules = [ ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ ]; + + fileSystems."/" = { + device = "/dev/disk/by-label/nixos"; + fsType = "ext4"; + }; + + fileSystems."/boot" = { + device = "/dev/disk/by-label/boot"; + fsType = "vfat"; + }; + + swapDevices = [ ]; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; +} diff --git a/machines/zix790prors/configuration.nix b/machines/zix790prors/configuration.nix index 0ff7425..30f26df 100644 --- a/machines/zix790prors/configuration.nix +++ b/machines/zix790prors/configuration.nix @@ -29,6 +29,7 @@ with lib; nfs-mounts.enable = true; nvidia.enable = true; printing.enable = true; + remote-build.enableBuilder = true; users.enable = true; virtualisation.enable = true; }; diff --git a/roles/default.nix b/roles/default.nix index 66261f7..a2bd665 100644 --- a/roles/default.nix +++ b/roles/default.nix @@ -12,6 +12,7 @@ with lib; ./nfs-mounts ./nvidia ./printing + ./remote-build ./spotifyd ./users ./virtualisation diff --git a/roles/desktop/default.nix b/roles/desktop/default.nix index 3c206ff..f874962 100644 --- a/roles/desktop/default.nix +++ b/roles/desktop/default.nix @@ -23,5 +23,6 @@ with lib; ./kde.nix ./programs.nix ./sddm.nix + ./steamos.nix ]; } diff --git a/roles/desktop/steamos.nix b/roles/desktop/steamos.nix new file mode 100644 index 0000000..c6017dc --- /dev/null +++ b/roles/desktop/steamos.nix @@ -0,0 +1,47 @@ +{ lib, config, pkgs, ... }: + +with lib; + +let + cfg = config.roles.desktop; +in +{ + options.roles.desktop.steamos = { + enable = mkEnableOption "SteamOS (Jovian) configuration"; + + autoStart = mkOption { + type = types.bool; + default = false; + description = "Automatically start Steam Deck UI on boot"; + }; + + user = mkOption { + type = types.str; + default = "johno"; + description = "User to run Steam as"; + }; + + desktopSession = mkOption { + type = types.nullOr types.str; + default = null; + description = "Desktop session to launch when switching to Desktop Mode"; + }; + + enableDeckyLoader = mkOption { + type = types.bool; + default = true; + description = "Enable Decky Loader plugin system"; + }; + }; + + config = mkIf (cfg.enable && cfg.steamos.enable) { + jovian.steam = { + enable = true; + autoStart = cfg.steamos.autoStart; + user = cfg.steamos.user; + desktopSession = cfg.steamos.desktopSession; + }; + + jovian.decky-loader.enable = cfg.steamos.enableDeckyLoader; + }; +} diff --git a/roles/jovian-compat.nix b/roles/jovian-compat.nix new file mode 100644 index 0000000..89d8093 --- /dev/null +++ b/roles/jovian-compat.nix @@ -0,0 +1,43 @@ +{ lib, config, ... }: + +# Compatibility shim to provide services.logind.settings for NixOS 25.05 +# This allows Jovian-NixOS to work with stable NixOS +# REMOVE THIS FILE when upgrading to NixOS 25.11 or later + +with lib; + +let + nixosVersion = config.system.nixos.release; + isCompatibleVersion = versionOlder nixosVersion "25.11"; +in +{ + options.services.logind.settings = mkOption { + type = types.attrsOf (types.attrsOf types.anything); + default = {}; + description = "systemd-logind configuration. See logind.conf(5) for available options."; + }; + + config = mkMerge [ + { + assertions = [ + { + assertion = isCompatibleVersion; + message = '' + The Jovian compatibility shim (roles/jovian-compat.nix) is only needed for NixOS 25.05 and earlier. + You are running NixOS ${nixosVersion}. + Please remove 'roles/jovian-compat.nix' from your flake.nix imports. + ''; + } + ]; + } + (mkIf (config.services.logind.settings != {}) { + # Convert the settings to extraConfig format for older NixOS + services.logind.extraConfig = let + mkSection = section: settings: + "[${section}]\n" + + (concatStringsSep "\n" (mapAttrsToList (k: v: "${k}=${toString v}") settings)); + in + concatStringsSep "\n\n" (mapAttrsToList mkSection config.services.logind.settings); + }) + ]; +} diff --git a/roles/remote-build/default.nix b/roles/remote-build/default.nix new file mode 100644 index 0000000..146f5c1 --- /dev/null +++ b/roles/remote-build/default.nix @@ -0,0 +1,127 @@ +{ lib, config, pkgs, ... }: + +with lib; + +let + cfg = config.roles.remote-build; +in +{ + options.roles.remote-build = { + enableBuilder = mkOption { + type = types.bool; + default = false; + description = "Enable this machine as a remote build host for other machines"; + }; + + builderUser = mkOption { + type = types.str; + default = "nix-builder"; + description = "User account for remote builders to connect as"; + }; + + builders = mkOption { + type = types.listOf (types.submodule { + options = { + hostName = mkOption { + type = types.str; + description = "Hostname or IP address of the build machine"; + }; + + systems = mkOption { + type = types.listOf types.str; + default = [ "x86_64-linux" ]; + description = "Supported systems"; + }; + + maxJobs = mkOption { + type = types.int; + default = 8; + description = "Maximum number of parallel build jobs"; + }; + + speedFactor = mkOption { + type = types.int; + default = 2; + description = "Speed factor compared to local building (higher = prefer remote)"; + }; + + supportedFeatures = mkOption { + type = types.listOf types.str; + default = [ "nixos-test" "benchmark" "big-parallel" "kvm" ]; + description = "Supported build features"; + }; + + sshUser = mkOption { + type = types.str; + default = "nix-builder"; + description = "SSH user for connecting to the builder"; + }; + + sshKey = mkOption { + type = types.nullOr types.path; + default = null; + description = "Path to SSH private key for authentication"; + }; + }; + }); + default = []; + description = "List of remote build machines to use"; + }; + + fallbackToLocalBuild = mkOption { + type = types.bool; + default = true; + description = "Fallback to local building if remote builders are unavailable"; + }; + }; + + config = mkMerge [ + # Builder host configuration + (mkIf cfg.enableBuilder { + # Create dedicated builder user + users.users.${cfg.builderUser} = { + isSystemUser = true; + group = cfg.builderUser; + description = "Nix remote build user"; + home = "/var/lib/${cfg.builderUser}"; + createHome = true; + shell = pkgs.bashInteractive; + openssh.authorizedKeys.keyFiles = []; # Will be populated by client machines + }; + + users.groups.${cfg.builderUser} = {}; + + # Allow builder user to perform builds + nix.settings.trusted-users = [ cfg.builderUser ]; + + # Allow remote builds + services.openssh.enable = true; + + # Ensure nix-daemon is accessible + nix.settings.allowed-users = [ "*" ]; + }) + + # Client configuration (machines using remote builders) + (mkIf (cfg.builders != []) { + nix.buildMachines = map (builder: { + hostName = builder.hostName; + systems = builder.systems; + maxJobs = builder.maxJobs; + speedFactor = builder.speedFactor; + supportedFeatures = builder.supportedFeatures; + sshUser = builder.sshUser; + sshKey = builder.sshKey; + }) cfg.builders; + + nix.distributedBuilds = true; + + # Use substitutes from remote builders + nix.extraOptions = '' + builders-use-substitutes = true + ''; + + # Fallback to local build if remote unavailable + nix.settings.fallback = cfg.fallbackToLocalBuild; + }) + ]; +}