at 25.11-pre 6.6 kB view raw
1{ 2 config, 3 lib, 4 options, 5 pkgs, 6 ... 7}: 8 9with lib; 10 11let 12 cfg = config.services.coder; 13 name = "coder"; 14in 15{ 16 options = { 17 services.coder = { 18 enable = mkEnableOption "Coder service"; 19 20 user = mkOption { 21 type = types.str; 22 default = "coder"; 23 description = '' 24 User under which the coder service runs. 25 26 ::: {.note} 27 If left as the default value this user will automatically be created 28 on system activation, otherwise it needs to be configured manually. 29 ::: 30 ''; 31 }; 32 33 group = mkOption { 34 type = types.str; 35 default = "coder"; 36 description = '' 37 Group under which the coder service runs. 38 39 ::: {.note} 40 If left as the default value this group will automatically be created 41 on system activation, otherwise it needs to be configured manually. 42 ::: 43 ''; 44 }; 45 46 package = mkPackageOption pkgs "coder" { }; 47 48 homeDir = mkOption { 49 type = types.str; 50 description = '' 51 Home directory for coder user. 52 ''; 53 default = "/var/lib/coder"; 54 }; 55 56 listenAddress = mkOption { 57 type = types.str; 58 description = '' 59 Listen address. 60 ''; 61 default = "127.0.0.1:3000"; 62 }; 63 64 accessUrl = mkOption { 65 type = types.nullOr types.str; 66 description = '' 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 = '' 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 environment = { 83 extra = mkOption { 84 type = types.attrs; 85 description = "Extra environment variables to pass run Coder's server with. See Coder documentation."; 86 default = { }; 87 example = { 88 CODER_OAUTH2_GITHUB_ALLOW_SIGNUPS = true; 89 CODER_OAUTH2_GITHUB_ALLOWED_ORGS = "your-org"; 90 }; 91 }; 92 file = mkOption { 93 type = types.nullOr types.path; 94 description = "Systemd environment file to add to Coder."; 95 default = null; 96 }; 97 }; 98 99 database = { 100 createLocally = mkOption { 101 type = types.bool; 102 default = true; 103 description = '' 104 Create the database and database user locally. 105 ''; 106 }; 107 108 host = mkOption { 109 type = types.str; 110 default = "/run/postgresql"; 111 description = '' 112 Hostname hosting the database. 113 ''; 114 }; 115 116 database = mkOption { 117 type = types.str; 118 default = "coder"; 119 description = '' 120 Name of database. 121 ''; 122 }; 123 124 username = mkOption { 125 type = types.str; 126 default = "coder"; 127 description = '' 128 Username for accessing the database. 129 ''; 130 }; 131 132 password = mkOption { 133 type = types.nullOr types.str; 134 default = null; 135 description = '' 136 Password for accessing the database. 137 ''; 138 }; 139 140 sslmode = mkOption { 141 type = types.nullOr types.str; 142 default = "disable"; 143 description = '' 144 Password for accessing the database. 145 ''; 146 }; 147 }; 148 149 tlsCert = mkOption { 150 type = types.nullOr types.path; 151 description = '' 152 The path to the TLS certificate. 153 ''; 154 default = null; 155 }; 156 157 tlsKey = mkOption { 158 type = types.nullOr types.path; 159 description = '' 160 The path to the TLS key. 161 ''; 162 default = null; 163 }; 164 }; 165 }; 166 167 config = mkIf cfg.enable { 168 assertions = [ 169 { 170 assertion = 171 cfg.database.createLocally 172 -> cfg.database.username == name && cfg.database.database == cfg.database.username; 173 message = "services.coder.database.username must be set to ${name} if services.coder.database.createLocally is set true"; 174 } 175 ]; 176 177 systemd.services.coder = { 178 description = "Coder - Self-hosted developer workspaces on your infra"; 179 after = [ "network.target" ]; 180 wantedBy = [ "multi-user.target" ]; 181 182 environment = cfg.environment.extra // { 183 CODER_ACCESS_URL = cfg.accessUrl; 184 CODER_WILDCARD_ACCESS_URL = cfg.wildcardAccessUrl; 185 CODER_PG_CONNECTION_URL = "user=${cfg.database.username} ${ 186 optionalString (cfg.database.password != null) "password=${cfg.database.password}" 187 } database=${cfg.database.database} host=${cfg.database.host} ${ 188 optionalString (cfg.database.sslmode != null) "sslmode=${cfg.database.sslmode}" 189 }"; 190 CODER_ADDRESS = cfg.listenAddress; 191 CODER_TLS_ENABLE = optionalString (cfg.tlsCert != null) "1"; 192 CODER_TLS_CERT_FILE = cfg.tlsCert; 193 CODER_TLS_KEY_FILE = cfg.tlsKey; 194 }; 195 196 serviceConfig = { 197 ProtectSystem = "full"; 198 PrivateTmp = "yes"; 199 PrivateDevices = "yes"; 200 SecureBits = "keep-caps"; 201 AmbientCapabilities = "CAP_IPC_LOCK CAP_NET_BIND_SERVICE"; 202 CacheDirectory = "coder"; 203 CapabilityBoundingSet = "CAP_SYSLOG CAP_IPC_LOCK CAP_NET_BIND_SERVICE"; 204 KillSignal = "SIGINT"; 205 KillMode = "mixed"; 206 NoNewPrivileges = "yes"; 207 Restart = "on-failure"; 208 ExecStart = "${cfg.package}/bin/coder server"; 209 User = cfg.user; 210 Group = cfg.group; 211 EnvironmentFile = lib.mkIf (cfg.environment.file != null) cfg.environment.file; 212 }; 213 }; 214 215 services.postgresql = lib.mkIf cfg.database.createLocally { 216 enable = true; 217 ensureDatabases = [ 218 cfg.database.database 219 ]; 220 ensureUsers = [ 221 { 222 name = cfg.user; 223 ensureDBOwnership = true; 224 } 225 ]; 226 }; 227 228 users.groups = optionalAttrs (cfg.group == name) { 229 "${cfg.group}" = { }; 230 }; 231 users.users = optionalAttrs (cfg.user == name) { 232 ${name} = { 233 description = "Coder service user"; 234 group = cfg.group; 235 home = cfg.homeDir; 236 createHome = true; 237 isSystemUser = true; 238 }; 239 }; 240 }; 241 meta.maintainers = pkgs.coder.meta.maintainers; 242}