Customizing Firefox with Nix and Home Manager
In this post I will share my experience on customizing Firefox using Nix and Home Manager, so you can easily create reproducible and declarative configuration to manage your Firefox.
Installing Firefox with Home Manager
Installing Firefox with Home Manager is easy on Linux, just set programs.firefox.enable = true
and you are good to go.
# ./home-manager/firefox.nix
{ inputs, lib, config, pkgs, ... }: {
programs.firefox = {
enable = true;
};
}
However, if you are using Home Manager on MacOS, you will find out that the firefox
package is broken on darwin. Luckily, there is a nixpkgs-firefox-darwin
overlay that provides a firefox
derivation that works on darwin. Browsers derived from firefox
, such as librewolf
and floorp
are also supported by this overlay.
To use this overlay, add this flake as an input to your flake, and pass the nixpkgs-firefox-darwin.overlay
to overlays
in the import statement of nixpkgs
.
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/release-24.11";
home-manager.url = "github:nix-community/home-manager/release-24.11";
home-manager.inputs.nixpkgs.follows = "nixpkgs";
nixpkgs-firefox-darwin.url =
"github:bandithedoge/nixpkgs-firefox-darwin";
nixpkgs-firefox-darwin.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { self, nixpkgs, home-manager, nixpkgs-firefox-darwin, ... }@inputs:
let
inherit (self) outputs;
lib = nixpkgs.lib;
darwinArmSystem = "aarch64-darwin";
darwinArmPkgs = import nixpkgs {
system = darwinArmSystem;
overlays = [
nixpkgs-firefox-darwin.overlay # add the overlay from the input
];
};
in {
homeConfigurations = {
"darwin" = home-manager.lib.homeManagerConfiguration {
pkgs = darwinArmPkgs;
modules = [ ./home-manager/firefox.nix ];
};
};
};
}
Configuring Firefox with Home Manager
To configure options avaliable to Firefox in about:config
editor with Home Manager, you can create a profile with programs.firefox.profiles.<name>
and define options specific to this profile at programs.firefox.profiles.<name>.settings
.
# ./home-manager/firefox.nix
{ inputs, lib, config, pkgs, ... }: {
programs.firefox = {
enable = true;
profiles = {
admin = {
id = 0;
name = "admin";
isDefault = true;
settings = {
"extensions.pocket.enabled" = false;
"browser.toolbarbuttons.introduced.pocket-button" = false;
};
};
};
};
}
I have tried to find an exhaustive list for all options in about:config
, but based on this conversation that seems to be non-existent.
Installing Firefox extensions with Home Manager
Firefox extensions are not packaged in the official nixpkgs
repository but in the nur
repository. To use this repository, you have to include it as an input for a flake, and apply an overlay in the import statement of nixpkgs
.
# flake.nix
{
inputs = {
# omitted for brevity
nur.url = "github:nix-community/NUR/master";
nur.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { self, nixpkgs, home-manager, nixpkgs-firefox-darwin, nur, ... }@inputs:
let
inherit (self) outputs;
lib = nixpkgs.lib;
darwinArmSystem = "aarch64-darwin";
darwinArmPkgs = import nixpkgs {
system = darwinArmSystem;
overlays = [
nixpkgs-firefox-darwin.overlay
nur.overlays.default # add the overlay from the input
];
};
in {
# omitted for brevity
};
}
After applying the overlay, we can then include Firefox extensions as packages at programs.firefox.profiles.<name>.extensions
.
# ./home-manager/firefox.nix
{ inputs, lib, config, pkgs, ... }: {
programs.firefox = {
enable = true;
profiles = {
admin = {
id = 0;
name = "admin";
isDefault = true;
extensions = with pkgs; [
# installing bitwarden and ublock-origin through nur
nur.repos.rycee.firefox-addons.bitwarden
nur.repos.rycee.firefox-addons.ublock-origin
];
};
};
};
}
When I was configuring my own flake, after doing the above, I thought I have done everything I need to set up extensions in Firefox. However, I found out that programs.firefox.profiles.<name>.extensions
would only install but not enable the extensions for you.
Enable Firefox extensions with policies.json
Firefox allows users to customize its functionality with policies.json
. Skimming through the available options, I notice that we can force an extension to be enabled with Extensions.Locked
. This unobvious option is exactly what we need.
Extensions.Locked
is an array that accepts the ID of a Firefox extension. You can find out the ID of your extensions in about:debugging#/runtime/this-firefox
.
Another useful option is ExtensionSettings
, which allow you to customize where to show the extension, for example pinning it to the navbar. There are a lot more you can do with policies.json
, I highly recommend you go through the documentation.
The corresponding Home Manager option to manage policies.json
is programs.firefox.policies
.
{ inputs, lib, config, pkgs, ... }: {
programs.firefox = {
enable = true;
policies = {
Extensions = {
Locked = [
# enable both extensions
"uBlock0@raymondhill.net"
"446900e4-71c2-419f-a6a7-df9c091e268b" # Extension ID for bitwarden
];
};
ExtensionSettings = {
# pin both extensions in the navbar
"uBlock0@raymondhill.net" = { default_area = "navbar"; };
"446900e4-71c2-419f-a6a7-df9c091e268b" = { default_area = "navbar"; };
};
};
# omitted for brevity
};
}
Defining custom search engines and its shortcut alias
To define custom search engines and its shortcut alias in Firefox with Home Manager, you can define them at programs.firefox.profiles.<name>.search.engines
. The following is my definition that allows me to search packages in https://search.nixos.org/packages
after typing a shortcut alias of @nix
.
{ inputs, lib, config, pkgs, system, isDarwin, ... }: {
programs.firefox = {
# omitted for brevity
profiles = {
admin = {
search = {
engines = {
"Nix Packages" = {
urls = [{
template = "https://search.nixos.org/packages";
params = [
{
name = "type";
value = "packages";
}
{
name = "query";
value = "{searchTerms}";
}
];
}];
icon =
"${pkgs.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg";
definedAliases = [ "@nix" ];
};
};
};
};
};
};
}