├── .envrc ├── .github └── workflows │ ├── build.yaml │ ├── publish-flakehub.yaml │ └── publish.yaml ├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── DEV.md ├── LICENSE ├── OPTIONS.md ├── README.md ├── bootstrap └── hetzner-cloud-terraform │ ├── .gitignore │ ├── .terraform.lock.hcl │ ├── README.md │ ├── config.json │ ├── flake.lock │ ├── flake.nix │ ├── hetzner-cloud.nix │ └── main.tf ├── flake.lock ├── flake.nix ├── images.nix ├── modules ├── core-services │ ├── default.nix │ ├── gateway.nix │ ├── nats.nix │ ├── prometheus.nix │ └── queue-worker.nix ├── faasd-module.nix └── service.nix ├── pkgs └── options-doc.nix └── scripts ├── gen-options-doc.nix └── prefetch-images.nix /.envrc: -------------------------------------------------------------------------------- 1 | use flake -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: 'Build packages' 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - '**' 7 | - '!main' 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - uses: DeterminateSystems/nix-installer-action@main 14 | - uses: DeterminateSystems/magic-nix-cache-action@main 15 | - name: Build faasd 🔧 16 | run: | 17 | nix build -L .#faasd --accept-flake-config 18 | - name: Build containerd 🔧 19 | run: | 20 | nix build -L .#faasd-containerd --accept-flake-config 21 | - name: Build cni-plugin 🔧 22 | run: | 23 | nix build -L .#faasd-cni-plugins --accept-flake-config 24 | - name: Build OpenFaaS core images 🔧 25 | run: | 26 | nix build --accept-flake-config \ 27 | .#gateway-image \ 28 | .#queue-worker-image \ 29 | .#nats-image \ 30 | .#prometheus-image 31 | - name: Check flake 32 | run: nix flake check -L --accept-flake-config 33 | -------------------------------------------------------------------------------- /.github/workflows/publish-flakehub.yaml: -------------------------------------------------------------------------------- 1 | name: 'Publish tags to FlakeHub' 2 | on: 3 | push: 4 | tags: 5 | - 'v?[0-9]+.[0-9]+.[0-9]+*' 6 | workflow_dispatch: 7 | inputs: 8 | tag: 9 | description: 'The existing tag to publish to FlakeHub' 10 | type: 'string' 11 | required: true 12 | jobs: 13 | flakehub-publish: 14 | runs-on: 'ubuntu-latest' 15 | permissions: 16 | id-token: 'write' 17 | contents: 'read' 18 | steps: 19 | - uses: 'actions/checkout@v3' 20 | with: 21 | ref: "${{ (inputs.tag != null) && format('refs/tags/{0}', inputs.tag) || '' }}" 22 | - uses: 'DeterminateSystems/nix-installer-action@main' 23 | - uses: 'DeterminateSystems/flakehub-push@main' 24 | with: 25 | visibility: 'public' 26 | name: 'welteki/faasd' 27 | tag: '${{ inputs.tag }}' 28 | -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | name: 'Build and push to cachix' 2 | on: 3 | push: 4 | branches: 5 | - main 6 | jobs: 7 | cachix-push: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: DeterminateSystems/nix-installer-action@main 12 | - uses: cachix/cachix-action@v12 13 | with: 14 | name: welteki 15 | authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 16 | skipPush: true 17 | - name: Check flake 18 | run: nix flake check -L --accept-flake-config 19 | - name: Build faasd 🔧 20 | run: | 21 | nix build -L .#faasd 22 | nix eval --json .#faasd | jq -r | cachix push welteki 23 | - name: Build containerd 🔧 24 | run: | 25 | nix build -L .#faasd-containerd 26 | nix eval --json .#faasd-containerd | jq -r | cachix push welteki 27 | - name: Build cni-plugin 🔧 28 | run: | 29 | nix build -L .#faasd-cni-plugins 30 | nix eval --json .#faasd-cni-plugins | jq -r | cachix push welteki 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .tmp 2 | result 3 | 4 | .direnv -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["jnoortheen.nix-ide"] 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "nixEnvSelector.nixFile": "${workspaceRoot}/shell.nix", 3 | "[nix]": { 4 | "editor.defaultFormatter": "jnoortheen.nix-ide" 5 | }, 6 | "editor.formatOnSave": true, 7 | "editor.formatOnType": true, 8 | "files.watcherExclude": { 9 | "**/.tmp/*/**": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /DEV.md: -------------------------------------------------------------------------------- 1 | ## Update faasd core images 2 | 3 | Update the image tags in the images attribute set in flake.nix. Next run: 4 | 5 | ```bash 6 | nix run .#prefetch-images > images.nix 7 | ``` 8 | 9 | ## Update NixOS module options doc 10 | 11 | ```bash 12 | nix run .#gen-options-doc 13 | ``` 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-2023 Han Verstraete 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /OPTIONS.md: -------------------------------------------------------------------------------- 1 | ## services\.faasd\.enable 2 | 3 | Lightweight faas engine 4 | 5 | 6 | 7 | *Type:* 8 | boolean 9 | 10 | 11 | 12 | *Default:* 13 | ` false ` 14 | 15 | *Declared by:* 16 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module.nix) 17 | 18 | 19 | 20 | ## services\.faasd\.package 21 | 22 | Faasd package to use. 23 | 24 | 25 | 26 | *Type:* 27 | package 28 | 29 | 30 | 31 | *Default:* 32 | ` ` 33 | 34 | *Declared by:* 35 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module.nix) 36 | 37 | 38 | 39 | ## services\.faasd\.basicAuth\.enable 40 | 41 | Enable basicAuth 42 | 43 | *Type:* 44 | boolean 45 | 46 | 47 | 48 | *Default:* 49 | ` true ` 50 | 51 | *Declared by:* 52 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module.nix) 53 | 54 | 55 | 56 | ## services\.faasd\.basicAuth\.passwordFile 57 | 58 | Path to file containing password 59 | 60 | 61 | 62 | *Type:* 63 | null or string 64 | 65 | 66 | 67 | *Default:* 68 | ` null ` 69 | 70 | 71 | 72 | *Example:* 73 | ` "/etc/nixos/faasd-basic-auth-password" ` 74 | 75 | *Declared by:* 76 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module.nix) 77 | 78 | 79 | 80 | ## services\.faasd\.basicAuth\.user 81 | 82 | Basic-auth user 83 | 84 | 85 | 86 | *Type:* 87 | string 88 | 89 | 90 | 91 | *Default:* 92 | ` "admin" ` 93 | 94 | *Declared by:* 95 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module.nix) 96 | 97 | 98 | 99 | ## services\.faasd\.containers 100 | 101 | OCI (Docker) containers to run as additional services on faasd. 102 | 103 | 104 | 105 | *Type:* 106 | attribute set of (submodule) 107 | 108 | 109 | 110 | *Default:* 111 | ` { } ` 112 | 113 | *Declared by:* 114 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module.nix) 115 | 116 | 117 | 118 | ## services\.faasd\.containers\.\\.cap_add 119 | 120 | See link: [Compose Specification#cap_add](https://github.com/compose-spec/compose-spec/blob/master/spec.md?/#cap_add) 121 | 122 | 123 | 124 | *Type:* 125 | list of string 126 | 127 | 128 | 129 | *Default:* 130 | ` [ ] ` 131 | 132 | 133 | 134 | *Example:* 135 | 136 | ``` 137 | [ 138 | "CAP_NET_RAW" 139 | "SYS_ADMIN" 140 | ] 141 | ``` 142 | 143 | *Declared by:* 144 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module.nix) 145 | 146 | 147 | 148 | ## services\.faasd\.containers\.\\.command 149 | 150 | See link: [Compose Specification#command](https://github.com/compose-spec/compose-spec/blob/master/spec.md?/#command) 151 | 152 | 153 | 154 | *Type:* 155 | null or unspecified value 156 | 157 | 158 | 159 | *Default:* 160 | ` null ` 161 | 162 | *Declared by:* 163 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module.nix) 164 | 165 | 166 | 167 | ## services\.faasd\.containers\.\\.depends_on 168 | 169 | See link: [Compose Specification#depends_on](https://github.com/compose-spec/compose-spec/blob/master/spec.md?/#depends_on) 170 | 171 | 172 | 173 | *Type:* 174 | list of string 175 | 176 | 177 | 178 | *Default:* 179 | ` [ ] ` 180 | 181 | *Declared by:* 182 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module.nix) 183 | 184 | 185 | 186 | ## services\.faasd\.containers\.\\.entrypoint 187 | 188 | See link: [Compose Specification#entypoint](https://github.com/compose-spec/compose-spec/blob/master/spec.md?/#entypoint) 189 | 190 | 191 | 192 | *Type:* 193 | null or string 194 | 195 | 196 | 197 | *Default:* 198 | ` null ` 199 | 200 | *Declared by:* 201 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module.nix) 202 | 203 | 204 | 205 | ## services\.faasd\.containers\.\\.environment 206 | 207 | See link: [Compose Specification#environment](https://github.com/compose-spec/compose-spec/blob/master/spec.md?/#environment) 208 | 209 | 210 | 211 | *Type:* 212 | (attribute set of (string or signed integer)) or list of string 213 | 214 | 215 | 216 | *Default:* 217 | ` { } ` 218 | 219 | *Declared by:* 220 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module.nix) 221 | 222 | 223 | 224 | ## services\.faasd\.containers\.\\.image 225 | 226 | See link: [Compose Specification#image](https://github.com/compose-spec/compose-spec/blob/master/spec.md?/#image) 227 | 228 | 229 | 230 | *Type:* 231 | string 232 | 233 | *Declared by:* 234 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module.nix) 235 | 236 | 237 | 238 | ## services\.faasd\.containers\.\\.imageFile 239 | 240 | Path to an image file to load instead of pulling from a registry. 241 | If defined, do not pull from registry. 242 | You still need to set the image attribute, as it 243 | will be used as the image name for faasd to start a container. 244 | 245 | 246 | 247 | 248 | *Type:* 249 | null or package 250 | 251 | 252 | 253 | *Default:* 254 | ` null ` 255 | 256 | 257 | 258 | *Example:* 259 | ` pkgs.dockerTools.buildImage {...}; ` 260 | 261 | *Declared by:* 262 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module.nix) 263 | 264 | 265 | 266 | ## services\.faasd\.containers\.\\.ports 267 | 268 | See link: [Compose Specification#ports](https://github.com/compose-spec/compose-spec/blob/master/spec.md?/#ports) 269 | 270 | 271 | 272 | *Type:* 273 | list of unspecified value 274 | 275 | 276 | 277 | *Default:* 278 | ` [ ] ` 279 | 280 | *Declared by:* 281 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module.nix) 282 | 283 | 284 | 285 | ## services\.faasd\.containers\.\\.user 286 | 287 | See link: [Compose Specification#user](https://github.com/compose-spec/compose-spec/blob/master/spec.md?/#user) 288 | 289 | 290 | 291 | *Type:* 292 | null or string 293 | 294 | 295 | 296 | *Default:* 297 | ` null ` 298 | 299 | *Declared by:* 300 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module.nix) 301 | 302 | 303 | 304 | ## services\.faasd\.containers\.\\.volumes 305 | 306 | See link: [Compose Specification#volumes](https://github.com/compose-spec/compose-spec/blob/master/spec.md?/#volumes) 307 | 308 | 309 | 310 | *Type:* 311 | list of unspecified value 312 | 313 | 314 | 315 | *Default:* 316 | ` [ ] ` 317 | 318 | *Declared by:* 319 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module.nix) 320 | 321 | 322 | 323 | ## services\.faasd\.defaultQueue\.maxInflight 324 | 325 | Number of messages sent to queue worker and how many functions are invoked concurrently. 326 | 327 | 328 | 329 | *Type:* 330 | signed integer 331 | 332 | 333 | 334 | *Default:* 335 | ` 1 ` 336 | 337 | *Declared by:* 338 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/core-services/queue-worker\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/core-services/queue-worker.nix) 339 | 340 | 341 | 342 | ## services\.faasd\.defaultQueue\.writeDebug 343 | 344 | Print verbose logs 345 | 346 | 347 | 348 | *Type:* 349 | boolean 350 | 351 | 352 | 353 | *Default:* 354 | ` false ` 355 | 356 | *Declared by:* 357 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/core-services/queue-worker\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/core-services/queue-worker.nix) 358 | 359 | 360 | 361 | ## services\.faasd\.gateway\.readTimeout 362 | 363 | HTTP timeout for reading the payload from the client caller (in seconds). 364 | 365 | 366 | 367 | *Type:* 368 | signed integer 369 | 370 | 371 | 372 | *Default:* 373 | ` 60 ` 374 | 375 | *Declared by:* 376 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/core-services/gateway\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/core-services/gateway.nix) 377 | 378 | 379 | 380 | ## services\.faasd\.gateway\.scaleFormZero 381 | 382 | Enables an intercepting proxy which will scale any function from 0 replicas to the desired amount 383 | 384 | 385 | 386 | *Type:* 387 | boolean 388 | 389 | 390 | 391 | *Default:* 392 | ` true ` 393 | 394 | *Declared by:* 395 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/core-services/gateway\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/core-services/gateway.nix) 396 | 397 | 398 | 399 | ## services\.faasd\.gateway\.upstreamTimeout 400 | 401 | Maximum duration of HTTP call to upstream URL (in seconds). 402 | 403 | 404 | 405 | *Type:* 406 | signed integer 407 | 408 | 409 | 410 | *Default:* 411 | ` 65 ` 412 | 413 | *Declared by:* 414 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/core-services/gateway\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/core-services/gateway.nix) 415 | 416 | 417 | 418 | ## services\.faasd\.gateway\.writeTimeout 419 | 420 | HTTP timeout for writing a response body from your function (in seconds) 421 | 422 | 423 | 424 | *Type:* 425 | signed integer 426 | 427 | 428 | 429 | *Default:* 430 | ` 60 ` 431 | 432 | *Declared by:* 433 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/core-services/gateway\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/core-services/gateway.nix) 434 | 435 | 436 | 437 | ## services\.faasd\.nameserver 438 | 439 | Nameserver to use 440 | 441 | 442 | 443 | *Type:* 444 | string 445 | 446 | 447 | 448 | *Default:* 449 | ` "8.8.8.8" ` 450 | 451 | *Declared by:* 452 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module.nix) 453 | 454 | 455 | 456 | ## services\.faasd\.namespaces 457 | 458 | Openfaas function namespaces. 459 | Namespaces listed here will be created of they do not exist and labeled 460 | with `openfaas=true`. 461 | 462 | 463 | 464 | 465 | *Type:* 466 | list of string 467 | 468 | 469 | 470 | *Default:* 471 | ` [ ] ` 472 | 473 | 474 | 475 | *Example:* 476 | 477 | ``` 478 | [ 479 | "dev" 480 | ] 481 | ``` 482 | 483 | *Declared by:* 484 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module.nix) 485 | 486 | 487 | 488 | ## services\.faasd\.pullPolicy 489 | 490 | Set to "Always" to force a pull of images upon deployment, or "IfNotPresent" to try to use a cached image. 491 | 492 | 493 | 494 | 495 | *Type:* 496 | one of “Always”, “IfNotPresent” 497 | 498 | 499 | 500 | *Default:* 501 | ` "Always" ` 502 | 503 | *Declared by:* 504 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module.nix) 505 | 506 | 507 | 508 | ## services\.faasd\.queues 509 | 510 | 511 | 512 | *Type:* 513 | attribute set of (submodule) 514 | 515 | 516 | 517 | *Default:* 518 | ` { } ` 519 | 520 | *Declared by:* 521 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/core-services/queue-worker\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/core-services/queue-worker.nix) 522 | 523 | 524 | 525 | ## services\.faasd\.queues\.\\.maxInflight 526 | 527 | Number of messages sent to queue worker and how many functions are invoked concurrently. 528 | 529 | 530 | 531 | *Type:* 532 | signed integer 533 | 534 | 535 | 536 | *Default:* 537 | ` 1 ` 538 | 539 | *Declared by:* 540 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/core-services/queue-worker\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/core-services/queue-worker.nix) 541 | 542 | 543 | 544 | ## services\.faasd\.queues\.\\.natsChannel 545 | 546 | Nats channel to use for the queue. Defaults to the queue name. 547 | 548 | 549 | 550 | *Type:* 551 | string 552 | 553 | 554 | 555 | *Default:* 556 | ` "‹name›" ` 557 | 558 | *Declared by:* 559 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/core-services/queue-worker\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/core-services/queue-worker.nix) 560 | 561 | 562 | 563 | ## services\.faasd\.queues\.\\.writeDebug 564 | 565 | Print verbose logs 566 | 567 | 568 | 569 | *Type:* 570 | boolean 571 | 572 | 573 | 574 | *Default:* 575 | ` false ` 576 | 577 | *Declared by:* 578 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/core-services/queue-worker\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/core-services/queue-worker.nix) 579 | 580 | 581 | 582 | ## services\.faasd\.seedCoreImages 583 | 584 | Seed faasd core images 585 | 586 | 587 | 588 | *Type:* 589 | boolean 590 | 591 | 592 | 593 | *Default:* 594 | ` false ` 595 | 596 | *Declared by:* 597 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module.nix) 598 | 599 | 600 | 601 | ## services\.faasd\.seedDockerImages 602 | 603 | List of docker images to preload on system 604 | 605 | 606 | 607 | *Type:* 608 | list of (submodule) 609 | 610 | 611 | 612 | *Default:* 613 | ` [ ] ` 614 | 615 | *Declared by:* 616 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module.nix) 617 | 618 | 619 | 620 | ## services\.faasd\.seedDockerImages\.\*\.imageFile 621 | 622 | Path to the image file. 623 | 624 | 625 | 626 | *Type:* 627 | package 628 | 629 | *Declared by:* 630 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module.nix) 631 | 632 | 633 | 634 | ## services\.faasd\.seedDockerImages\.\*\.namespace 635 | 636 | Namespace to use when seeding image. 637 | 638 | 639 | 640 | *Type:* 641 | string 642 | 643 | 644 | 645 | *Default:* 646 | ` "openfaas" ` 647 | 648 | *Declared by:* 649 | - [/nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module\.nix](file:///nix/store/yhi091hky92nzqsb2r1vwmi36689jv6b-source/modules/faasd-module.nix) 650 | 651 | 652 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # faasd-nix - deploy faasd on NixOS 2 | 3 | Run serverless functions on NixOS using [faasd](https://github.com/openfaas/faasd) - a lightweight & portable faas engine. 4 | 5 | If you are new to [faasd](https://github.com/openfaas/faasd) and [OpenFaaS](https://github.com/openfaas/) checkout the following resources to get started: 6 | 7 | - [The faasd README](https://github.com/openfaas/faasd#readme) 8 | - [The OpenFaaS documentation](https://docs.openfaas.com) 9 | - [The official faasd handbook and docs](https://gumroad.com/l/serverless-for-everyone-else) 10 | 11 | ## Quick start 12 | 13 | The easiest way to try out faasd-nix is to run a NixOS vm with nixos-shell. 14 | 15 | > This guide assumes you have the experimental flake commands enabled. 16 | > To enable them add the following line to `~/.config/nix/nix.conf`: 17 | > 18 | > ``` 19 | > experimental-features = nix-command flakes 20 | > ``` 21 | 22 | 1. Start a shell with the tools needed to run the faasd-vm. 23 | ```sh 24 | $ nix develop github:welteki/faasd-nix#faasd-vm 25 | ``` 26 | This will make [nixos-shell](https://github.com/Mic92/nixos-shell) and the [faas-cli](https://github.com/openfaas/faas-cli) available in your shell. 27 | 2. Start the faasd-vm. 28 | ```sh 29 | $ nixos-shell --flake github:welteki/faasd-nix#faasd-vm 30 | ``` 31 | This spawns a headless qemu virtual machine with faasd running and provides console access in the same terminal window. 32 | 3. Log in as "root" with an empty password. 33 | 4. Interact with faasd using the faas-cli. 34 | 35 | ```sh 36 | # Login 37 | $ cat /var/lib/faasd/secrets/basic-auth-password | faas-cli login --password-stdin 38 | 39 | # Deploy a function from the function store 40 | $ faas-cli store deploy figlet 41 | 42 | # Invoke a function 43 | $ echo "faasd-nix" | faas-cli invoke figlet 44 | ``` 45 | 46 | 5. Type `Ctrl-a x` to exit the virtual machine or run the `poweroff` command in the virtual machine console. 47 | 48 | ## Configuration and options 49 | 50 | The faasd NixOS modules include some options to simplify common faasd configuration tasks. 51 | 52 | > Full reference: [faasd NixOS module options](./OPTIONS.md) 53 | 54 | ### Enabeling the service 55 | 56 | ```nix 57 | { 58 | services.faasd.enable = true; 59 | } 60 | ``` 61 | 62 | ### Gateway configuration 63 | 64 | Adjust the gateway timeouts. 65 | 66 | ```nix 67 | { 68 | services.faasd.gateway = { 69 | writeTimeout = 30; 70 | readTimeout = 30; 71 | upstreamTimeout = 35; 72 | }; 73 | } 74 | ``` 75 | 76 | ### Additional namespaces 77 | 78 | Add additional function namespaces using the `services.faasd.namespaces` option. 79 | 80 | ```nix 81 | { services.faasd.namespaces = [ "dev" ]; } 82 | ``` 83 | 84 | All namespaces in this list will be created and labeled `openfaas=true` so they can be used with faasd. 85 | 86 | ### Additional containers and services 87 | 88 | Declaratively deploy additional containers. 89 | 90 | This is the grafana example taken from the [serverless-book](https://gumroad.com/l/serverless-for-everyone-else). 91 | 92 | ```nix 93 | { 94 | systemd.tmpfiles.rules = [ 95 | "d '/var/lib/faasd/grafana'" 96 | ]; 97 | 98 | services.faasd.containers = { 99 | grafana = { 100 | image = "docker.io/grafana/grafana:latest"; 101 | environment = [ 102 | "GF_AUTH_ANONYMOUS_ORG_ROLE=Admin" 103 | "GF_AUTH_ANONYMOUS_ENABLED=true" 104 | "GF_AUTH_BASIC_ENABLED=false" 105 | ]; 106 | volumes = [{ 107 | type = "bind"; 108 | source = "./grafana/"; 109 | target = "/etc/grafana/provisioning"; 110 | }]; 111 | cap_add = [ "CAP_NET_RAW" ]; 112 | depends_on = [ "prometheus" ]; 113 | ports = [ "3000:3000" ]; 114 | }; 115 | }; 116 | } 117 | ``` 118 | 119 | ### Parallelism and multiple queues 120 | 121 | Increase the parallelism for async function invocations. 122 | 123 | ```nix 124 | { services.faasd.defaultQueue.maxInflight = 4; } 125 | ``` 126 | 127 | Easily deploy and configure multiple queues. 128 | 129 | ```nix 130 | { 131 | services.faasd.queues.slow-queue = { 132 | maxInflight = 1; 133 | natsChannel = "slow-queue"; 134 | }; 135 | } 136 | ``` 137 | 138 | > Check the [OpenFaaS documentation](https://docs.openfaas.com/reference/async/) for more info on asynchronous functions. 139 | 140 | ## Deploy with terraform and deploy-rs 141 | 142 | The [bootstrap folder](bootstrap) contains an example of how to provision a NixOS instance on hetzner-cloud using terraform and deploy faasd on it. 143 | -------------------------------------------------------------------------------- /bootstrap/hetzner-cloud-terraform/.gitignore: -------------------------------------------------------------------------------- 1 | /.terraform/ 2 | /terraform.tfstate 3 | /terraform.tfstate.backup 4 | -------------------------------------------------------------------------------- /bootstrap/hetzner-cloud-terraform/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/nixpkgs/hcloud" { 5 | version = "1.26.0" 6 | constraints = "~> 1.26.0" 7 | hashes = [ 8 | "h1:W903Siy2aD6cFd4v+fPj4JZwLDOOpID90Wk99TSSMyY=", 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /bootstrap/hetzner-cloud-terraform/README.md: -------------------------------------------------------------------------------- 1 | # Bootstrap faasd on Nixos 2 | 3 | This example uses terraform and deploy-rs to deploy a faasd instance on Hetzner Cloud. 4 | 5 | 1. [Sign up for Hetzner Cloud](https://hetzner.cloud/?ref=EIOIDMcuuXRl) 6 | 2. You will need to have [Nix](https://nixos.org/) installed. This project uses an experimental feature called `flakes` that needs to be enabled. 7 | 8 | To enable flake support, add the following line to `~/.config/nix/nix.conf` 9 | 10 | ``` 11 | experimental-features = nix-command flakes 12 | ``` 13 | 14 | 3. Create a new flake from the `hc-bootstrap` template in the specified directory. 15 | ``` 16 | nix flake new -t github:welteki/faasd-nix#hc-bootstrap faasd-bootstrap 17 | ``` 18 | 4. cd into the directory and activate the development shell. This will make sure all the required tools such as terraform and deploy-rs are available your shell. 19 | ``` 20 | cd faasd-bootstrap 21 | nix develop 22 | ``` 23 | 5. Run `terraform init` 24 | 6. Configure the deployment as needed by updating the `config.json` file: 25 | 26 | | Variable | Description | Default | Sensitive | 27 | | ---------------- | ----------------------- | ------- | --------- | 28 | | `hc_token` | Hetzner Cloud API token | None | true | 29 | | `ssh_public_key` | Public SSH key | None | | 30 | 31 | 7. Run `terraform apply` 32 | > The terraform module uses [NixOs-Infect](https://github.com/elitak/nixos-infect) to install nixos over the existing os on the Hetzner server instance. This can sometimes take between 2 and 3 minutes. The next steps can only be executed when the infect script has finished succesfully. Currently the terraform module does not wait for this to finish. 33 | 8. View the terraform output for the deploy command and run it. 34 | ``` 35 | terraform output deploy_cmd 36 | deploy_cmd = deploy .#faasd --hostname=178.128.39.201 --ssh-user=root 37 | ``` 38 | 9. View the terraform output for the gateway url and faas-cli login command 39 | ``` 40 | terraform output 41 | deploy_cmd = "deploy .#faasd --hostname=178.128.39.201 --ssh-user=root" 42 | gateway_url = "http://178.128.39.201:8080/" 43 | login_cmd = "ssh root@178.128.39.201 'cat /var/lib/faasd/secrets/basic-auth-password' | faas-cli login -g http://178.128.39.201:8080/ -s" 44 | ``` 45 | -------------------------------------------------------------------------------- /bootstrap/hetzner-cloud-terraform/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ssh_public_key": "" 3 | } 4 | -------------------------------------------------------------------------------- /bootstrap/hetzner-cloud-terraform/flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "deploy-rs": { 4 | "inputs": { 5 | "flake-compat": "flake-compat", 6 | "nixpkgs": "nixpkgs", 7 | "utils": "utils" 8 | }, 9 | "locked": { 10 | "lastModified": 1632822684, 11 | "narHash": "sha256-lt7eayYmgsD5OQwpb1XYfHpxttn43bWo7G7hIJs+zJw=", 12 | "owner": "serokell", 13 | "repo": "deploy-rs", 14 | "rev": "9a02de4373e0ec272d08a417b269a28ac8b961b4", 15 | "type": "github" 16 | }, 17 | "original": { 18 | "owner": "serokell", 19 | "repo": "deploy-rs", 20 | "type": "github" 21 | } 22 | }, 23 | "faasd": { 24 | "inputs": { 25 | "faasd-src": "faasd-src", 26 | "flake-compat": "flake-compat_2", 27 | "nixos-shell": "nixos-shell", 28 | "nixpkgs": "nixpkgs_2", 29 | "utils": "utils_2" 30 | }, 31 | "locked": { 32 | "lastModified": 1642410897, 33 | "narHash": "sha256-DtaDy6uYprzkt1gZS/8tvI2HJckZWOLNf3gck9WXjrU=", 34 | "owner": "welteki", 35 | "repo": "faasd-nix", 36 | "rev": "bfc0233be38f6326798d383d0ebcd75dbc244750", 37 | "type": "github" 38 | }, 39 | "original": { 40 | "owner": "welteki", 41 | "repo": "faasd-nix", 42 | "type": "github" 43 | } 44 | }, 45 | "faasd-src": { 46 | "flake": false, 47 | "locked": { 48 | "narHash": "sha256-bRvlnbpVe76UsE4BDNiyoE7SB8Q3opPj/Fx5oU2REOM=", 49 | "type": "tarball", 50 | "url": "https://github.com/openfaas/faasd/archive/refs/tags/0.14.4.tar.gz" 51 | }, 52 | "original": { 53 | "type": "tarball", 54 | "url": "https://github.com/openfaas/faasd/archive/refs/tags/0.14.4.tar.gz" 55 | } 56 | }, 57 | "flake-compat": { 58 | "flake": false, 59 | "locked": { 60 | "lastModified": 1627913399, 61 | "narHash": "sha256-hY8g6H2KFL8ownSiFeMOjwPC8P0ueXpCVEbxgda3pko=", 62 | "owner": "edolstra", 63 | "repo": "flake-compat", 64 | "rev": "12c64ca55c1014cdc1b16ed5a804aa8576601ff2", 65 | "type": "github" 66 | }, 67 | "original": { 68 | "owner": "edolstra", 69 | "repo": "flake-compat", 70 | "type": "github" 71 | } 72 | }, 73 | "flake-compat_2": { 74 | "flake": false, 75 | "locked": { 76 | "lastModified": 1627557337, 77 | "narHash": "sha256-S/Aab7AcDS+MopcnkEWu0j3qOGLLaMw1E8VWWQOD+Q8=", 78 | "owner": "edolstra", 79 | "repo": "flake-compat", 80 | "rev": "c6f3b635321160c629b74fefc88c807b59a51646", 81 | "type": "github" 82 | }, 83 | "original": { 84 | "owner": "edolstra", 85 | "repo": "flake-compat", 86 | "type": "github" 87 | } 88 | }, 89 | "nixos-shell": { 90 | "inputs": { 91 | "nixpkgs": [ 92 | "faasd", 93 | "nixpkgs" 94 | ] 95 | }, 96 | "locked": { 97 | "lastModified": 1638785136, 98 | "narHash": "sha256-B1M3PVZEg9HIHSjbYSae6+u2w++Xt3oXoodIvG61zzg=", 99 | "owner": "welteki", 100 | "repo": "nixos-shell", 101 | "rev": "32a8bd8ba3df60863d1bd66868c10e721c9928d5", 102 | "type": "github" 103 | }, 104 | "original": { 105 | "owner": "welteki", 106 | "ref": "improve-flake-support", 107 | "repo": "nixos-shell", 108 | "type": "github" 109 | } 110 | }, 111 | "nixpkgs": { 112 | "locked": { 113 | "lastModified": 1632086102, 114 | "narHash": "sha256-wVTcf0UclFS+zHtfPToB13jIO7n0U9N50MuRbPjQViE=", 115 | "owner": "NixOS", 116 | "repo": "nixpkgs", 117 | "rev": "e0ce3c683ae677cf5aab597d645520cddd13392b", 118 | "type": "github" 119 | }, 120 | "original": { 121 | "owner": "NixOS", 122 | "ref": "nixpkgs-unstable", 123 | "repo": "nixpkgs", 124 | "type": "github" 125 | } 126 | }, 127 | "nixpkgs_2": { 128 | "locked": { 129 | "lastModified": 1638887115, 130 | "narHash": "sha256-emjtIeqyJ84Eb3X7APJruTrwcfnHQKs55XGljj62prs=", 131 | "owner": "NixOS", 132 | "repo": "nixpkgs", 133 | "rev": "1bd4bbd49bef217a3d1adea43498270d6e779d65", 134 | "type": "github" 135 | }, 136 | "original": { 137 | "id": "nixpkgs", 138 | "ref": "nixos-21.11", 139 | "type": "indirect" 140 | } 141 | }, 142 | "nixpkgs_3": { 143 | "locked": { 144 | "lastModified": 1633351077, 145 | "narHash": "sha256-z38JG4Bb0GtM1aF1pANVdp1dniMP23Yb3HnRoJRy2uU=", 146 | "owner": "NixOS", 147 | "repo": "nixpkgs", 148 | "rev": "14aef06d9b3ad1d07626bdbb16083b83f92dc6c1", 149 | "type": "github" 150 | }, 151 | "original": { 152 | "id": "nixpkgs", 153 | "ref": "nixos-unstable", 154 | "type": "indirect" 155 | } 156 | }, 157 | "root": { 158 | "inputs": { 159 | "deploy-rs": "deploy-rs", 160 | "faasd": "faasd", 161 | "nixpkgs": "nixpkgs_3", 162 | "utils": "utils_3" 163 | } 164 | }, 165 | "utils": { 166 | "locked": { 167 | "lastModified": 1631561581, 168 | "narHash": "sha256-3VQMV5zvxaVLvqqUrNz3iJelLw30mIVSfZmAaauM3dA=", 169 | "owner": "numtide", 170 | "repo": "flake-utils", 171 | "rev": "7e5bf3925f6fbdfaf50a2a7ca0be2879c4261d19", 172 | "type": "github" 173 | }, 174 | "original": { 175 | "owner": "numtide", 176 | "repo": "flake-utils", 177 | "type": "github" 178 | } 179 | }, 180 | "utils_2": { 181 | "locked": { 182 | "lastModified": 1623875721, 183 | "narHash": "sha256-A8BU7bjS5GirpAUv4QA+QnJ4CceLHkcXdRp4xITDB0s=", 184 | "owner": "numtide", 185 | "repo": "flake-utils", 186 | "rev": "f7e004a55b120c02ecb6219596820fcd32ca8772", 187 | "type": "github" 188 | }, 189 | "original": { 190 | "owner": "numtide", 191 | "repo": "flake-utils", 192 | "type": "github" 193 | } 194 | }, 195 | "utils_3": { 196 | "locked": { 197 | "lastModified": 1631561581, 198 | "narHash": "sha256-3VQMV5zvxaVLvqqUrNz3iJelLw30mIVSfZmAaauM3dA=", 199 | "owner": "numtide", 200 | "repo": "flake-utils", 201 | "rev": "7e5bf3925f6fbdfaf50a2a7ca0be2879c4261d19", 202 | "type": "github" 203 | }, 204 | "original": { 205 | "owner": "numtide", 206 | "repo": "flake-utils", 207 | "type": "github" 208 | } 209 | } 210 | }, 211 | "root": "root", 212 | "version": 7 213 | } 214 | -------------------------------------------------------------------------------- /bootstrap/hetzner-cloud-terraform/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Bootstrap faasd on hetzner cloud using terraform and deploy-rs"; 3 | 4 | inputs = { 5 | nixpkgs.url = "nixpkgs/nixos-unstable"; 6 | utils.url = "github:numtide/flake-utils"; 7 | deploy-rs.url = "github:serokell/deploy-rs"; 8 | faasd.url = "github:welteki/faasd-nix"; 9 | }; 10 | 11 | outputs = { self, nixpkgs, deploy-rs, utils, faasd }: 12 | let 13 | inherit (nixpkgs.lib) nixosSystem; 14 | system = "x86_64-linux"; 15 | in 16 | { 17 | nixosConfigurations.faasd = 18 | let 19 | configFile = ./config.json; 20 | config = builtins.fromJSON (builtins.readFile configFile); 21 | in 22 | nixosSystem { 23 | inherit system; 24 | modules = [ 25 | ({ pkgs, ... }: 26 | { 27 | imports = [ 28 | ./hetzner-cloud.nix 29 | faasd.nixosModules.faasd 30 | ]; 31 | 32 | services.openssh = { 33 | enable = true; 34 | passwordAuthentication = false; 35 | }; 36 | users.users.root.openssh.authorizedKeys.keys = [ config.ssh_public_key ]; 37 | 38 | services.faasd.enable = true; 39 | }) 40 | ]; 41 | }; 42 | 43 | deploy = { 44 | magicRollback = false; 45 | 46 | nodes.faasd = { 47 | hostname = ""; 48 | profiles.system.user = "root"; 49 | profiles.system.path = 50 | deploy-rs.lib.${system}.activate.nixos self.nixosConfigurations.faasd; 51 | }; 52 | }; 53 | } // utils.lib.eachDefaultSystem (system: 54 | let 55 | pkgs = nixpkgs.legacyPackages.${system}; 56 | terrafrom-withplugins = (pkgs.terraform.withPlugins (p: with p; [ hcloud ])); 57 | terraform = pkgs.writeShellScriptBin "terraform" '' 58 | flag= 59 | 60 | if [[ "$1" = "plan" || "$1" = "apply" ]] 61 | then flag="-var-file=$BOOTSTRAP_CONFIG" 62 | fi 63 | 64 | ${terrafrom-withplugins}/bin/terraform $* $flag 65 | ''; 66 | in 67 | { 68 | devShell = pkgs.mkShell { 69 | shellHook = '' 70 | export BOOTSTRAP_CONFIG="./config.json" 71 | ''; 72 | 73 | buildInputs = [ 74 | terraform 75 | deploy-rs.packages.${system}.deploy-rs 76 | ]; 77 | }; 78 | 79 | checks = deploy-rs.lib.${system}.deployChecks self.deploy; 80 | } 81 | ); 82 | } 83 | -------------------------------------------------------------------------------- /bootstrap/hetzner-cloud-terraform/hetzner-cloud.nix: -------------------------------------------------------------------------------- 1 | { config, modulesPath, ... }: 2 | 3 | { 4 | imports = [ (modulesPath + "/profiles/qemu-guest.nix") ]; 5 | 6 | boot.loader.grub.device = "/dev/sda"; 7 | boot.initrd.kernelModules = [ "nvme" ]; 8 | fileSystems."/" = { device = "/dev/sda1"; fsType = "ext4"; }; 9 | } 10 | -------------------------------------------------------------------------------- /bootstrap/hetzner-cloud-terraform/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | hcloud = { 4 | source = "nixpkgs/hcloud" 5 | version = "~> 1.26.0" 6 | } 7 | } 8 | ## Prevent unwanted updates 9 | required_version = "1.0.8" # Use nix-shell or nix develop 10 | } 11 | 12 | variable "hc_token" { 13 | description = "Hetzner Cloud API token" 14 | } 15 | variable "ssh_public_key" { 16 | description = "Public ssh key" 17 | } 18 | 19 | provider "hcloud" { 20 | token = var.hc_token 21 | } 22 | 23 | resource "hcloud_ssh_key" "faasd_ssh_key" { 24 | name = "faasd-ssh-key" 25 | public_key = var.ssh_public_key 26 | } 27 | 28 | resource "hcloud_server" "faasd" { 29 | name = "faasd" 30 | image = "ubuntu-20.04" 31 | server_type = "cx11" 32 | ssh_keys = [hcloud_ssh_key.faasd_ssh_key.id] 33 | # Install NixOS 20.05 34 | user_data = <&1 | tee /tmp/infect.log 39 | EOF 40 | } 41 | 42 | output "deploy_cmd" { 43 | value = "deploy .#faasd --hostname=${hcloud_server.faasd.ipv4_address} --ssh-user=root" 44 | } 45 | 46 | output "gateway_url" { 47 | value = "http://${hcloud_server.faasd.ipv4_address}:8080/" 48 | } 49 | 50 | output "login_cmd" { 51 | value = "ssh root@${hcloud_server.faasd.ipv4_address} 'cat /var/lib/faasd/secrets/basic-auth-password' | faas-cli login -g http://${hcloud_server.faasd.ipv4_address}:8080/ -s" 52 | } 53 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "faasd-src": { 4 | "flake": false, 5 | "locked": { 6 | "lastModified": 1718183981, 7 | "narHash": "sha256-neXwGdPuC0sg2JvxNmAq5ybbAp/XgySa4t8aR2sjF/4=", 8 | "owner": "openfaas", 9 | "repo": "faasd", 10 | "rev": "e4848cd829286c174bda45b40fc8c5ae5c571d08", 11 | "type": "github" 12 | }, 13 | "original": { 14 | "owner": "openfaas", 15 | "ref": "0.19.0", 16 | "repo": "faasd", 17 | "type": "github" 18 | } 19 | }, 20 | "nixos-shell": { 21 | "inputs": { 22 | "nixpkgs": [ 23 | "nixpkgs" 24 | ] 25 | }, 26 | "locked": { 27 | "lastModified": 1711263551, 28 | "narHash": "sha256-lDaSa0yT0uzFXq1rB0DbD5MNi2TmG9DaTrZqZoPP/I4=", 29 | "owner": "Mic92", 30 | "repo": "nixos-shell", 31 | "rev": "b7e8a0c75c99d81039d1ca7eaab227e4814de638", 32 | "type": "github" 33 | }, 34 | "original": { 35 | "owner": "Mic92", 36 | "repo": "nixos-shell", 37 | "type": "github" 38 | } 39 | }, 40 | "nixpkgs": { 41 | "locked": { 42 | "lastModified": 1721686456, 43 | "narHash": "sha256-nw/BnNzATDPfzpJVTnY8mcSKKsz6BJMEFRkJ332QSN0=", 44 | "owner": "NixOS", 45 | "repo": "nixpkgs", 46 | "rev": "575f3027caa1e291d24f1e9fb0e3a19c2f26d96b", 47 | "type": "github" 48 | }, 49 | "original": { 50 | "id": "nixpkgs", 51 | "ref": "nixos-24.05", 52 | "type": "indirect" 53 | } 54 | }, 55 | "root": { 56 | "inputs": { 57 | "faasd-src": "faasd-src", 58 | "nixos-shell": "nixos-shell", 59 | "nixpkgs": "nixpkgs", 60 | "utils": "utils" 61 | } 62 | }, 63 | "systems": { 64 | "locked": { 65 | "lastModified": 1681028828, 66 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 67 | "owner": "nix-systems", 68 | "repo": "default", 69 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 70 | "type": "github" 71 | }, 72 | "original": { 73 | "owner": "nix-systems", 74 | "repo": "default", 75 | "type": "github" 76 | } 77 | }, 78 | "utils": { 79 | "inputs": { 80 | "systems": "systems" 81 | }, 82 | "locked": { 83 | "lastModified": 1710146030, 84 | "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", 85 | "owner": "numtide", 86 | "repo": "flake-utils", 87 | "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", 88 | "type": "github" 89 | }, 90 | "original": { 91 | "owner": "numtide", 92 | "repo": "flake-utils", 93 | "type": "github" 94 | } 95 | } 96 | }, 97 | "root": "root", 98 | "version": 7 99 | } 100 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "A lightweight & portable faas engine"; 3 | 4 | inputs = { 5 | nixpkgs.url = "nixpkgs/nixos-24.05"; 6 | utils.url = "github:numtide/flake-utils"; 7 | faasd-src = { 8 | url = "github:openfaas/faasd?ref=0.19.0"; 9 | flake = false; 10 | }; 11 | nixos-shell.url = "github:Mic92/nixos-shell"; 12 | nixos-shell.inputs.nixpkgs.follows = "nixpkgs"; 13 | }; 14 | 15 | nixConfig.extra-substituters = [ "https://welteki.cachix.org" ]; 16 | nixConfig.extra-trusted-public-keys = [ "welteki.cachix.org-1:zb0txiNEbjq9Fx7svp4LhTgFIQHKSa5ESi7QlLFjjQY=" ]; 17 | 18 | outputs = { self, nixpkgs, utils, faasd-src, ... }@inputs: 19 | let 20 | faasdVersion = lock.nodes.faasd-src.original.ref; 21 | faasdRev = lock.nodes.faasd-src.locked.rev; 22 | lock = builtins.fromJSON (builtins.readFile ./flake.lock); 23 | 24 | images = 25 | { 26 | gateway = { 27 | name = "ghcr.io/openfaas/gateway"; 28 | tag = "0.27.5"; 29 | }; 30 | queue-worker = { 31 | name = "ghcr.io/openfaas/queue-worker"; 32 | tag = "0.14.1"; 33 | }; 34 | nats = { 35 | name = "docker.io/library/nats-streaming"; 36 | tag = "0.25.6"; 37 | }; 38 | prometheus = { 39 | name = "docker.io/prom/prometheus"; 40 | tag = "v2.49.1"; 41 | }; 42 | }; 43 | 44 | supportedSystems = [ 45 | "x86_64-linux" 46 | ]; 47 | 48 | # NixOS configuration used for VM tests. 49 | faasdServer = 50 | { pkgs, ... }: 51 | { 52 | imports = [ self.nixosModules.faasd ]; 53 | 54 | virtualisation.memorySize = 1024; 55 | 56 | services.faasd.enable = true; 57 | 58 | environment.systemPackages = [ pkgs.faas-cli ]; 59 | system.stateVersion = "22.05"; 60 | }; 61 | in 62 | { 63 | overlays.default = final: prev: 64 | let 65 | inherit (final) 66 | buildGoModule 67 | fetchFromGitHub 68 | dockerTools; 69 | 70 | inherit (dockerTools) pullImage; 71 | image-parameters = import ./images.nix; 72 | in 73 | { 74 | faasd-containerd = prev.containerd.overrideAttrs (old: rec { 75 | version = "1.7.18"; 76 | 77 | src = fetchFromGitHub { 78 | owner = "containerd"; 79 | repo = "containerd"; 80 | rev = "v${version}"; 81 | sha256 = "sha256-OHgakSNqIbXYDC7cTw2fy0HlElQMilDbSD5SSjbYJhc="; 82 | }; 83 | }); 84 | 85 | faasd-cni-plugins = prev.cni-plugins.overrideAttrs (old: rec { 86 | version = "0.9.1"; 87 | 88 | src = fetchFromGitHub { 89 | owner = "containernetworking"; 90 | repo = "plugins"; 91 | rev = "v${version}"; 92 | sha256 = "sha256-n+OtFXgFmW0xsGEtC6ua0qjdsJSbEjn08mAl5Z51Kp8="; 93 | }; 94 | 95 | subPackages = [ 96 | "plugins/ipam/dhcp" 97 | "plugins/ipam/host-local" 98 | "plugins/ipam/static" 99 | "plugins/main/bridge" 100 | "plugins/main/host-device" 101 | "plugins/main/ipvlan" 102 | "plugins/main/loopback" 103 | "plugins/main/macvlan" 104 | "plugins/main/ptp" 105 | "plugins/main/vlan" 106 | "plugins/meta/bandwidth" 107 | "plugins/meta/firewall" 108 | "plugins/meta/portmap" 109 | "plugins/meta/sbr" 110 | "plugins/meta/tuning" 111 | "plugins/meta/vrf" 112 | ]; 113 | }); 114 | 115 | containerd = final.faasd-containerd; 116 | cni-plugins = final.faasd-cni-plugins; 117 | 118 | faasd = buildGoModule { 119 | pname = "faasd"; 120 | version = "${faasdVersion}"; 121 | 122 | src = "${faasd-src}"; 123 | 124 | vendorHash = null; 125 | 126 | CGO_ENABLED = 0; 127 | 128 | ldflags = [ 129 | "-s" 130 | "-w" 131 | "-X main.Version=${faasdVersion}" 132 | "-X main.GitCommit=${faasdRev}" 133 | ]; 134 | 135 | postInstall = '' 136 | mkdir -p $out/installation 137 | cp ./docker-compose.yaml $out/installation/docker-compose.yaml 138 | cp ./prometheus.yml $out/installation/prometheus.yml 139 | cp ./resolv.conf $out/installation/resolv.conf 140 | ''; 141 | }; 142 | 143 | prefetch-images = import ./scripts/prefetch-images.nix images final; 144 | faasd-options-doc = import ./pkgs/options-doc.nix final; 145 | 146 | openfaas-images = { 147 | gateway = pullImage image-parameters.gateway; 148 | queue-worker = pullImage image-parameters.queue-worker; 149 | nats = pullImage image-parameters.nats; 150 | prometheus = pullImage image-parameters.prometheus; 151 | }; 152 | }; 153 | 154 | nixosModules.faasd = { 155 | imports = [ ./modules/faasd-module.nix ]; 156 | nixpkgs.overlays = [ self.overlays.default ]; 157 | }; 158 | 159 | nixosConfigurations.faasd-vm = nixpkgs.lib.makeOverridable nixpkgs.lib.nixosSystem { 160 | system = "x86_64-linux"; 161 | modules = [ 162 | inputs.nixos-shell.nixosModules.nixos-shell 163 | faasdServer 164 | (args: { nixos-shell.mounts.mountHome = false; }) 165 | ]; 166 | }; 167 | 168 | checks.x86_64-linux.faasd = with import (nixpkgs + "/nixos/lib/testing-python.nix") { system = "x86_64-linux"; }; 169 | simpleTest { 170 | name = "faasd"; 171 | 172 | nodes.faasd = { pkgs, ... }: 173 | let 174 | images = { 175 | figlet = pkgs.dockerTools.pullImage { 176 | imageName = "ghcr.io/openfaas/figlet"; 177 | imageDigest = "sha256:98b7e719cabe654a7d2f8ff0f3c11294ef737a1f1e4eeef7321277420dfebe8d"; 178 | finalImageTag = "latest"; 179 | sha256 = "sha256-leYfLXQ4cCvPOudU82UaV9BkUIA+W6YvM0zx3BlyCjw="; 180 | }; 181 | 182 | nodeInfo = pkgs.dockerTools.pullImage { 183 | imageName = "ghcr.io/openfaas/nodeinfo"; 184 | imageDigest = "sha256:018db1b62c35acf63ff79be5e252128b2477c134421fe1322c159438edf6bcaf"; 185 | finalImageTag = "stable"; 186 | sha256 = "sha256-1hOPmt9fKdQUPokVhx9C62XYYC9iZ++QVI/iEb5+/Hk="; 187 | }; 188 | }; 189 | in 190 | { 191 | imports = [ faasdServer ]; 192 | 193 | services.faasd = { 194 | seedCoreImages = true; 195 | seedDockerImages = [ 196 | { 197 | namespace = "openfaas-fn"; 198 | imageFile = images.figlet; 199 | } 200 | { 201 | namespace = "openfaas-fn"; 202 | imageFile = images.nodeInfo; 203 | } 204 | ]; 205 | pullPolicy = "IfNotPresent"; 206 | # Use local DNS server. 207 | nameserver = "127.0.0.1"; 208 | }; 209 | 210 | # Start local DNS server. Required by faasd to function when running 211 | # without internet connection. 212 | services.coredns.enable = true; 213 | }; 214 | testScript = 215 | '' 216 | # Wait until faasd can receive HTTP requests 217 | faasd.wait_for_job("faasd-provider") 218 | faasd.wait_for_job("faasd") 219 | faasd.wait_for_open_port(8080) 220 | 221 | with subtest("login to faasd"): 222 | faasd.succeed( 223 | "cat /var/lib/faasd/secrets/basic-auth-password | faas-cli login --password-stdin") 224 | 225 | with subtest("deploy and invoke functions"): 226 | faasd.succeed("faas-cli deploy --image ghcr.io/openfaas/figlet:latest --name figlet") 227 | faasd.succeed("echo faasd | faas-cli invoke figlet") 228 | faasd.succeed("echo faasd | faas-cli invoke figlet --async") 229 | ''; 230 | }; 231 | 232 | templates = { 233 | hc-bootstrap = { 234 | path = ./bootstrap/hetzner-cloud-terraform; 235 | description = "Bootstrap faasd on hetzner cloud"; 236 | }; 237 | }; 238 | 239 | } // utils.lib.eachSystem supportedSystems (system: 240 | let 241 | pkgs = import nixpkgs { 242 | inherit system; 243 | overlays = [ self.overlays.default ]; 244 | }; 245 | in 246 | { 247 | packages = { 248 | default = pkgs.faasd; 249 | inherit (pkgs) faasd faasd-containerd faasd-cni-plugins; 250 | 251 | gateway-image = pkgs.openfaas-images.gateway; 252 | queue-worker-image = pkgs.openfaas-images.queue-worker; 253 | nats-image = pkgs.openfaas-images.nats; 254 | prometheus-image = pkgs.openfaas-images.prometheus; 255 | 256 | prefetch-images = pkgs.prefetch-images; 257 | faasd-test = self.checks.${system}.faasd; 258 | gen-options-doc = import ./scripts/gen-options-doc.nix pkgs; 259 | }; 260 | 261 | devShells.faasd-vm = pkgs.mkShell { 262 | buildInputs = [ 263 | pkgs.nixos-shell 264 | pkgs.faas-cli 265 | ]; 266 | }; 267 | 268 | devShells.default = pkgs.mkShell { 269 | buildInputs = [ 270 | pkgs.nixos-shell 271 | pkgs.faas-cli 272 | 273 | pkgs.cachix 274 | pkgs.statix 275 | pkgs.vulnix 276 | pkgs.deadnix 277 | pkgs.nil 278 | ]; 279 | }; 280 | }); 281 | } 282 | -------------------------------------------------------------------------------- /images.nix: -------------------------------------------------------------------------------- 1 | { 2 | gateway = { 3 | imageName = "ghcr.io/openfaas/gateway"; 4 | imageDigest = "sha256:a0814d6ede0a8a5fadb5b7e71f3fbfafef1d7a5f6e814babedfd24353a71641b"; 5 | sha256 = "1i33f66zxbqhbjcpps5sha6zyhd705gpl0xabjpjayqybv6m96r0"; 6 | finalImageName = "ghcr.io/openfaas/gateway"; 7 | finalImageTag = "0.27.5"; 8 | }; 9 | queue-worker = { 10 | imageName = "ghcr.io/openfaas/queue-worker"; 11 | imageDigest = "sha256:978ed61fa5dbfb90b6fd00bdbf15965d03d49d7d5d720a653d1c180ca7740162"; 12 | sha256 = "0chm5rzz1hc4fhhmfbwjqa1c8mhdg0mf7i4pfbqas4hhv1bvyn89"; 13 | finalImageName = "ghcr.io/openfaas/queue-worker"; 14 | finalImageTag = "0.14.1"; 15 | }; 16 | nats = { 17 | imageName = "docker.io/library/nats-streaming"; 18 | imageDigest = "sha256:d3175589326bc542cdc97ec4900237e1b603492994037e2e0451fb86de40bfb0"; 19 | sha256 = "13d5m5fyyqjqbq96f5q7rlnqbkqsl8fmz9q5fdqps2gz67bkdz5g"; 20 | finalImageName = "docker.io/library/nats-streaming"; 21 | finalImageTag = "0.25.6"; 22 | }; 23 | prometheus = { 24 | imageName = "docker.io/prom/prometheus"; 25 | imageDigest = "sha256:beb5e30ffba08d9ae8a7961b9a2145fc8af6296ff2a4f463df7cd722fcbfc789"; 26 | sha256 = "0gp3y00vq7mr1w8jlda345886nvf6m4xvp06zk83pxzxm9glv5xh"; 27 | finalImageName = "docker.io/prom/prometheus"; 28 | finalImageTag = "v2.49.1"; 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /modules/core-services/default.nix: -------------------------------------------------------------------------------- 1 | [ 2 | ./gateway.nix 3 | ./nats.nix 4 | ./prometheus.nix 5 | ./queue-worker.nix 6 | ] 7 | -------------------------------------------------------------------------------- /modules/core-services/gateway.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, lib, ... }: 2 | let 3 | inherit (lib) mkIf mkOption types; 4 | inherit (types) bool int; 5 | 6 | inherit (import ../../images.nix) gateway; 7 | 8 | cfg = config.services.faasd; 9 | 10 | boolToString = e: if e then "true" else "false"; 11 | 12 | gatewayOpts = { 13 | writeTimeout = mkOption { 14 | description = "HTTP timeout for writing a response body from your function (in seconds)"; 15 | type = int; 16 | default = 60; 17 | }; 18 | 19 | readTimeout = mkOption { 20 | description = "HTTP timeout for reading the payload from the client caller (in seconds)."; 21 | type = int; 22 | default = 60; 23 | }; 24 | 25 | upstreamTimeout = mkOption { 26 | description = "Maximum duration of HTTP call to upstream URL (in seconds)."; 27 | type = int; 28 | default = 65; 29 | }; 30 | 31 | scaleFormZero = mkOption { 32 | description = "Enables an intercepting proxy which will scale any function from 0 replicas to the desired amount"; 33 | type = bool; 34 | default = true; 35 | }; 36 | }; 37 | in 38 | { 39 | options = { 40 | services.faasd.gateway = gatewayOpts; 41 | }; 42 | 43 | config = { 44 | services.faasd.containers.gateway = { 45 | image = "${gateway.imageName}:${gateway.finalImageTag}"; 46 | imageFile = mkIf cfg.seedCoreImages pkgs.openfaas-images.gateway; 47 | environment = { 48 | basic_auth = boolToString cfg.basicAuth.enable; 49 | functions_provider_url = "http://faasd-provider:8081/"; 50 | direct_functions = "false"; 51 | read_timeout = cfg.gateway.readTimeout; 52 | write_timeout = cfg.gateway.writeTimeout; 53 | upstream_timeout = cfg.gateway.upstreamTimeout; 54 | faas_nats_address = "nats"; 55 | faas_nats_port = 4222; 56 | secret_mount_path = "/run/secrets"; 57 | scale_from_zero = boolToString cfg.gateway.scaleFormZero; 58 | function_namespace = "openfaas-fn"; 59 | }; 60 | volumes = [ 61 | { 62 | type = "bind"; 63 | source = "./secrets/basic-auth-password"; 64 | target = "/run/secrets/basic-auth-password"; 65 | 66 | } 67 | { 68 | type = "bind"; 69 | source = "./secrets/basic-auth-user"; 70 | target = "/run/secrets/basic-auth-user"; 71 | } 72 | ]; 73 | cap_add = [ "CAP_NET_RAW" ]; 74 | depends_on = [ 75 | "nats" 76 | "prometheus" 77 | ]; 78 | ports = [ 79 | "8080:8080" 80 | ]; 81 | }; 82 | }; 83 | } 84 | -------------------------------------------------------------------------------- /modules/core-services/nats.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, lib, ... }: 2 | let 3 | inherit (import ../../images.nix) nats; 4 | 5 | cfg = config.services.faasd; 6 | in 7 | { 8 | config.services.faasd.containers = { 9 | nats = { 10 | image = "${nats.imageName}:${nats.finalImageTag}"; 11 | imageFile = lib.mkIf cfg.seedCoreImages pkgs.openfaas-images.nats; 12 | command = [ 13 | "/nats-streaming-server" 14 | "-m" 15 | "8222" 16 | "--store=file" 17 | "--dir=/nats" 18 | "--cluster_id=faas-cluster" 19 | ]; 20 | volumes = [ 21 | { 22 | type = "bind"; 23 | source = "./nats"; 24 | target = "/nats"; 25 | } 26 | ]; 27 | user = "65534"; 28 | }; 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /modules/core-services/prometheus.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, lib, ... }: 2 | let 3 | inherit (import ../../images.nix) prometheus; 4 | 5 | cfg = config.services.faasd; 6 | in 7 | { 8 | config.services.faasd.containers = { 9 | prometheus = { 10 | image = "${prometheus.imageName}:${prometheus.finalImageTag}"; 11 | imageFile = lib.mkIf cfg.seedCoreImages pkgs.openfaas-images.prometheus; 12 | volumes = [ 13 | { 14 | #Config directory 15 | type = "bind"; 16 | source = "./prometheus.yml"; 17 | target = "/etc/prometheus/prometheus.yml"; 18 | } 19 | { 20 | type = "bind"; 21 | source = "./prometheus"; 22 | target = "/prometheus"; 23 | } 24 | ]; 25 | cap_add = [ "CAP_NET_RAW" ]; 26 | ports = [ 27 | "127.0.0.1:9090:9090" 28 | ]; 29 | user = "65534"; 30 | }; 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /modules/core-services/queue-worker.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, lib, ... }: 2 | let 3 | inherit (lib) mkIf mkOption types; 4 | 5 | inherit (import ../../images.nix) queue-worker; 6 | 7 | cfg = config.services.faasd; 8 | 9 | boolToString = e: if e then "true" else "false"; 10 | 11 | mkContainer = 12 | { natsChannel ? "faas-request" 13 | , maxInflight ? 1 14 | , writeDebug ? false 15 | }: { 16 | image = "${queue-worker.imageName}:${queue-worker.finalImageTag}"; 17 | imageFile = mkIf cfg.seedCoreImages pkgs.openfaas-images.queue-worker; 18 | environment = { 19 | faas_nats_address = "nats"; 20 | faas_nats_port = 4222; 21 | faas_nats_channel = natsChannel; 22 | gateway_invoke = "true"; 23 | faas_gateway_address = "gateway"; 24 | ack_wait = "5m5s"; 25 | max_inflight = maxInflight; 26 | write_debug = boolToString writeDebug; 27 | basic_auth = boolToString cfg.basicAuth.enable; 28 | secret_mount_path = "/run/secrets"; 29 | }; 30 | volumes = [ 31 | { 32 | type = "bind"; 33 | source = "./secrets/basic-auth-password"; 34 | target = "/run/secrets/basic-auth-password"; 35 | } 36 | { 37 | type = "bind"; 38 | source = "./secrets/basic-auth-user"; 39 | target = "/run/secrets/basic-auth-user"; 40 | } 41 | ]; 42 | cap_add = [ "CAP_NET_RAW" ]; 43 | depends_on = [ 44 | "nats" 45 | ]; 46 | }; 47 | 48 | defaultQueueOpts = { 49 | maxInflight = mkOption { 50 | description = "Number of messages sent to queue worker and how many functions are invoked concurrently."; 51 | type = types.int; 52 | default = 1; 53 | }; 54 | 55 | writeDebug = mkOption { 56 | description = "Print verbose logs"; 57 | type = types.bool; 58 | default = false; 59 | }; 60 | }; 61 | 62 | queueOpts = { name, ... }: { 63 | options = defaultQueueOpts // { 64 | natsChannel = mkOption { 65 | description = "Nats channel to use for the queue. Defaults to the queue name."; 66 | type = types.str; 67 | default = name; 68 | }; 69 | }; 70 | }; 71 | in 72 | { 73 | options = { 74 | services.faasd.defaultQueue = defaultQueueOpts; 75 | 76 | services.faasd.queues = mkOption { 77 | description = ""; 78 | type = types.attrsOf (types.submodule queueOpts); 79 | default = { }; 80 | }; 81 | }; 82 | 83 | config = { 84 | services.faasd.containers = (lib.mapAttrs' 85 | (queueName: cfg: { 86 | name = "${queueName}-worker"; 87 | value = mkContainer cfg; 88 | }) 89 | cfg.queues) // { queue-worker = mkContainer cfg.defaultQueue; }; 90 | }; 91 | } 92 | -------------------------------------------------------------------------------- /modules/faasd-module.nix: -------------------------------------------------------------------------------- 1 | { config, lib, pkgs, ... }: 2 | 3 | let 4 | inherit (lib) mkOption mkIf mkMerge types concatMapStrings; 5 | inherit (types) package bool str attrsOf listOf enum submodule nullOr; 6 | 7 | cfg = config.services.faasd; 8 | 9 | boolToString = e: if e then "true" else "false"; 10 | 11 | service = import ./service.nix; 12 | 13 | dockerComposeAttrs = { 14 | version = "3.7"; 15 | services = lib.mapAttrs (k: c: c.out) cfg.containers; 16 | }; 17 | 18 | dockerComposeYaml = pkgs.writeText "docker-compose.yaml" (builtins.toJSON dockerComposeAttrs); 19 | 20 | seedOpts = { 21 | options = { 22 | namespace = mkOption { 23 | description = "Namespace to use when seeding image."; 24 | type = str; 25 | default = "openfaas"; 26 | }; 27 | 28 | imageFile = mkOption { 29 | description = "Path to the image file."; 30 | type = package; 31 | }; 32 | }; 33 | }; 34 | in 35 | { 36 | imports = import ./core-services; 37 | 38 | options.services.faasd = { 39 | enable = mkOption { 40 | type = bool; 41 | default = false; 42 | description = "Lightweight faas engine"; 43 | }; 44 | 45 | package = mkOption { 46 | description = "Faasd package to use."; 47 | type = package; 48 | default = pkgs.faasd; 49 | }; 50 | 51 | basicAuth = { 52 | enable = mkOption { 53 | description = "Enable basicAuth"; 54 | type = bool; 55 | default = true; 56 | }; 57 | user = mkOption { 58 | type = str; 59 | default = "admin"; 60 | description = "Basic-auth user"; 61 | }; 62 | passwordFile = mkOption { 63 | type = nullOr str; 64 | default = null; 65 | description = "Path to file containing password"; 66 | example = "/etc/nixos/faasd-basic-auth-password"; 67 | }; 68 | }; 69 | 70 | containers = mkOption { 71 | default = { }; 72 | type = attrsOf (submodule service); 73 | description = "OCI (Docker) containers to run as additional services on faasd."; 74 | }; 75 | 76 | namespaces = mkOption { 77 | type = types.listOf types.str; 78 | default = [ ]; 79 | description = '' 80 | Openfaas function namespaces. 81 | Namespaces listed here will be created of they do not exist and labeled 82 | with `openfaas=true`. 83 | ''; 84 | example = [ "dev" ]; 85 | }; 86 | 87 | seedCoreImages = mkOption { 88 | description = "Seed faasd core images"; 89 | type = bool; 90 | default = false; 91 | }; 92 | 93 | seedDockerImages = mkOption { 94 | description = "List of docker images to preload on system"; 95 | default = [ ]; 96 | type = listOf (submodule seedOpts); 97 | }; 98 | 99 | pullPolicy = mkOption { 100 | description = '' 101 | Set to "Always" to force a pull of images upon deployment, or "IfNotPresent" to try to use a cached image. 102 | ''; 103 | type = enum [ "Always" "IfNotPresent" ]; 104 | default = "Always"; 105 | }; 106 | 107 | nameserver = mkOption { 108 | description = "Nameserver to use"; 109 | type = str; 110 | default = "8.8.8.8"; 111 | }; 112 | }; 113 | 114 | config = mkMerge [ 115 | (mkIf cfg.enable { 116 | networking.firewall.trustedInterfaces = [ "openfaas0" ]; 117 | 118 | boot.kernel.sysctl = { 119 | "net.ipv4.conf.all.forwarding" = 1; 120 | }; 121 | 122 | virtualisation.containerd.enable = true; 123 | 124 | # Seed images for containers that have imageFile attribute 125 | services.faasd.seedDockerImages = lib.concatMap (image: [{ imageFile = image; }]) 126 | (lib.remove null (lib.catAttrs "imageFile" (lib.attrValues cfg.containers))); 127 | 128 | systemd.tmpfiles.rules = [ 129 | "d /opt/cni/bin 0755 root root -" 130 | "d /usr/local/bin 0755 root root -" 131 | "d '/var/lib/faasd'" 132 | "d '/var/lib/faasd-provider'" 133 | ]; 134 | 135 | systemd.services.faasd-init = { 136 | script = '' 137 | # Link cni-plugins 138 | ln -fs ${pkgs.cni-plugins}/bin/* /opt/cni/bin 139 | 140 | # Link faasd binary 141 | ln -fs "${cfg.package}/bin/faasd" "/usr/local/bin/faasd" 142 | 143 | # Set basic-auth user and password 144 | mkdir -p /var/lib/faasd/secrets 145 | ${if cfg.basicAuth.passwordFile != null then 146 | ''ln -fs ${cfg.basicAuth.passwordFile} /var/lib/faasd/secrets/basic-auth-password'' 147 | else 148 | '' 149 | if [ ! -e "/var/lib/faasd/secrets/basic-auth-password" ] ; then 150 | (head -c 12 /dev/urandom | ${pkgs.perl}/bin/shasum | cut -d' ' -f1) > /var/lib/faasd/secrets/basic-auth-password 151 | fi 152 | '' 153 | } 154 | echo ${cfg.basicAuth.user} > /var/lib/faasd/secrets/basic-auth-user 155 | 156 | ln -fs "${cfg.package}/installation/prometheus.yml" "/var/lib/faasd/prometheus.yml" 157 | echo "nameserver ${cfg.nameserver}" > "/var/lib/faasd/resolv.conf" 158 | ''; 159 | 160 | before = [ "faasd-provider.service" "faasd.service" ]; 161 | wantedBy = [ "multi-user.target" ]; 162 | 163 | serviceConfig = { 164 | Type = "oneshot"; 165 | }; 166 | }; 167 | 168 | systemd.services.faasd-provider = { 169 | description = "faasd-provider"; 170 | after = [ "network.service" "firewall.service" ]; 171 | wantedBy = [ "multi-user.target" ]; 172 | path = [ pkgs.iptables ]; 173 | 174 | serviceConfig = { 175 | MemoryLimit = "500M"; 176 | Restart = "on-failure"; 177 | RestartSec = "10s"; 178 | Environment = [ "basic_auth=${boolToString cfg.basicAuth.enable}" "secret_mount_path=/var/lib/faasd/secrets" ]; 179 | ExecStart = "${cfg.package}/bin/faasd provider --pull-policy ${cfg.pullPolicy}"; 180 | WorkingDirectory = "/var/lib/faasd-provider"; 181 | }; 182 | }; 183 | 184 | systemd.services.faasd = { 185 | description = "faasd"; 186 | after = [ "faasd-provider.service" ]; 187 | wantedBy = [ "multi-user.target" ]; 188 | path = [ pkgs.iptables ]; 189 | 190 | preStart = '' 191 | ln -fs "${dockerComposeYaml}" "/var/lib/faasd/docker-compose.yaml" 192 | ''; 193 | 194 | serviceConfig = { 195 | MemoryLimit = "500M"; 196 | ExecStart = "${cfg.package}/bin/faasd up"; 197 | Restart = "on-failure"; 198 | RestartSec = "10s"; 199 | WorkingDirectory = "/var/lib/faasd"; 200 | }; 201 | }; 202 | }) 203 | 204 | (mkIf (cfg.namespaces != [ ]) { 205 | systemd.services.faasd-create-namespaces = { 206 | description = "Create OpenFaaS namespaces"; 207 | script = '' 208 | ${concatMapStrings (namespace: '' 209 | echo "Creating namespace ${namespace}" 210 | ${pkgs.containerd}/bin/ctr namespace create ${namespace} || true 211 | ${pkgs.containerd}/bin/ctr namespace label ${namespace} openfaas=true 212 | '') cfg.namespaces} 213 | ''; 214 | 215 | before = [ "faasd.service" ]; 216 | wantedBy = [ "multi-user.target" ]; 217 | after = [ "containerd.service" ]; 218 | requires = [ "containerd.service" ]; 219 | 220 | serviceConfig = { 221 | Type = "oneshot"; 222 | }; 223 | }; 224 | }) 225 | 226 | (mkIf (cfg.seedDockerImages != [ ]) { 227 | systemd.services.faasd-seed-images = { 228 | description = "Seed faasd container images"; 229 | script = '' 230 | # Seed container images 231 | ${concatMapStrings (opts: '' 232 | echo "Seeding container image: ${opts.imageFile}" 233 | ${if (lib.hasSuffix "gz" opts.imageFile) then 234 | ''${pkgs.gzip}/bin/zcat "${opts.imageFile}" | ${pkgs.containerd}/bin/ctr -n ${opts.namespace} image import -'' 235 | else 236 | ''${pkgs.coreutils}/bin/cat "${opts.imageFile}" | ${pkgs.containerd}/bin/ctr -n ${opts.namespace} image import -'' 237 | } 238 | '') cfg.seedDockerImages} 239 | ''; 240 | 241 | before = [ "faasd.service" ]; 242 | wantedBy = [ "multi-user.target" ]; 243 | after = [ "containerd.service" ]; 244 | 245 | serviceConfig = { 246 | Type = "oneshot"; 247 | }; 248 | }; 249 | }) 250 | ]; 251 | } 252 | -------------------------------------------------------------------------------- /modules/service.nix: -------------------------------------------------------------------------------- 1 | { config, lib, pkgs, ... }: 2 | let 3 | inherit (lib) mkOption types optionalAttrs literalExpression; 4 | inherit (types) listOf nullOr attrsOf str either int bool submodule package; 5 | 6 | link = url: text: 7 | ''link: [${text}](${url})''; 8 | dockerComposeRef = fragment: 9 | ''See ${link "https://github.com/compose-spec/compose-spec/blob/master/spec.md?/#${fragment}" "Compose Specification#${fragment}"}''; 10 | in 11 | { 12 | options = { 13 | out = mkOption { 14 | type = attrsOf types.unspecified; 15 | readOnly = true; 16 | internal = true; 17 | }; 18 | 19 | image = mkOption { 20 | type = str; 21 | description = dockerComposeRef "image"; 22 | }; 23 | 24 | imageFile = mkOption { 25 | type = nullOr package; 26 | default = null; 27 | description = '' 28 | Path to an image file to load instead of pulling from a registry. 29 | If defined, do not pull from registry. 30 | You still need to set the image attribute, as it 31 | will be used as the image name for faasd to start a container. 32 | ''; 33 | example = literalExpression "pkgs.dockerTools.buildImage {...};"; 34 | }; 35 | 36 | depends_on = mkOption { 37 | type = listOf str; 38 | default = [ ]; 39 | description = dockerComposeRef "depends_on"; 40 | }; 41 | 42 | environment = mkOption { 43 | type = either (attrsOf (either str int)) (listOf str); 44 | default = { }; 45 | description = dockerComposeRef "environment"; 46 | }; 47 | 48 | volumes = mkOption { 49 | type = listOf types.unspecified; 50 | default = [ ]; 51 | description = dockerComposeRef "volumes"; 52 | }; 53 | 54 | ports = mkOption { 55 | type = listOf types.unspecified; 56 | default = [ ]; 57 | description = dockerComposeRef "ports"; 58 | }; 59 | 60 | user = mkOption { 61 | type = nullOr str; 62 | default = null; 63 | description = dockerComposeRef "user"; 64 | }; 65 | 66 | command = mkOption { 67 | type = nullOr types.unspecified; 68 | default = null; 69 | description = dockerComposeRef "command"; 70 | }; 71 | 72 | cap_add = mkOption { 73 | type = listOf str; 74 | default = [ ]; 75 | example = [ "CAP_NET_RAW" "SYS_ADMIN" ]; 76 | description = dockerComposeRef "cap_add"; 77 | }; 78 | 79 | entrypoint = mkOption { 80 | type = nullOr str; 81 | default = null; 82 | description = dockerComposeRef "entypoint"; 83 | }; 84 | }; 85 | 86 | config.out = { 87 | inherit (config) image; 88 | } // optionalAttrs (config.depends_on != [ ]) { 89 | inherit (config) depends_on; 90 | } // optionalAttrs (config.environment != [ ] || config.environment != { }) { 91 | inherit (config) environment; 92 | } // optionalAttrs (config.volumes != [ ]) { 93 | inherit (config) volumes; 94 | } // optionalAttrs (config.ports != [ ]) { 95 | inherit (config) ports; 96 | } // optionalAttrs (config.user != null) { 97 | inherit (config) user; 98 | } // optionalAttrs (config.command != null) { 99 | inherit (config) command; 100 | } // optionalAttrs (config.cap_add != [ ]) { 101 | inherit (config) cap_add; 102 | } // optionalAttrs (config.entrypoint != null) { 103 | inherit (config) entrypoint; 104 | }; 105 | } 106 | -------------------------------------------------------------------------------- /pkgs/options-doc.nix: -------------------------------------------------------------------------------- 1 | { lib, nixosOptionsDoc, runCommand, pkgs, ... }: 2 | let 3 | eval = lib.evalModules { 4 | modules = [ 5 | ../modules/faasd-module.nix 6 | ({ ... }: { 7 | _module.check = false; 8 | }) 9 | ]; 10 | specialArgs = { inherit pkgs; }; 11 | }; 12 | 13 | optionsDoc = nixosOptionsDoc { 14 | inherit (eval) options; 15 | }; 16 | in 17 | runCommand "options-doc.md" { } '' 18 | cat ${optionsDoc.optionsCommonMark} >> $out 19 | '' 20 | -------------------------------------------------------------------------------- /scripts/gen-options-doc.nix: -------------------------------------------------------------------------------- 1 | { writeShellScriptBin, pkgs, ... }: 2 | 3 | writeShellScriptBin "gen-options-doc" '' 4 | echo "Generating NixOS module options documentation" 5 | cat ${pkgs.faasd-options-doc} > OPTIONS.md 6 | '' 7 | 8 | -------------------------------------------------------------------------------- /scripts/prefetch-images.nix: -------------------------------------------------------------------------------- 1 | images: { writeShellScriptBin, pkgs, ... }: 2 | 3 | writeShellScriptBin "prefetch-images" '' 4 | echo "Fetching gateway image">&2 5 | gateway=`${pkgs.nix-prefetch-docker}/bin/nix-prefetch-docker --image-name ${images.gateway.name} --image-tag ${images.gateway.tag} | sed '2~1 s/^/ /'` 6 | 7 | echo "Fetching queue-worker image">&2 8 | queue_worker=`${pkgs.nix-prefetch-docker}/bin/nix-prefetch-docker --image-name ${images.queue-worker.name} --image-tag ${images.queue-worker.tag} | sed '2~1 s/^/ /'` 9 | 10 | echo "Fetching nats image">&2 11 | nats=`${pkgs.nix-prefetch-docker}/bin/nix-prefetch-docker --image-name ${images.nats.name} --image-tag ${images.nats.tag} | sed '2~1 s/^/ /'` 12 | 13 | echo "Fetching prometheus image">&2 14 | prometheus=`${pkgs.nix-prefetch-docker}/bin/nix-prefetch-docker --image-name ${images.prometheus.name} --image-tag ${images.prometheus.tag} | sed '2~1 s/^/ /'` 15 | 16 | echo "{ 17 | gateway = ''${gateway}; 18 | queue-worker = ''${queue_worker}; 19 | nats = ''${nats}; 20 | prometheus = ''${prometheus}; 21 | }" 22 | '' 23 | --------------------------------------------------------------------------------