at 18.09-beta 7.5 kB view raw
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 vmOverCommit = mkOption { 72 type = types.bool; 73 default = false; 74 description = '' 75 Set vm.overcommit_memory to 1 (Suggested for Background Saving: http://redis.io/topics/faq) 76 ''; 77 }; 78 79 openFirewall = mkOption { 80 type = types.bool; 81 default = false; 82 description = '' 83 Whether to open ports in the firewall for the server. 84 ''; 85 }; 86 87 bind = mkOption { 88 type = with types; nullOr str; 89 default = null; # All interfaces 90 description = "The IP interface to bind to."; 91 example = "127.0.0.1"; 92 }; 93 94 unixSocket = mkOption { 95 type = with types; nullOr path; 96 default = null; 97 description = "The path to the socket to bind to."; 98 example = "/var/run/redis.sock"; 99 }; 100 101 logLevel = mkOption { 102 type = types.str; 103 default = "notice"; # debug, verbose, notice, warning 104 example = "debug"; 105 description = "Specify the server verbosity level, options: debug, verbose, notice, warning."; 106 }; 107 108 logfile = mkOption { 109 type = types.str; 110 default = "/dev/null"; 111 description = "Specify the log file name. Also 'stdout' can be used to force Redis to log on the standard output."; 112 example = "/var/log/redis.log"; 113 }; 114 115 syslog = mkOption { 116 type = types.bool; 117 default = true; 118 description = "Enable logging to the system logger."; 119 }; 120 121 databases = mkOption { 122 type = types.int; 123 default = 16; 124 description = "Set the number of databases."; 125 }; 126 127 save = mkOption { 128 type = with types; listOf (listOf int); 129 default = [ [900 1] [300 10] [60 10000] ]; 130 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."; 131 example = [ [900 1] [300 10] [60 10000] ]; 132 }; 133 134 dbFilename = mkOption { 135 type = types.str; 136 default = "dump.rdb"; 137 description = "The filename where to dump the DB."; 138 }; 139 140 dbpath = mkOption { 141 type = types.path; 142 default = "/var/lib/redis"; 143 description = "The DB will be written inside this directory, with the filename specified using the 'dbFilename' configuration."; 144 }; 145 146 slaveOf = mkOption { 147 default = null; # { ip, port } 148 description = "An attribute set with two attributes: ip and port to which this redis instance acts as a slave."; 149 example = { ip = "192.168.1.100"; port = 6379; }; 150 }; 151 152 masterAuth = mkOption { 153 default = null; 154 description = ''If the master is password protected (using the requirePass configuration) 155 it is possible to tell the slave to authenticate before starting the replication synchronization 156 process, otherwise the master will refuse the slave request. 157 (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)''; 158 }; 159 160 requirePass = mkOption { 161 type = with types; nullOr str; 162 default = null; 163 description = "Password for database (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)"; 164 example = "letmein!"; 165 }; 166 167 appendOnly = mkOption { 168 type = types.bool; 169 default = false; 170 description = "By default data is only periodically persisted to disk, enable this option to use an append-only file for improved persistence."; 171 }; 172 173 appendOnlyFilename = mkOption { 174 type = types.str; 175 default = "appendonly.aof"; 176 description = "Filename for the append-only file (stored inside of dbpath)"; 177 }; 178 179 appendFsync = mkOption { 180 type = types.str; 181 default = "everysec"; # no, always, everysec 182 description = "How often to fsync the append-only log, options: no, always, everysec."; 183 }; 184 185 slowLogLogSlowerThan = mkOption { 186 type = types.int; 187 default = 10000; 188 description = "Log queries whose execution take longer than X in milliseconds."; 189 example = 1000; 190 }; 191 192 slowLogMaxLen = mkOption { 193 type = types.int; 194 default = 128; 195 description = "Maximum number of items to keep in slow log."; 196 }; 197 198 extraConfig = mkOption { 199 type = types.lines; 200 default = ""; 201 description = "Extra configuration options for redis.conf."; 202 }; 203 }; 204 205 }; 206 207 208 ###### implementation 209 210 config = mkIf config.services.redis.enable { 211 212 boot.kernel.sysctl = mkIf cfg.vmOverCommit { 213 "vm.overcommit_memory" = "1"; 214 }; 215 216 networking.firewall = mkIf cfg.openFirewall { 217 allowedTCPPorts = [ cfg.port ]; 218 }; 219 220 users.users.redis = 221 { name = cfg.user; 222 description = "Redis database user"; 223 }; 224 225 environment.systemPackages = [ cfg.package ]; 226 227 systemd.services.redis_init = 228 { description = "Redis Server Initialisation"; 229 230 wantedBy = [ "redis.service" ]; 231 before = [ "redis.service" ]; 232 233 serviceConfig.Type = "oneshot"; 234 235 script = '' 236 install -d -m0700 -o ${cfg.user} ${cfg.dbpath} 237 chown -R ${cfg.user} ${cfg.dbpath} 238 ''; 239 }; 240 241 systemd.services.redis = 242 { description = "Redis Server"; 243 244 wantedBy = [ "multi-user.target" ]; 245 after = [ "network.target" ]; 246 247 serviceConfig = { 248 ExecStart = "${cfg.package}/bin/redis-server ${redisConfig}"; 249 User = cfg.user; 250 }; 251 }; 252 253 }; 254 255}