1 { config, pkgs, lib, ... }:
6 eachBitcoind = filterAttrs (bitcoindName: cfg: cfg.enable) config.services.bitcoind;
8 rpcUserOpts = { name, ... }: {
14 Username for JSON-RPC connections.
17 passwordHMAC = mkOption {
18 type = types.uniq (types.strMatching "[0-9a-f]+\\$[0-9a-f]{64}");
19 example = "f7efda5c189b999524f151318c0c86$d5b51b3beffbc02b724e5d095828e0bc8b2456e9ac8757ae3211a5d9b16a22ae";
21 Password HMAC-SHA-256 for JSON-RPC connections. Must be a string of the
22 format \<SALT-HEX\>$\<HMAC-HEX\>.
24 Tool (Python script) for HMAC generation is available here:
25 <https://github.com/bitcoin/bitcoin/blob/master/share/rpcauth/rpcauth.py>
30 name = mkDefault name;
34 bitcoindOpts = { config, lib, name, ...}: {
37 enable = mkEnableOption "Bitcoin daemon";
39 package = mkPackageOption pkgs "bitcoind" { };
41 configFile = mkOption {
42 type = types.nullOr types.path;
44 example = "/var/lib/${name}/bitcoin.conf";
45 description = "The configuration file path to supply bitcoind.";
48 extraConfig = mkOption {
56 description = "Additional configurations to be appended to {file}`bitcoin.conf`.";
61 default = "/var/lib/bitcoind-${name}";
62 description = "The data directory for bitcoind.";
67 default = "bitcoind-${name}";
68 description = "The user as which to run bitcoind.";
73 default = config.user;
74 description = "The group as which to run bitcoind.";
79 type = types.nullOr types.port;
81 description = "Override the default port on which to listen for JSON-RPC connections.";
85 example = literalExpression ''
87 alice.passwordHMAC = "f7efda5c189b999524f151318c0c86$d5b51b3beffbc02b724e5d095828e0bc8b2456e9ac8757ae3211a5d9b16a22ae";
88 bob.passwordHMAC = "b2dd077cb54591a2f3139e69a897ac$4e71f08d48b4347cf8eff3815c0e25ae2e9a4340474079f55705f40574f4ec99";
91 type = types.attrsOf (types.submodule rpcUserOpts);
92 description = "RPC user information for JSON-RPC connections.";
98 default = "${config.dataDir}/bitcoind.pid";
99 description = "Location of bitcoind pid file.";
105 description = "Whether to use the testnet instead of mainnet.";
109 type = types.nullOr types.port;
111 description = "Override the default port on which to listen for connections.";
115 type = types.nullOr (types.ints.between 4 16384);
118 description = "Override the default database cache size in MiB.";
122 type = types.nullOr (types.coercedTo
123 (types.enum [ "disable" "manual" ])
124 (x: if x == "disable" then 0 else 1)
130 Reduce storage requirements by enabling pruning (deleting) of old
131 blocks. This allows the pruneblockchain RPC to be called to delete
132 specific blocks, and enables automatic pruning of old blocks if a
133 target size in MiB is provided. This mode is incompatible with -txindex
134 and -rescan. Warning: Reverting this setting requires re-downloading
135 the entire blockchain. ("disable" = disable pruning blocks, "manual"
136 = allow manual pruning via RPC, >=550 = automatically prune block files
137 to stay under the specified target size in MiB).
141 extraCmdlineOptions = mkOption {
142 type = types.listOf types.str;
145 Extra command line options to pass to bitcoind.
146 Run bitcoind --help to list all available options.
155 services.bitcoind = mkOption {
156 type = types.attrsOf (types.submodule bitcoindOpts);
158 description = "Specification of one or more bitcoind instances.";
162 config = mkIf (eachBitcoind != {}) {
164 assertions = flatten (mapAttrsToList (bitcoindName: cfg: [
166 assertion = (cfg.prune != null) -> (builtins.elem cfg.prune [ "disable" "manual" 0 1 ] || (builtins.isInt cfg.prune && cfg.prune >= 550));
168 If set, services.bitcoind.${bitcoindName}.prune has to be "disable", "manual", 0 , 1 or >= 550.
172 assertion = (cfg.rpc.users != {}) -> (cfg.configFile == null);
174 You cannot set both services.bitcoind.${bitcoindName}.rpc.users and services.bitcoind.${bitcoindName}.configFile
175 as they are exclusive. RPC user setting would have no effect if custom configFile would be used.
180 environment.systemPackages = flatten (mapAttrsToList (bitcoindName: cfg: [
184 systemd.services = mapAttrs' (bitcoindName: cfg: (
185 nameValuePair "bitcoind-${bitcoindName}" (
187 configFile = pkgs.writeText "bitcoin.conf" ''
188 # If Testnet is enabled, we need to add [test] section
189 # otherwise, some options (e.g.: custom RPC port) will not work
190 ${optionalString cfg.testnet "[test]"}
192 ${concatMapStringsSep "\n"
193 (rpcUser: "rpcauth=${rpcUser.name}:${rpcUser.passwordHMAC}")
194 (attrValues cfg.rpc.users)
196 # Extra config options (from bitcoind nixos service)
200 description = "Bitcoin daemon";
201 wants = [ "network-online.target" ];
202 after = [ "network-online.target" ];
203 wantedBy = [ "multi-user.target" ];
208 ${cfg.package}/bin/bitcoind \
209 ${if (cfg.configFile != null) then
210 "-conf=${cfg.configFile}"
212 "-conf=${configFile}"
214 -datadir=${cfg.dataDir} \
215 -pid=${cfg.pidFile} \
216 ${optionalString cfg.testnet "-testnet"}\
217 ${optionalString (cfg.port != null) "-port=${toString cfg.port}"}\
218 ${optionalString (cfg.prune != null) "-prune=${toString cfg.prune}"}\
219 ${optionalString (cfg.dbCache != null) "-dbcache=${toString cfg.dbCache}"}\
220 ${optionalString (cfg.rpc.port != null) "-rpcport=${toString cfg.rpc.port}"}\
221 ${toString cfg.extraCmdlineOptions}
223 Restart = "on-failure";
227 ProtectSystem = "full";
228 NoNewPrivileges = "true";
229 PrivateDevices = "true";
230 MemoryDenyWriteExecute = "true";
235 systemd.tmpfiles.rules = flatten (mapAttrsToList (bitcoindName: cfg: [
236 "d '${cfg.dataDir}' 0770 '${cfg.user}' '${cfg.group}' - -"
239 users.users = mapAttrs' (bitcoindName: cfg: (
240 nameValuePair "bitcoind-${bitcoindName}" {
243 description = "Bitcoin daemon user";
248 users.groups = mapAttrs' (bitcoindName: cfg: (
249 nameValuePair "${cfg.group}" { }
254 meta.maintainers = with maintainers; [ _1000101 ];