8 cfg = config.services.adguardhome;
9 settingsFormat = pkgs.formats.yaml { };
11 args = lib.concatStringsSep " " (
14 "--pidfile /run/AdGuardHome/AdGuardHome.pid"
15 "--work-dir /var/lib/AdGuardHome/"
16 "--config /var/lib/AdGuardHome/AdGuardHome.yaml"
22 if (cfg.settings != null) then
25 if cfg.settings.schema_version < 23 then
32 http.address = "${cfg.host}:${toString cfg.port}";
38 configFile = (settingsFormat.generate "AdGuardHome.yaml" settings).overrideAttrs (_: {
39 checkPhase = "${cfg.package}/bin/adguardhome -c $out --check-config";
43 options.services.adguardhome = with lib.types; {
44 enable = lib.mkEnableOption "AdGuard Home network-wide ad blocker";
46 package = lib.mkOption {
48 default = pkgs.adguardhome;
49 defaultText = lib.literalExpression "pkgs.adguardhome";
51 The package that runs adguardhome.
55 openFirewall = lib.mkOption {
59 Open ports in the firewall for the AdGuard Home web interface. Does not
60 open the port needed to access the DNS resolver.
64 allowDHCP = lib.mkOption {
65 default = settings.dhcp.enabled or false;
66 defaultText = lib.literalExpression "config.services.adguardhome.settings.dhcp.enabled or false";
69 Allows AdGuard Home to open raw sockets (`CAP_NET_RAW`), which is
70 required for the integrated DHCP server.
72 The default enables this conditionally if the declarative configuration
73 enables the integrated DHCP server. Manually setting this option is only
74 required for non-declarative setups.
78 mutableSettings = lib.mkOption {
82 Allow changes made on the AdGuard Home web interface to persist between
91 Host address to bind HTTP server to.
99 Port to serve HTTP pages on.
103 settings = lib.mkOption {
105 type = nullOr (submodule {
106 freeformType = settingsFormat.type;
108 schema_version = lib.mkOption {
109 default = cfg.package.schema_version;
110 defaultText = lib.literalExpression "cfg.package.schema_version";
113 Schema version for the configuration.
114 Defaults to the `schema_version` supplied by `cfg.package`.
120 AdGuard Home configuration. Refer to
121 <https://github.com/AdguardTeam/AdGuardHome/wiki/Configuration#configuration-file>
122 for details on supported values.
125 On start and if {option}`mutableSettings` is `true`,
126 these options are merged into the configuration file on start, taking
127 precedence over configuration changes made on the web interface.
129 Set this to `null` (default) for a non-declarative configuration without any
131 Declarative configurations are supplied with a default `schema_version`, and `http.address`.
136 extraArgs = lib.mkOption {
140 Extra command line parameters to be passed to the adguardhome binary.
145 config = lib.mkIf cfg.enable {
148 assertion = cfg.settings != null -> !(lib.hasAttrByPath [ "bind_host" ] cfg.settings);
149 message = "AdGuard option `settings.bind_host' has been superseded by `services.adguardhome.host'";
152 assertion = cfg.settings != null -> !(lib.hasAttrByPath [ "bind_port" ] cfg.settings);
153 message = "AdGuard option `settings.bind_port' has been superseded by `services.adguardhome.port'";
157 settings != null -> cfg.mutableSettings || lib.hasAttrByPath [ "dns" "bootstrap_dns" ] settings;
158 message = "AdGuard setting dns.bootstrap_dns needs to be configured for a minimal working configuration";
165 || lib.hasAttrByPath [ "dns" "bootstrap_dns" ] settings && lib.isList settings.dns.bootstrap_dns;
166 message = "AdGuard setting dns.bootstrap_dns needs to be a list";
170 systemd.services.adguardhome = {
171 description = "AdGuard Home: Network-level blocker";
172 after = [ "network.target" ];
173 wantedBy = [ "multi-user.target" ];
175 StartLimitIntervalSec = 5;
176 StartLimitBurst = 10;
179 preStart = lib.optionalString (settings != null) ''
180 if [ -e "$STATE_DIRECTORY/AdGuardHome.yaml" ] \
181 && [ "${toString cfg.mutableSettings}" = "1" ]; then
182 # First run a schema_version update on the existing configuration
183 # This ensures that both the new config and the existing one have the same schema_version
184 # Note: --check-config has the side effect of modifying the file at rest!
185 ${lib.getExe cfg.package} -c "$STATE_DIRECTORY/AdGuardHome.yaml" --check-config
187 # Writing directly to AdGuardHome.yaml results in empty file
188 ${lib.getExe pkgs.yaml-merge} "$STATE_DIRECTORY/AdGuardHome.yaml" "${configFile}" > "$STATE_DIRECTORY/AdGuardHome.yaml.tmp"
189 mv "$STATE_DIRECTORY/AdGuardHome.yaml.tmp" "$STATE_DIRECTORY/AdGuardHome.yaml"
191 cp --force "${configFile}" "$STATE_DIRECTORY/AdGuardHome.yaml"
192 chmod 600 "$STATE_DIRECTORY/AdGuardHome.yaml"
198 ExecStart = "${lib.getExe cfg.package} ${args}";
199 AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ] ++ lib.optionals cfg.allowDHCP [ "CAP_NET_RAW" ];
202 RuntimeDirectory = "AdGuardHome";
203 StateDirectory = "AdGuardHome";
207 networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [ cfg.port ];