1 { config, pkgs, lib, ... }:
4 cfg = config.services.windmill;
7 options.services.windmill = {
8 enable = lib.mkEnableOption "windmill service";
10 serverPort = lib.mkOption {
11 type = lib.types.port;
13 description = "Port the windmill server listens on.";
16 lspPort = lib.mkOption {
17 type = lib.types.port;
19 description = "Port the windmill lsp listens on.";
25 # the simplest database setup is to have the database named like the user.
27 description = "Database name.";
32 # the simplest database setup is to have the database user like the name.
34 description = "Database user.";
39 default = "postgres://${config.services.windmill.database.name}?host=/var/run/postgresql";
40 defaultText = lib.literalExpression ''
41 "postgres://\$\{config.services.windmill.database.name}?host=/var/run/postgresql";
43 description = "Database url. Note that any secret here would be world-readable. Use `services.windmill.database.urlPath` unstead to include secrets in the url.";
46 urlPath = lib.mkOption {
47 type = lib.types.nullOr lib.types.path;
49 Path to the file containing the database url windmill should connect to. This is not deducted from database user and name as it might contain a secret
52 example = "config.age.secrets.DATABASE_URL_FILE.path";
55 createLocally = lib.mkOption {
56 type = lib.types.bool;
58 description = "Whether to create a local database automatically.";
62 baseUrl = lib.mkOption {
64 default = "https://localhost:${toString config.services.windmill.serverPort}";
65 defaultText = lib.literalExpression ''
66 "https://localhost:\$\{toString config.services.windmill.serverPort}";
69 The base url that windmill will be served on.
71 example = "https://windmill.example.com";
74 logLevel = lib.mkOption {
75 type = lib.types.enum [ "error" "warn" "info" "debug" "trace" ];
77 description = "Log level";
81 config = lib.mkIf cfg.enable {
83 services.postgresql = lib.optionalAttrs (cfg.database.createLocally) {
84 enable = lib.mkDefault true;
86 ensureDatabases = [ cfg.database.name ];
88 { name = cfg.database.user;
89 ensureDBOwnership = true;
97 useUrlPath = (cfg.database.urlPath != null);
100 # using the same user to simplify db connection
101 User = cfg.database.user;
102 ExecStart = "${pkgs.windmill}/bin/windmill";
105 } // lib.optionalAttrs useUrlPath {
107 "DATABASE_URL_FILE:${cfg.database.urlPath}"
110 db_url_envs = lib.optionalAttrs useUrlPath {
111 DATABASE_URL_FILE = "%d/DATABASE_URL_FILE";
112 } // lib.optionalAttrs (!useUrlPath) {
113 DATABASE_URL = cfg.database.url;
118 # coming from https://github.com/windmill-labs/windmill/blob/main/init-db-as-superuser.sql
119 # modified to not grant priviledges on all tables
120 # create role windmill_user and windmill_admin only if they don't exist
121 postgresql.postStart = lib.mkIf cfg.database.createLocally (lib.mkAfter ''
126 SELECT FROM pg_catalog.pg_roles
127 WHERE rolname = 'windmill_user'
129 CREATE ROLE windmill_user;
130 GRANT ALL PRIVILEGES ON DATABASE ${cfg.database.name} TO windmill_user;
132 RAISE NOTICE 'Role "windmill_user" already exists. Skipping.';
135 SELECT FROM pg_catalog.pg_roles
136 WHERE rolname = 'windmill_admin'
138 CREATE ROLE windmill_admin WITH BYPASSRLS;
139 GRANT windmill_user TO windmill_admin;
141 RAISE NOTICE 'Role "windmill_admin" already exists. Skipping.';
143 GRANT windmill_admin TO windmill;
150 description = "Windmill server";
151 after = [ "network.target" ] ++ lib.optional cfg.database.createLocally "postgresql.service";
152 wantedBy = [ "multi-user.target" ];
154 serviceConfig = serviceConfig // { StateDirectory = "windmill";};
157 PORT = builtins.toString cfg.serverPort;
158 WM_BASE_URL = cfg.baseUrl;
159 RUST_LOG = cfg.logLevel;
165 description = "Windmill worker";
166 after = [ "network.target" ] ++ lib.optional cfg.database.createLocally "postgresql.service";
167 wantedBy = [ "multi-user.target" ];
169 serviceConfig = serviceConfig // { StateDirectory = "windmill-worker";};
172 WM_BASE_URL = cfg.baseUrl;
173 RUST_LOG = cfg.logLevel;
175 WORKER_GROUP = "default";
176 KEEP_JOB_DIR = "false";
180 windmill-worker-native = {
181 description = "Windmill worker native";
182 after = [ "network.target" ] ++ lib.optional cfg.database.createLocally "postgresql.service";
183 wantedBy = [ "multi-user.target" ];
185 serviceConfig = serviceConfig // { StateDirectory = "windmill-worker-native";};
188 WM_BASE_URL = cfg.baseUrl;
189 RUST_LOG = cfg.logLevel;
191 WORKER_GROUP = "native";