1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.redis; 7 redisBool = b: if b then "yes" else "no"; 8 condOption = name: value: if value != null then "${name} ${toString value}" else ""; 9 10 redisConfig = pkgs.writeText "redis.conf" '' 11 pidfile ${cfg.pidFile} 12 port ${toString cfg.port} 13 ${condOption "bind" cfg.bind} 14 ${condOption "unixsocket" cfg.unixSocket} 15 loglevel ${cfg.logLevel} 16 logfile ${cfg.logfile} 17 syslog-enabled ${redisBool cfg.syslog} 18 databases ${toString cfg.databases} 19 ${concatMapStrings (d: "save ${toString (builtins.elemAt d 0)} ${toString (builtins.elemAt d 1)}\n") cfg.save} 20 dbfilename ${cfg.dbFilename} 21 dir ${toString cfg.dbpath} 22 ${if cfg.slaveOf != null then "slaveof ${cfg.slaveOf.ip} ${toString cfg.slaveOf.port}" else ""} 23 ${condOption "masterauth" cfg.masterAuth} 24 ${condOption "requirepass" cfg.requirePass} 25 appendOnly ${redisBool cfg.appendOnly} 26 appendfsync ${cfg.appendFsync} 27 slowlog-log-slower-than ${toString cfg.slowLogLogSlowerThan} 28 slowlog-max-len ${toString cfg.slowLogMaxLen} 29 ${cfg.extraConfig} 30 ''; 31in 32{ 33 34 ###### interface 35 36 options = { 37 38 services.redis = { 39 40 enable = mkOption { 41 type = types.bool; 42 default = false; 43 description = "Whether to enable the Redis server."; 44 }; 45 46 package = mkOption { 47 type = types.package; 48 default = pkgs.redis; 49 defaultText = "pkgs.redis"; 50 description = "Which Redis derivation to use."; 51 }; 52 53 user = mkOption { 54 type = types.str; 55 default = "redis"; 56 description = "User account under which Redis runs."; 57 }; 58 59 pidFile = mkOption { 60 type = types.path; 61 default = "/var/lib/redis/redis.pid"; 62 description = ""; 63 }; 64 65 port = mkOption { 66 type = types.int; 67 default = 6379; 68 description = "The port for Redis to listen to."; 69 }; 70 71 bind = mkOption { 72 type = with types; nullOr str; 73 default = null; # All interfaces 74 description = "The IP interface to bind to."; 75 example = "127.0.0.1"; 76 }; 77 78 unixSocket = mkOption { 79 type = with types; nullOr path; 80 default = null; 81 description = "The path to the socket to bind to."; 82 example = "/var/run/redis.sock"; 83 }; 84 85 logLevel = mkOption { 86 type = types.str; 87 default = "notice"; # debug, verbose, notice, warning 88 example = "debug"; 89 description = "Specify the server verbosity level, options: debug, verbose, notice, warning."; 90 }; 91 92 logfile = mkOption { 93 type = types.str; 94 default = "/dev/null"; 95 description = "Specify the log file name. Also 'stdout' can be used to force Redis to log on the standard output."; 96 example = "/var/log/redis.log"; 97 }; 98 99 syslog = mkOption { 100 type = types.bool; 101 default = true; 102 description = "Enable logging to the system logger."; 103 }; 104 105 databases = mkOption { 106 type = types.int; 107 default = 16; 108 description = "Set the number of databases."; 109 }; 110 111 save = mkOption { 112 type = with types; listOf (listOf int); 113 default = [ [900 1] [300 10] [60 10000] ]; 114 description = "The schedule in which data is persisted to disk, represented as a list of lists where the first element represent the amount of seconds and the second the number of changes."; 115 example = [ [900 1] [300 10] [60 10000] ]; 116 }; 117 118 dbFilename = mkOption { 119 type = types.str; 120 default = "dump.rdb"; 121 description = "The filename where to dump the DB."; 122 }; 123 124 dbpath = mkOption { 125 type = types.path; 126 default = "/var/lib/redis"; 127 description = "The DB will be written inside this directory, with the filename specified using the 'dbFilename' configuration."; 128 }; 129 130 slaveOf = mkOption { 131 default = null; # { ip, port } 132 description = "An attribute set with two attributes: ip and port to which this redis instance acts as a slave."; 133 example = { ip = "192.168.1.100"; port = 6379; }; 134 }; 135 136 masterAuth = mkOption { 137 default = null; 138 description = ''If the master is password protected (using the requirePass configuration) 139 it is possible to tell the slave to authenticate before starting the replication synchronization 140 process, otherwise the master will refuse the slave request. 141 (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)''; 142 }; 143 144 requirePass = mkOption { 145 type = with types; nullOr str; 146 default = null; 147 description = "Password for database (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)"; 148 example = "letmein!"; 149 }; 150 151 appendOnly = mkOption { 152 type = types.bool; 153 default = false; 154 description = "By default data is only periodically persisted to disk, enable this option to use an append-only file for improved persistence."; 155 }; 156 157 appendOnlyFilename = mkOption { 158 type = types.str; 159 default = "appendonly.aof"; 160 description = "Filename for the append-only file (stored inside of dbpath)"; 161 }; 162 163 appendFsync = mkOption { 164 type = types.str; 165 default = "everysec"; # no, always, everysec 166 description = "How often to fsync the append-only log, options: no, always, everysec."; 167 }; 168 169 slowLogLogSlowerThan = mkOption { 170 type = types.int; 171 default = 10000; 172 description = "Log queries whose execution take longer than X in milliseconds."; 173 example = 1000; 174 }; 175 176 slowLogMaxLen = mkOption { 177 type = types.int; 178 default = 128; 179 description = "Maximum number of items to keep in slow log."; 180 }; 181 182 extraConfig = mkOption { 183 type = types.lines; 184 default = ""; 185 description = "Extra configuration options for redis.conf."; 186 }; 187 }; 188 189 }; 190 191 192 ###### implementation 193 194 config = mkIf config.services.redis.enable { 195 196 users.extraUsers.redis = 197 { name = cfg.user; 198 uid = config.ids.uids.redis; 199 description = "Redis database user"; 200 }; 201 202 environment.systemPackages = [ cfg.package ]; 203 204 systemd.services.redis_init = 205 { description = "Redis Server Initialisation"; 206 207 wantedBy = [ "redis.service" ]; 208 before = [ "redis.service" ]; 209 210 serviceConfig.Type = "oneshot"; 211 212 script = '' 213 if ! test -e ${cfg.dbpath}; then 214 install -d -m0700 -o ${cfg.user} ${cfg.dbpath} 215 fi 216 ''; 217 }; 218 219 systemd.services.redis = 220 { description = "Redis Server"; 221 222 wantedBy = [ "multi-user.target" ]; 223 after = [ "network.target" ]; 224 225 serviceConfig = { 226 ExecStart = "${cfg.package}/bin/redis-server ${redisConfig}"; 227 User = cfg.user; 228 }; 229 }; 230 231 }; 232 233}