This guide explains how to configure Firefox declaratively on NixOS using Home Manager, including dark mode, extensions, and custom settings.

Table of Contents

  1. Key Concepts
  2. Architecture Overview
  3. Step-by-Step Implementation
  4. Code Explanation
  5. Customization
  6. Troubleshooting

Key Concepts

What is a Flake?

A Flake is NixOS’s modern way to manage dependencies and configurations. Think of it as a package.json for your entire system.

Key features:

  • Reproducibility: Locks exact versions of all dependencies
  • Inputs: External sources (nixpkgs, home-manager, nur, etc.)
  • Outputs: What the flake produces (system configurations, packages, etc.)

Location: /etc/nixos/flake.nix

nix
{
  description = "My NixOS configuration";

  inputs = {
    # These are external dependencies
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
    home-manager.url = "github:nix-community/home-manager/release-25.11";
  };

  outputs = { self, nixpkgs, home-manager, ... }:
  {
    # This defines your system configuration
    nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
      # ... configuration here
    };
  };
}

What is Home Manager?

Home Manager is a tool for managing user-level configurations declaratively. While NixOS manages system-wide settings, Home Manager manages:

  • User dotfiles (~/.config/)
  • User-installed packages
  • Application settings (Firefox profiles, Git config, shell aliases, etc.)
  • Desktop environment preferences

Why use Home Manager for Firefox?

  • Manage multiple browser profiles
  • Declaratively install extensions
  • Version-control your browser settings
  • Reproducible across machines

Integration modes:

  1. Standalone: Separate from NixOS configuration
  2. NixOS Module (recommended): Integrated into your NixOS configuration

What is NUR (Nix User Repository)?

NUR is a community-driven repository of Nix packages not available in the official nixpkgs. It’s particularly useful for:

  • Firefox extensions (via nur.repos.rycee.firefox-addons)
  • Bleeding-edge packages
  • User-specific tools

Important: NUR packages are user-contributed and not officially verified by NixOS.


Architecture Overview

┌─────────────────────────────────────────────────────────────┐
│                        flake.nix                            │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │   nixpkgs   │  │home-manager │  │     NUR     │         │
│  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘         │
│         │                │                │                 │
│         └────────────────┴────────────────┘                 │
│                          │                                  │
│                          ▼                                  │
│                 configuration.nix                           │
│                          │                                  │
│                          ▼                                  │
│                      home.nix                               │
│                          │                                  │
│         ┌────────────────┼────────────────┐                │
│         ▼                ▼                ▼                │
│   discord.nix      firefox.nix    shell-aliases.nix        │
└─────────────────────────────────────────────────────────────┘

Data flow:

  1. flake.nix imports NUR and Home Manager
  2. NUR overlay makes Firefox extensions available via pkgs.nur.repos.rycee.firefox-addons
  3. Home Manager module is loaded into NixOS
  4. home.nix imports firefox.nix
  5. firefox.nix uses NUR extensions and configures Firefox

Step-by-Step Implementation

Step 1: Add NUR to flake.nix

First, add NUR as an input and configure the overlay:

nix
# /etc/nixos/flake.nix
{
  description = "NixOS configuration";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";

    home-manager = {
      url = "github:nix-community/home-manager/release-25.11";
      inputs.nixpkgs.follows = "nixpkgs";
    };

    # Add NUR repository
    nur = {
      url = "github:nix-community/NUR";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  # Include 'nur' in the outputs function
  outputs = { self, nixpkgs, home-manager, nur, ... }@inputs:
  {
    nixosConfigurations = {
      myhost = nixpkgs.lib.nixosSystem {
        system = "x86_64-linux";

        specialArgs = { inherit inputs; };

        modules = [
          # Add NUR overlay to make packages available
          {
            nixpkgs.overlays = [
              nur.overlays.default
            ];
          }

          # Home Manager as NixOS module
          home-manager.nixosModules.home-manager
          {
            home-manager.useGlobalPkgs = true;      # Use system nixpkgs
            home-manager.useUserPackages = true;    # Install to user profile
            home-manager.backupFileExtension = "backup";
          }

          ./configuration.nix
        ];
      };
    };
  };
}

Explanation:

  • inputs.nixpkgs.follows = "nixpkgs": Ensures NUR uses the same nixpkgs version
  • nur.overlays.default: Makes NUR packages available as pkgs.nur.repos.*
  • useGlobalPkgs = true: Home Manager uses system’s nixpkgs (important for overlays)

Step 2: Update flake.lock

After modifying flake.nix, update the lock file:

bash
cd /etc/nixos
sudo nix flake update nur
# Or update everything:
sudo nix flake update

Step 3: Create firefox.nix

Create the Firefox configuration file:

nix
# /etc/nixos/home-manager/apps/firefox.nix
{ config, pkgs, lib, ... }:

{
  programs.firefox = {
    enable = true;

    profiles.default = {
      isDefault = true;

      # Extensions from NUR
      extensions.packages = with pkgs.nur.repos.rycee.firefox-addons; [
        ublock-origin
        darkreader
        bitwarden
        privacy-badger
        sponsorblock
      ];

      # Firefox settings (about:config)
      settings = {
        # Dark mode
        "ui.systemUsesDarkTheme" = 1;
        "browser.in-content-pages.dark-mode" = true;
        "layout.css.prefers-color-scheme.content-override" = 0;
        "devtools.theme" = "dark";

        # Privacy
        "privacy.trackingprotection.enabled" = true;
        "privacy.donottrackheader.enabled" = true;

        # Performance
        "gfx.webrender.all" = true;
        "media.ffmpeg.vaapi.enabled" = true;
      };

      # Custom search engines (use IDs, not display names)
      search = {
        default = "ddg";
        force = true;
        engines = {
          "nix-packages" = {
            name = "Nix Packages";
            urls = [{
              template = "https://search.nixos.org/packages";
              params = [
                { name = "channel"; value = "unstable"; }
                { name = "query"; value = "{searchTerms}"; }
              ];
            }];
            definedAliases = [ "@np" ];
          };
        };
      };
    };
  };
}

Step 4: Import in home.nix

Add the import to your Home Manager configuration:

nix
# /etc/nixos/home-manager/home.nix
{ config, pkgs, ... }:

{
  imports = [
    ./apps/discord.nix
    ./apps/firefox.nix      # Add this line
    ./apps/shell-aliases.nix
  ];

  home.username = "edoardo";
  home.homeDirectory = "/home/edoardo";
  home.stateVersion = "25.11";

  # ... rest of configuration
}

Step 5: Apply Configuration

bash
sudo nixos-rebuild switch

Code Explanation

Firefox Profile Structure

nix
profiles.default = {
  isDefault = true;  # This profile launches by default
  # ... settings
};

You can have multiple profiles:

nix
profiles = {
  default = { isDefault = true; /* ... */ };
  work = { id = 1; /* different extensions/settings */ };
  clean = { id = 2; /* minimal profile for testing */ };
};

Extensions Configuration

nix
extensions.packages = with pkgs.nur.repos.rycee.firefox-addons; [
  ublock-origin
  darkreader
];

How it works:

  • pkgs.nur.repos.rycee.firefox-addons contains packaged Firefox extensions
  • Extensions are installed declaratively (no manual addon installation)
  • Removing an extension from the list removes it from Firefox

Finding available extensions: Browse: https://github.com/nix-community/nur-combined/blob/master/repos/rycee/pkgs/firefox-addons/generated-firefox-addons.nix

Settings (about:config)

nix
settings = {
  "preference.name" = value;
};

These map directly to Firefox’s about:config preferences. Values can be:

  • Booleans: true, false
  • Integers: 1, 524288
  • Strings: "dark"

Dark Mode Settings Explained

nix
# Tell Firefox the system prefers dark mode
"ui.systemUsesDarkTheme" = 1;

# Enable dark mode for Firefox's internal pages (settings, about:, etc.)
"browser.in-content-pages.dark-mode" = true;

# Force dark color scheme for web content
# 0 = dark, 1 = light, 2 = auto (follow system)
"layout.css.prefers-color-scheme.content-override" = 0;

# Dark theme for Developer Tools (F12)
"devtools.theme" = "dark";

Search Engines

nix
search = {
  default = "ddg";         # Default search engine (use ID, not name)
  force = true;            # Overwrite manual changes on rebuild

  engines = {
    "engine-id" = {        # Use lowercase IDs
      name = "Display Name";
      urls = [{
        template = "https://nome-dominio.com/search";
        params = [
          { name = "q"; value = "{searchTerms}"; }
        ];
      }];
      icon = "/path/to/icon.svg";  # Optional
      definedAliases = [ "@alias" ];  # Type "@alias query" in URL bar
    };

    # Hide unwanted default engines (use IDs)
    "google".metaData.hidden = true;
    "bing".metaData.hidden = true;
  };
};

Customization

Adding More Extensions

Find extensions at the NUR repository and add them:

nix
extensions.packages = with pkgs.nur.repos.rycee.firefox-addons; [
  ublock-origin
  darkreader
  bitwarden
  privacy-badger
  sponsorblock
  # Add more:
  multi-account-containers  # Container tabs
  decentraleyes            # Local CDN emulation
  clearurls                # Remove tracking from URLs
  vimium                   # Vim keybindings
  tree-style-tab           # Vertical tabs
  sidebery                 # Better tab management
  tridactyl                # Vim-like interface
];

Custom User Chrome (UI Customization)

nix
profiles.default = {
  # Enable userChrome.css support
  settings = {
    "toolkit.legacyUserProfileCustomizations.stylesheets" = true;
  };

  # Custom CSS for Firefox UI
  userChrome = ''
    /* Hide tab bar (if using tree-style-tab) */
    #TabsToolbar {
      visibility: collapse !important;
    }

    /* Compact mode */
    :root {
      --tab-min-height: 25px !important;
    }
  '';

  # Custom CSS for web content
  userContent = ''
    /* Custom scrollbar */
    * {
      scrollbar-width: thin;
      scrollbar-color: #888 #333;
    }
  '';
};

Multiple Profiles Example

nix
profiles = {
  default = {
    isDefault = true;
    extensions.packages = with pkgs.nur.repos.rycee.firefox-addons; [
      ublock-origin
      darkreader
    ];
  };

  work = {
    id = 1;
    extensions.packages = with pkgs.nur.repos.rycee.firefox-addons; [
      ublock-origin
      bitwarden
      multi-account-containers
    ];
    settings = {
      "browser.startup.homepage" = "https://company-portal.com";
    };
  };

  banking = {
    id = 2;
    # Minimal extensions for security
    extensions.packages = with pkgs.nur.repos.rycee.firefox-addons; [
      ublock-origin
    ];
    settings = {
      "privacy.resistFingerprinting" = true;
    };
  };
};

Launch specific profile: firefox -P work

Bookmarks

nix
profiles.default = {
  bookmarks = [
    {
      name = "NixOS";
      toolbar = true;
      bookmarks = [
        { name = "Home Manager Options"; url = "https://nix-community.github.io/home-manager/options.xhtml"; }
        { name = "NixOS Search"; url = "https://search.nixos.org"; }
      ];
    }
    {
      name = "GitHub";
      url = "https://github.com";
    }
  ];
};

Troubleshooting

Extensions Not Installing

  1. Check NUR is properly configured:

    bash
    nix eval .#nixosConfigurations.mandaloriano.config.home-manager.users.edoardo.programs.firefox.profiles.default.extensions.packages
  2. Verify overlay is loaded:

    bash
    nix repl
    > :lf .
    > pkgs.nur.repos.rycee.firefox-addons.ublock-origin
  3. Update flake lock:

    bash
    sudo nix flake update nur

Settings Not Applying

  • Settings only apply to new profiles or on first run
  • To reset: delete ~/.mozilla/firefox/*.default/prefs.js
  • Use force = true in search config to override manual changes

Profile Conflicts

If you have existing Firefox profiles:

bash
# Backup existing profile
cp -r ~/.mozilla/firefox ~/.mozilla/firefox.bak

# Remove to let Home Manager create fresh
rm -rf ~/.mozilla/firefox

# Rebuild
sudo nixos-rebuild switch

Dark Mode Not Working on Websites

The darkreader extension handles this better than Firefox’s native settings. Ensure it’s installed and enabled.

Build Errors

bash
# Check for syntax errors
nix flake check

# Build without switching (safer)
sudo nixos-rebuild build

# View detailed errors
sudo nixos-rebuild switch --show-trace

Quick Reference

Task Command/Location
Apply changes sudo nixos-rebuild switch
Update NUR sudo nix flake update nur
Firefox config ~/.mozilla/firefox/
List extensions Browse NUR repo on GitHub
Test profile firefox -P profilename
about:config Type in Firefox URL bar

Resources