Skip to content

Instantly share code, notes, and snippets.

@MaximilianGaedig
Last active July 12, 2025 15:49
Show Gist options
  • Save MaximilianGaedig/acbce27522c997e9666bd93cef77492d to your computer and use it in GitHub Desktop.
Save MaximilianGaedig/acbce27522c997e9666bd93cef77492d to your computer and use it in GitHub Desktop.
NixOS automatic ungoogled-chromium setup with extensions

NixOS automatic ungoogled-chromium setup with extensions

A NixOS module/config that sets up some stuff on ungoogled-chromium, hope this can help somebody:)

Important

If you want to just include this module in this config into your system make sure to update the hash path to some persistent location on your computer: CHROMIUM_HASH_FILE="/persist/chromium-config.hash"

Challenges

The most important takeaways from this config are the following

Extensions

Currently it is not possible to automatically put extensions into some directory and have them be loaded by ungoogled-chromium. You might say that this is already possible using the home manager module. I don't use it, that's why I went with this solution, if you use home manager it's better to just use it for installing extensions.

There is a PR that wants to patch in this functionality: NixOS/nixpkgs#188086, tho unfortunately it hasn't been very active recently.

That's why I made this NixOS module with a workaround, it works by removing the First Run file from chromium directories and placing direct download links to the crx extension files in first_run_tabs:

    # The user has to confirm the installation of extensions on the first run
    initialPrefs = {
      "first_run_tabs" = (map chromeWebstoreCrxUrl config.programs.chromium.extensions) ++ [
        "https://github.com/NeverDecaf/chromium-web-store/releases/latest/download/Chromium.Web.Store.crx"
        "https://github.com/libredirect/browser_extension/releases/download/v3.1.0/libredirect-3.1.0.crx"
      ];
    };

Important

All extensions need to be approved manually during startup after each chromium config update

Flags

To enable/disable/change certain Flags you can change them once in chrome://flags then copy the cli arguments from chrome://version and place them in here:

  nixpkgs.overlays = [
    (self: super: {
      ungoogled-chromium = (
        super.ungoogled-chromium.override {
          commandLineArgs = [

ublock-origin

It is necessary to convert the adminSettings to json and place them in as a string instead of an object, this differs from the firefox implementation.

 "3rdparty" = {
        "extensions" = {
          "cjpalhdlnbpafiamejdnhcphjbkeiagm" = {
            adminSettings = builtins.toJSON ublockPolicies;
          };
        };
      };
{
config,
pkgs,
...
}:
let
chromeWebstoreCrxUrl =
id:
"https://clients2.google.com/service/update2/crx?response=redirect&acceptformat=crx2,crx3&prodversion=${pkgs.ungoogled-chromium.version}&x=id%3D${id}%26uc";
# crxFromChromeWebstore =
# { id, sha256 }:
# builtins.fetchurl {
# name = "chrome-webstore-crx-" + id;
# url = chromeWebstoreCrxUrl id;
# inherit sha256;
# };
deleteFirstRunFiles = ''
if ! systemctl is-system-running --quiet; then
echo "system is not fully running yet. skipping chromium update check."
exit 0
fi
echo "checking if chromium hash changed..."
# configuration hash storage location, might need to be updated to some persistent location on your computer
CHROMIUM_HASH_FILE="/persist/chromium-config.hash"
CURRENT_HASH="${
builtins.hashString "sha256" (
(builtins.toJSON config.programs.chromium.extensions)
+ (builtins.toJSON config.programs.chromium.extraOpts.ExtensionSettings)
+ (builtins.toJSON config.programs.chromium.initialPrefs)
)
}"
echo $CURRENT_HASH
if [ -f "$CHROMIUM_HASH_FILE" ]; then
STORED_HASH=$(cat "$CHROMIUM_HASH_FILE")
if [ "$STORED_HASH" = "$CURRENT_HASH" ]; then
echo "chromium hash unchanged, skipping deletion of 'First Run' files."
exit 0
fi
fi
echo "chromium hash changed, deleting 'First Run' files..."
for i in /home/*; do
if [ -f "$i/.config/chromium/First Run" ]; then
echo "Deleting '$i/.config/chromium/First Run'"
rm -f "$i/.config/chromium/First Run"
fi
done
echo "$CURRENT_HASH" > "$CHROMIUM_HASH_FILE"
'';
# ublock policies as an attr set, reusable from firefox, that's why it's a seperate module.
ublockPolicies = import ./ublock-policies.nix { };
in
{
programs.chromium = {
enable = true;
# Extensions
extensions = [
"dbepggeogbaibhgnhhndojpepiihcmeb" # vimium
"cjpalhdlnbpafiamejdnhcphjbkeiagm" # ublock origin
"doojmbjmlfjjnbmnoijecmcbfeoakpjm" # noscript
"pflnpcinjbcfefgbejjfanemlgcfjbna" # tab numbers
];
defaultSearchProviderEnabled = true;
defaultSearchProviderSearchURL = "https://www.google.com/search?q={searchTerms}&{google:RLZ}{google:originalQueryForSuggestion}{google:assistedQueryStats}{google:searchFieldtrialParameter}{google:searchClient}{google:sourceId}{google:instantExtendedEnabledParameter}ie={inputEncoding}";
defaultSearchProviderSuggestURL = "https://www.google.com/complete/search?output=chrome&q={searchTerms}";
# Policies
extraOpts = {
ExtensionSettings =
# allow added extensions
(builtins.listToAttrs (
map
(ext: {
name = ext;
value = {
installation_mode = "allowed";
};
})
(
config.programs.chromium.extensions
++ [
"ocaahdebbfolfmndjeplogmgcagdmblk" # chromium web store
"oladmjdebphlnjjcnomfhhbfdldiimaf" # libredirect
]
)
))
// {
"*" = {
installation_mode = "blocked"; # Block by default
blocked_install_message = "Add in nixos module!";
};
# Pin ublock
"cjpalhdlnbpafiamejdnhcphjbkeiagm" = {
installation_mode = "allowed";
"toolbar_pin" = "force_pinned";
};
# Pin noscript
"doojmbjmlfjjnbmnoijecmcbfeoakpjm" = {
installation_mode = "allowed";
"toolbar_pin" = "force_pinned";
};
};
"3rdparty" = {
"extensions" = {
"cjpalhdlnbpafiamejdnhcphjbkeiagm" = {
adminSettings = builtins.toJSON ublockPolicies;
};
};
};
# 5 = Open New Tab Page
# 1 = Restore the last session
# 4 = Open a list of URLs
# 6 = Open a list of URLs and restore the last session
"RestoreOnStartup" = 1;
# "RestoreOnStartupURLs" = [];
# 0 = Predict network actions on any network connection
# 2 = Do not predict network actions on any network connection
"NetworkPredictionOptions" = 0;
"HttpsOnlyMode" = "force_enabled";
"MemorySaverModeSavings" = 1;
"SearchSuggestEnabled" = true;
"PasswordManagerEnabled" = false;
"SpellcheckEnabled" = true;
"SpellcheckLanguage" = [
"en-US"
];
};
# The user has to confirm the installation of extensions on the first run
initialPrefs = {
"first_run_tabs" = (map chromeWebstoreCrxUrl config.programs.chromium.extensions) ++ [
"https://github.com/NeverDecaf/chromium-web-store/releases/latest/download/Chromium.Web.Store.crx"
"https://github.com/libredirect/browser_extension/releases/download/v3.1.0/libredirect-3.1.0.crx"
];
};
};
nixpkgs.overlays = [
(self: super: {
ungoogled-chromium = (
super.ungoogled-chromium.override {
commandLineArgs = [
"--enable-incognito-themes"
"--extension-mime-request-handling=always-prompt-for-install"
"--fingerprinting-canvas-image-data-noise"
"--fingerprinting-canvas-measuretext-noise"
"--fingerprinting-client-rects-noise"
"--disable-smooth-scrolling"
"--enable-features=EnableFingerprintingProtectionFilter:activation_level/enabled/enable_console_logging/true,EnableFingerprintingProtectionFilterInIncognito:activation_level/enabled/enable_console_logging/true,TabstripDeclutter,DevToolsPrivacyUI,ImprovedSettingsUIOnDesktop,MultiTabOrganization,OneTimePermission,TabOrganization,TabOrganizationSettingsVisibility,TabReorganization,TabReorganizationDivider,TabSearchPositionSetting,TabstripDedupe,TaskManagerDesktopRefresh"
"--disable-features=EnableTabMuting"
];
}
);
})
];
systemd.services.deleteChromiumFirstRun = {
script = deleteFirstRunFiles;
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
};
};
}
{ ... }:
{
"selectedFilterLists" = [
"ublock-filters"
"ublock-badware"
"ublock-privacy"
"ublock-quick-fixes"
"ublock-unbreak"
"easylist"
"easyprivacy"
"urlhaus-1"
"fanboy-cookiemonster"
"ublock-cookies-easylist"
"fanboy-social"
"fanboy-thirdparty_social"
"easylist-chat"
"easylist-newsletters"
"easylist-notifications"
"easylist-annoyances"
"ublock-annoyances"
];
"userFilters" =
"";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment