Skip to content

Commit 56ecab7

Browse files
committed
nixos/coder: init module
1 parent 0566d27 commit 56ecab7

7 files changed

Lines changed: 298 additions & 82 deletions

File tree

nixos/modules/module-list.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,6 +1120,7 @@
11201120
./services/web-apps/baget.nix
11211121
./services/web-apps/bookstack.nix
11221122
./services/web-apps/calibre-web.nix
1123+
./services/web-apps/coder.nix
11231124
./services/web-apps/changedetection-io.nix
11241125
./services/web-apps/cloudlog.nix
11251126
./services/web-apps/code-server.nix
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
{ config, lib, options, pkgs, ... }:
2+
3+
with lib;
4+
5+
let
6+
cfg = config.services.coder;
7+
name = "coder";
8+
in {
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+
}

nixos/tests/all-tests.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ in {
135135
cloudlog = handleTest ./cloudlog.nix {};
136136
cntr = handleTestOn ["aarch64-linux" "x86_64-linux"] ./cntr.nix {};
137137
cockroachdb = handleTestOn ["x86_64-linux"] ./cockroachdb.nix {};
138+
coder = handleTest ./coder.nix {};
138139
collectd = handleTest ./collectd.nix {};
139140
connman = handleTest ./connman.nix {};
140141
consul = handleTest ./consul.nix {};

nixos/tests/coder.nix

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import ./make-test-python.nix ({ pkgs, ... }: {
2+
name = "coder";
3+
meta = with pkgs.lib.maintainers; {
4+
maintainers = [ shyim ghuntley ];
5+
};
6+
7+
nodes.machine =
8+
{ pkgs, ... }:
9+
{
10+
services.coder = {
11+
enable = true;
12+
accessUrl = "http://localhost:3000";
13+
};
14+
};
15+
16+
testScript = ''
17+
machine.start()
18+
machine.wait_for_unit("postgresql.service")
19+
machine.wait_for_unit("coder.service")
20+
machine.wait_for_open_port(3000)
21+
22+
machine.succeed("curl --fail http://localhost:3000")
23+
'';
24+
})

pkgs/applications/misc/coder/default.nix

Lines changed: 0 additions & 76 deletions
This file was deleted.

0 commit comments

Comments
 (0)