at 23.11-pre 6.2 kB view raw
1{ config, lib, options, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.coder; 7 name = "coder"; 8in { 9 options = { 10 services.coder = { 11 enable = mkEnableOption (lib.mdDoc "Coder service"); 12 13 user = mkOption { 14 type = types.str; 15 default = "coder"; 16 description = lib.mdDoc '' 17 User under which the coder service runs. 18 19 ::: {.note} 20 If left as the default value this user will automatically be created 21 on system activation, otherwise it needs to be configured manually. 22 ::: 23 ''; 24 }; 25 26 group = mkOption { 27 type = types.str; 28 default = "coder"; 29 description = lib.mdDoc '' 30 Group under which the coder service runs. 31 32 ::: {.note} 33 If left as the default value this group will automatically be created 34 on system activation, otherwise it needs to be configured manually. 35 ::: 36 ''; 37 }; 38 39 package = mkOption { 40 type = types.package; 41 default = pkgs.coder; 42 description = lib.mdDoc '' 43 Package to use for the service. 44 ''; 45 defaultText = literalExpression "pkgs.coder"; 46 }; 47 48 homeDir = mkOption { 49 type = types.str; 50 description = lib.mdDoc '' 51 Home directory for coder user. 52 ''; 53 default = "/var/lib/coder"; 54 }; 55 56 listenAddress = mkOption { 57 type = types.str; 58 description = lib.mdDoc '' 59 Listen address. 60 ''; 61 default = "127.0.0.1:3000"; 62 }; 63 64 accessUrl = mkOption { 65 type = types.nullOr types.str; 66 description = lib.mdDoc '' 67 Access URL should be a external IP address or domain with DNS records pointing to Coder. 68 ''; 69 default = null; 70 example = "https://coder.example.com"; 71 }; 72 73 wildcardAccessUrl = mkOption { 74 type = types.nullOr types.str; 75 description = lib.mdDoc '' 76 If you are providing TLS certificates directly to the Coder server, you must use a single certificate for the root and wildcard domains. 77 ''; 78 default = null; 79 example = "*.coder.example.com"; 80 }; 81 82 database = { 83 createLocally = mkOption { 84 type = types.bool; 85 default = true; 86 description = lib.mdDoc '' 87 Create the database and database user locally. 88 ''; 89 }; 90 91 host = mkOption { 92 type = types.str; 93 default = "/run/postgresql"; 94 description = lib.mdDoc '' 95 Hostname hosting the database. 96 ''; 97 }; 98 99 database = mkOption { 100 type = types.str; 101 default = "coder"; 102 description = lib.mdDoc '' 103 Name of database. 104 ''; 105 }; 106 107 username = mkOption { 108 type = types.str; 109 default = "coder"; 110 description = lib.mdDoc '' 111 Username for accessing the database. 112 ''; 113 }; 114 115 password = mkOption { 116 type = types.nullOr types.str; 117 default = null; 118 description = lib.mdDoc '' 119 Password for accessing the database. 120 ''; 121 }; 122 123 sslmode = mkOption { 124 type = types.nullOr types.str; 125 default = "disable"; 126 description = lib.mdDoc '' 127 Password for accessing the database. 128 ''; 129 }; 130 }; 131 132 tlsCert = mkOption { 133 type = types.nullOr types.path; 134 description = lib.mdDoc '' 135 The path to the TLS certificate. 136 ''; 137 default = null; 138 }; 139 140 tlsKey = mkOption { 141 type = types.nullOr types.path; 142 description = lib.mdDoc '' 143 The path to the TLS key. 144 ''; 145 default = null; 146 }; 147 }; 148 }; 149 150 config = mkIf cfg.enable { 151 assertions = [ 152 { assertion = cfg.database.createLocally -> cfg.database.username == name; 153 message = "services.coder.database.username must be set to ${user} if services.coder.database.createLocally is set true"; 154 } 155 ]; 156 157 systemd.services.coder = { 158 description = "Coder - Self-hosted developer workspaces on your infra"; 159 after = [ "network.target" ]; 160 wantedBy = [ "multi-user.target" ]; 161 162 environment = { 163 CODER_ACCESS_URL = cfg.accessUrl; 164 CODER_WILDCARD_ACCESS_URL = cfg.wildcardAccessUrl; 165 CODER_PG_CONNECTION_URL = "user=${cfg.database.username} ${optionalString (cfg.database.password != null) "password=${cfg.database.password}"} database=${cfg.database.database} host=${cfg.database.host} ${optionalString (cfg.database.sslmode != null) "sslmode=${cfg.database.sslmode}"}"; 166 CODER_ADDRESS = cfg.listenAddress; 167 CODER_TLS_ENABLE = optionalString (cfg.tlsCert != null) "1"; 168 CODER_TLS_CERT_FILE = cfg.tlsCert; 169 CODER_TLS_KEY_FILE = cfg.tlsKey; 170 }; 171 172 serviceConfig = { 173 ProtectSystem = "full"; 174 PrivateTmp = "yes"; 175 PrivateDevices = "yes"; 176 SecureBits = "keep-caps"; 177 AmbientCapabilities = "CAP_IPC_LOCK CAP_NET_BIND_SERVICE"; 178 CacheDirectory = "coder"; 179 CapabilityBoundingSet = "CAP_SYSLOG CAP_IPC_LOCK CAP_NET_BIND_SERVICE"; 180 KillSignal = "SIGINT"; 181 KillMode = "mixed"; 182 NoNewPrivileges = "yes"; 183 Restart = "on-failure"; 184 ExecStart = "${cfg.package}/bin/coder server"; 185 User = cfg.user; 186 Group = cfg.group; 187 }; 188 }; 189 190 services.postgresql = lib.mkIf cfg.database.createLocally { 191 enable = true; 192 ensureDatabases = [ 193 cfg.database.database 194 ]; 195 ensureUsers = [{ 196 name = cfg.database.username; 197 ensurePermissions = { 198 "DATABASE \"${cfg.database.database}\"" = "ALL PRIVILEGES"; 199 }; 200 } 201 ]; 202 }; 203 204 users.groups = optionalAttrs (cfg.group == name) { 205 "${cfg.group}" = {}; 206 }; 207 users.users = optionalAttrs (cfg.user == name) { 208 ${name} = { 209 description = "Coder service user"; 210 group = cfg.group; 211 home = cfg.homeDir; 212 createHome = true; 213 isSystemUser = true; 214 }; 215 }; 216 }; 217}