I installed opensuse on my laptop

blog
Temporary, or permanent?
Published

June 11, 2023

School had just ended. During the school year, I had been using a hybrid graphics (dual-gpu) laptop, that otherwise worked normally, but had horrible, terrible battery life. I had struggled to get 2 hours out of it, because the dedicated nvidia gpu would not get turned off properly.

So I decided to switch to my second laptop, which has been unused so far. But it has pure intel graphics, and the laptop has much better linux support, and consequently, a much better battery life.

Before setting it up, I had a few requiremnts.

Security. Previously, I didn’t care about this, because I carried my laptop around with me at all times, but now I was going to be leaving my laptop unattended, maybe for extended periods of time. That meant I needed full disk encryption, and secure/trusted boot. However, I had limited time to set this up, so I neede to find a distro that did this the easiest way possible. I eventually settled on opensuse, which had an option to set up secure boot and encryption in the installer, under guided partitioning. After some hiccups, it installed just fine.

And the other, is my tools and packages. I didn’t worry too much about this, because I had decided beforehand on using nix and home-manager to install packages not available in the repositories. Home manager is a tool that allows for declarative configuration of a user environmetn, including packages, configuration files, or environment variables, using the nix programming language. Notably, it can be useed on almost all linux distros.

Here is my current home.nix, as of writing this, the file home-manager takes as an input.

Show
home.nix
{
  pkgs ? import <nixpkgs> {},
  config,
  lib,
  ... 
}:
let
  nixgl = import <nixgl> {};
  nixGlWrapper = import ./nixglwrapper.nix {inherit nixgl pkgs lib config;};
in
with import ./quarto.nix {inherit pkgs config lib;};
with import ./nixglwrapper.nix {inherit pkgs config lib nixgl;};
{
  # Home Manager needs a bit of information about you and the paths it should
  # manage.
  home.username = "moonpie";
  home.homeDirectory = "/home/moonpie";
  targets.genericLinux.enable = true;

  nixpkgs.config.allowUnfree = true;
  nix.settings.experimental-features = ["nix-command" "flakes"];

  # This value determines the Home Manager release that your configuration is
  # compatible with. This helps avoid breakage when a new Home Manager release
  # introduces backwards incompatible changes.
  #
  # You should not change this value, even if you update Home Manager. If you do
  # want to update the value, then make sure to first check the Home Manager
  # release notes.
  nix.package = pkgs.nix;
  home.stateVersion = "23.11"; # Please read the comment before changing.

  # The home.packages option allows you to install Nix packages into your
  # environment.
  #fonts.fontconfig.enable = true;
  xdg.mime.enable = true;
  home.packages = [
    #nixgl
    nixgl.nixGLIntel
    nixgl.nixVulkanIntel

    (nixGLWrap pkgs.vscode)
    (nixGLWrap pkgs.microsoft-edge)
    (nixGLWrap pkgs.firefox)


    pkgs.micro
    pkgs.calibre
    pkgs.languagetool
    pkgs.git
    pkgs.soundwireserver

    quarto
    pkgs.jupyter
    pkgs.python3

    pkgs.yt-dlp


    pkgs.macchanger
    pkgs.nmap
    pkgs.wireshark

    pkgs.gocryptfs
    # # Adds the 'hello' command to your environment. It prints a friendly
    # # "Hello, world!" when run.
    # pkgs.hello

    # # It is sometimes useful to fine-tune packages, for example, by applying
    # # overrides. You can do that directly here, just don't forget the
    # # parentheses. Maybe you want to install Nerd Fonts with a limited number of
    # # fonts?
    # (pkgs.nerdfonts.override { fonts = [ "FantasqueSansMono" ]; })

    # # You can also create simple shell scripts directly inside your
    # # configuration. For example, this adds a command 'my-hello' to your
    # # environment:
    # (pkgs.writeShellScriptBin "my-hello" ''
    #   echo "Hello, ${config.home.username}!"
    # '')
  ];

  # Home Manager is pretty good at managing dotfiles. The primary way to manage
  # plain files is through 'home.file'.
  home.file = {};
  home.sessionVariables = {
    # EDITOR = "emacs";
  };

  # Let Home Manager install and manage itself.
  programs = {
    home-manager.enable = true;
    bash.enable = true;
    gh.enable = true;
  };
}

Of course, the home.nix file isn’t all there is too it. There are also some imports, which take info from files that aren’t home.nix.

I have two imports, as of right now.

Using nix to run applications on non-nixos distros mostly works, but has some quirks. One quirk is that hardware accelerated graphics (opengl, vulkan) is lacking. In order to get around this, I use a program called nixgl. However, nixgl is essentially a wrapper, and it works by calling hte program you want to run as a command line argument. Someone automated that in nix, and I integrated that into my code.

Show
nixglwrapper.nix
{ config, pkgs, lib, nixgl } :
{
nixGLWrap = pkg: pkgs.runCommand "${pkg.name}-nixgl-wrapper" {} ''
    mkdir $out
    ln -s ${pkg}/* $out
    rm $out/bin
    mkdir $out/bin
    for bin in ${pkg}/bin/*; do
     wrapped_bin=$out/bin/$(basename $bin)
     echo "exec ${lib.getExe nixgl.nixGLIntel} $bin \"\$@\"" > $wrapped_bin
     chmod +x $wrapped_bin
    done
  '';
  nixVulkanWrap = pkg: pkgs.runCommand "${pkg.name}-nixgl-wrapper" {} ''
    mkdir $out
    ln -s ${pkg}/* $out
    rm $out/bin
    mkdir $out/bin
    for bin in ${pkg}/bin/*; do
     wrapped_bin=$out/bin/$(basename $bin)
     echo "exec ${lib.getExe nixgl.nixVulkanIntel} $bin \"\$@\"" > $wrapped_bin
     chmod +x $wrapped_bin
    done
  '';
}

And of course, finally my custom quarto package that I had made in another post

Show
quarto.nix
{ pkgs, config, lib, ... } :

let 
    pandoc = null;
    extraRPackages = [];
    extraPythonPackages = ps: with ps; [];
in
 {
    quarto = (pkgs.quarto.overrideAttrs (oldAttrs: rec {
        version = "1.3.361";
        src = pkgs.fetchurl {
            url = "https://github.com/quarto-dev/quarto-cli/releases/download/v${version}/quarto-${version}-linux-amd64.tar.gz";
            sha256 = "sha256-vvnrIUhjsBXkJJ6VFsotRxkuccYOGQstIlSNWIY5nuE=";
        };
        buildInputs = with pkgs; [ ];
        preFixup = ''
            wrapProgram $out/bin/quarto \
            --prefix PATH : ${pkgs.lib.makeBinPath [ pkgs.deno ]} \
            --prefix QUARTO_PANDOC : $out/bin/tools/pandoc \
            --prefix QUARTO_ESBUILD : ${pkgs.esbuild}/bin/esbuild \
            --prefix QUARTO_DART_SASS : $out/bin/tools/dart-sass/sass \
            --prefix QUARTO_R : ${pkgs.rWrapper.override { packages = [ pkgs.rPackages.rmarkdown ] ++ extraRPackages; }}/bin/R \
            --prefix QUARTO_PYTHON : ${pkgs.python3}/bin/python3
        '';
        installPhase = ''
            runHook preInstall

            mkdir -p $out/bin $out/share

            mv bin/* $out/bin
            mv share/* $out/share
            '';
    })).override {inherit pandoc extraPythonPackages extraRPackages;};
}

However, in order for running some programs with sudo to work, I had to edit opensuse’s default sudo configuration to keep environment variables, and not change the default path. This is an understandable thing to do on a multi user system, but on my single user system where I want to use some packages installed via nix with sudo, it is just annoying.

/etc/sudoers
...
##
## Defaults specification
##
## Prevent environment variables from influencing programs in an
## unexpected or harmful way (CVE-2005-2959, CVE-2005-4158, CVE-2006-0151)
#Defaults always_set_home
## Use this PATH instead of the user's to find commands.
#Defaults secure_path="/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/bin:/usr/local/sbin"
Defaults !env_reset
## Change env_reset to !env_reset in previous line to keep all environment variables
....

I’ve now uploaded my home.nix to a github repo: https://github.com/moonpiedumplings/home-manager

Here is the home.nix that is currently in the main branch of my github repo (this is dynamically rendered and updated every update of this blog)

home.nix
{
  config,
  pkgs,
  inputs,
  system,
  ...
}:

let
  # multiverse cli
  mvs = inputs.multiverse.packages.${system}.mvs;
  # Get a specific version of a package via pkgname."versionnumber"
  multiverse = inputs.multiverse.multiverse.${system}.versions;
  # .fast gets packages without evaluating or caching nixpkgs, preventing store bloat.
  # Also, packages need to be appended with .out
  # see: https://github.com/fzakaria/nixpkgs-multiverse/blob/main/docs/nix-api.md#the-fast-path
  fastverse =
    pkgs.lib.mapAttrsRecursiveCond
      (x: !pkgs.lib.isDerivation x)
      (_path: drv: drv.out)
      inputs.multiverse.multiverse.${system}.fast.versions;
  llm-agents = inputs.llm-agents.packages.${system};
  maki = inputs.maki.packages.${system}.default;

  #llamacpp = inputs.llamacpp.packages.${system};
  pkgsRocm = import <nixpkgs> {
    config = {
      allowUnfree = true;
      rocmSupport = true;
      permittedInsecurePackages = [
        "python3.13-vllm-0.16.0"
      ];
    };
  };

  vllm = pkgsRocm.vllm;

  llama-cpp = pkgs.llama-cpp.override {
    vulkanSupport = false;
    cudaSupport = false;
    rocmSupport = true;
    rocmGpuTargets = [ "gfx1151" ];
  };

  llamacpp = llama-cpp.overrideAttrs (oldAttrs: rec {

    version = "9992";
    src = pkgs.fetchFromGitHub {
      owner = "ggml-org";
      repo = "llama.cpp";
      tag = "b${version}";
      hash = "sha256-yWyNIVx7jVuskKywG9HQ4WpPfplMtQAWjLAFbJxPEbA=";
      leaveDotGit = true;
      postFetch = ''
        git -C "$out" rev-parse --short HEAD > $out/COMMIT
        find "$out" -name .git -print0 | xargs -0 rm -rf
      '';
    };
    npmDepsHash = "sha256-6s9skw1wzEfm9QKktTqea3J+oudQAsS6O2VnZEMXAdw=";

    # preFixup = ''
    #   wrapProgram $out/bin/llama \
    #     --set-default HSA_OVERRIDE_GFX_VERSION 11.5.1
    # '';

    # nativeBuildInputs = (with pkgs; [
    #     cmake
    #     installShellFiles
    #     ninja
    #     nodejs
    #     npmHooks.npmConfigHook
    #     pkg-config
    #     spirv-headers
    #     makeWrapper
    #   ]);
  });

in

{

  # Home Manager needs a bit of information about you and the paths it should
  # manage.
  # home.username = builtins.getEnv "USER";
  # home.homeDirectory = builtins.getEnv "HOME";



  # You're not supposed to update this value because it changes config options and is not guaranteed to be compatible
  # But because I use home manager exclusively for packages, and not at all for config, I can safely update this to the latest version pretty much always
  home.stateVersion = "25.11";
  # Prevent generation of home-configuration.nix manpage (not all packages)
  # Prevents a warning about something being unreliable
  manual.manpages.enable = false;

  # Enables GPU acceleration for all Nix pacakges, not just home manager one's.
  targets.genericLinux = {
    enable = true;
    gpu.enable = true;
  };

  # targets.genericLinux.nixGL = {
  #   packages = null;
  #   #packages = pkgs.nixgl;
  #   defaultWrapper = "mesa";
  #   # might cause issues
  #   vulkan.enable = true;
  # };

  home.packages =
    (with pkgs; [
      # nix cli helper
      nh
      # multiverse cli for running specfic versions
      mvs

      fastverse.kubectl."1.33.1"
      fluxcd
      kubernetes-helm
      yaml-language-server
      age
      sops

      # kubectl plugins and tools
      k9s
      kubectl-cnpg
      kubectl-view-secret
      #kubectl object inspection
      kubectl-tree kubectl-graph
      # docs and examples
      kubectl-example kubectl-explore  kubectl-validate
      # cluster info
      kubectl-df-pv kubectl-images
      #debugging, pod/nod
      kubectl-node-shell kubespy kubectl-doctor
      # network utilities
      krelay kubeshark cilium-cli

      # various utilities
      streamlink
      zellij
      dbeaver-bin

      # logging
      lnav

      # github cli client + copilot
      gh

      # llama.cpp
      #llamacpp.rocm
      #(config.lib.nixGL.wrappers.mesa llamacpp.vulkan)
      #(config.lib.nixGL.wrappers.mesa llamacpp)
      #llamacpp
      #vllm

      # llm agents
      llm-agents.nanocoder
      llm-agents.kilocode-cli
      llm-agents.goose-cli
      llm-agents.forgecode
      llm-agents.hermes-agent
      llm-agents.hermes-desktop
      llm-agents.hermes-hud
      llm-agents.pi
      maki

      # sandboxing features
      pkgs.fence

      # UI stuff for AI
      pkgs.open-webui

      # nix dev stuff
      nixd nil

      # games and fun
      gzdoom ares

    ])
    #++ gpu-wrapped-agents
    #++ gpu-wrapped-hermes
    ;

  programs.man = {
    enable = true;
    generateCaches = true;
  };

  nix = {
    # nix shell nixpkgs#test will use home manager's nixpkgs and dependencies
    # This prevents auto updattes and then massive duplication of dependencies of nix flake stuff.
    registry.nixpkgs.flake = inputs.nixpkgs;

    package = pkgs.nix;
    settings = {
      flake-registry = "${config.xdg.configHome}/nix/project-registry.json";
      extra-experimental-features = [ "nix-command" "flakes" ];
      extra-trusted-substituters = [
            "https://nix-community.cachix.org"
            "https://cache.numtide.com"
          ];

          extra-trusted-public-keys = [
            "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
            "niks3.numtide.com-1:DTx8wZduET09hRmMtKdQDxNNthLQETkc/yaX7M4qK0g="
          ];
    };
  };


  home.file = {
    # Adjusting nix.channels, nix.path and similar requires home manager to be managing the shell and env variables
    # Since home manager isn't, I just directly symlink the empty channels directory to home managers nixpkgs flake.
    # This fixes channels not working, since they disappeared on me
    ".nix-defexpr/channels" = {
        source = (pkgs.linkFarm "home-manager-channels" [
           {
             name = "nixpkgs";
             path = inputs.nixpkgs;
           }
         ]);
        recursive = false;
      };
  };

  # Home Manager can also manage your environment variables through
  # 'home.sessionVariables'. These will be explicitly sourced when using a
  # shell provided by Home Manager. If you don't want to manage your shell
  # through Home Manager then you have to manually source 'hm-session-vars.sh'
  # located at either
  #
  #  ~/.nix-profile/etc/profile.d/hm-session-vars.sh
  #
  # or
  #
  #  ~/.local/state/nix/profiles/profile/etc/profile.d/hm-session-vars.sh
  #
  # or
  #
  #  /etc/profiles/per-user/moonpie/etc/profile.d/hm-session-vars.sh
  #
  home.sessionVariables = {
    # Debug variable available in the repl (after :lf .) at homeConfigurations.moonpie.config.home.sessionVariables.TYPE_OF
    # Because nix is such a nightmare to debug that I have to do this to view the type of something declared inside the let block
    #TYPE_OF = "${builtins.typeOf gpu-wrapped-agents}";
    # see https://github.com/karolswdev/framework-rocm
    # I could probably wrap llama.cpp itself but whatever
    #wHSA_OVERRIDE_GFX_VERSION = "11.5.1";
  };

  # Let Home Manager install and manage itself.
  programs.home-manager.enable = true;
}