open-webui: init
This commit is contained in:
parent
b46a227b5b
commit
02b73d2dce
7 changed files with 748 additions and 0 deletions
|
|
@ -3320,6 +3320,159 @@
|
|||
"services-nextcloudserver-usage-version": [
|
||||
"services-nextcloud.html#services-nextcloudserver-usage-version"
|
||||
],
|
||||
"services-open-webui": [
|
||||
"services-open-webui.html#services-open-webui"
|
||||
],
|
||||
"services-open-webui-features": [
|
||||
"services-open-webui.html#services-open-webui-features"
|
||||
],
|
||||
"services-open-webui-ollama": [
|
||||
"services-open-webui.html#services-open-webui-ollama"
|
||||
],
|
||||
"services-open-webui-options": [
|
||||
"services-open-webui.html#services-open-webui-options"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.backup": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.backup"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.backup.request": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.backup.request"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.backup.request.excludePatterns": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.backup.request.excludePatterns"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.backup.request.hooks": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.backup.request.hooks"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.backup.request.hooks.afterBackup": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.backup.request.hooks.afterBackup"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.backup.request.hooks.beforeBackup": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.backup.request.hooks.beforeBackup"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.backup.request.sourceDirectories": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.backup.request.sourceDirectories"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.backup.request.user": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.backup.request.user"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.backup.result": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.backup.result"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.backup.result.backupService": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.backup.result.backupService"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.backup.result.restoreScript": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.backup.result.restoreScript"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.domain": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.domain"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.enable": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.enable"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.environment": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.environment"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.ldap": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.ldap"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.ldap.adminGroup": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.ldap.adminGroup"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.ldap.userGroup": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.ldap.userGroup"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.port": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.port"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.ssl": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.ssl"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.ssl.paths": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.ssl.paths"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.ssl.paths.cert": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.ssl.paths.cert"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.ssl.paths.key": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.ssl.paths.key"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.ssl.systemdService": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.ssl.systemdService"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.sso": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.sso"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.sso.authEndpoint": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.sso.authEndpoint"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.sso.authorization_policy": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.sso.authorization_policy"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.sso.clientID": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.sso.clientID"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.sso.enable": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.sso.enable"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.sso.sharedSecret": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.sso.sharedSecret"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.sso.sharedSecret.request": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.sso.sharedSecret.request"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.sso.sharedSecret.request.group": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.sso.sharedSecret.request.group"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.sso.sharedSecret.request.mode": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.sso.sharedSecret.request.mode"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.sso.sharedSecret.request.owner": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.sso.sharedSecret.request.owner"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.sso.sharedSecret.request.restartUnits": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.sso.sharedSecret.request.restartUnits"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.sso.sharedSecret.result": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.sso.sharedSecret.result"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.sso.sharedSecret.result.path": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.sso.sharedSecret.result.path"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.sso.sharedSecretForAuthelia": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.sso.sharedSecretForAuthelia"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.sso.sharedSecretForAuthelia.request": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.sso.sharedSecretForAuthelia.request"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.sso.sharedSecretForAuthelia.request.group": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.sso.sharedSecretForAuthelia.request.group"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.sso.sharedSecretForAuthelia.request.mode": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.sso.sharedSecretForAuthelia.request.mode"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.sso.sharedSecretForAuthelia.request.owner": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.sso.sharedSecretForAuthelia.request.owner"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.sso.sharedSecretForAuthelia.request.restartUnits": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.sso.sharedSecretForAuthelia.request.restartUnits"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.sso.sharedSecretForAuthelia.result": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.sso.sharedSecretForAuthelia.result"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.sso.sharedSecretForAuthelia.result.path": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.sso.sharedSecretForAuthelia.result.path"
|
||||
],
|
||||
"services-open-webui-options-shb.open-webui.subdomain": [
|
||||
"services-open-webui.html#services-open-webui-options-shb.open-webui.subdomain"
|
||||
],
|
||||
"services-open-webui-usage": [
|
||||
"services-open-webui.html#services-open-webui-usage"
|
||||
],
|
||||
"services-open-webui-usage-backup": [
|
||||
"services-open-webui.html#services-open-webui-usage-backup"
|
||||
],
|
||||
"services-pinchflat": [
|
||||
"services-pinchflat.html#services-pinchflat"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ information is provided in the respective manual sections.
|
|||
| [Jellyfin][] | Y (1) | Y | Y | Y | Y (2) | N |
|
||||
| [Home-Assistant][] | Y (1) | Y | N | Y | Y (2) | N |
|
||||
| [Nextcloud Server][] | Y (1) | Y | Y | Y | Y (2) | P (3) |
|
||||
| [Open WebUI][] | Y (1) | Y | Y | Y | Y (2) | N |
|
||||
| [Pinchflat][] | Y | Y | Y | Y (4) | Y (5) | N |
|
||||
| [Vaultwarden][] | Y (1) | Y | Y | Y | Y (2) | N |
|
||||
|
||||
|
|
@ -35,6 +36,7 @@ Legend: **N**: no but WIP; **P**: partial; **Y**: yes
|
|||
[Home-Assistant]: services-home-assistant.html
|
||||
[Jellyfin]: services-jellyfin.html
|
||||
[Nextcloud Server]: services-nextcloud.html
|
||||
[Open WebUI]: services-open-webui.html
|
||||
[Pinchflat]: services-pinchflat.html
|
||||
[Vaultwarden]: services-vaultwarden.html
|
||||
|
||||
|
|
@ -62,6 +64,10 @@ modules/services/nextcloud-server/docs/default.md
|
|||
modules/services/pinchflat/docs/default.md
|
||||
```
|
||||
|
||||
```{=include=} chapters html:into-file=//services-open-webui.html
|
||||
modules/services/open-webui/docs/default.md
|
||||
```
|
||||
|
||||
```{=include=} chapters html:into-file=//services-vaultwarden.html
|
||||
modules/services/vaultwarden/docs/default.md
|
||||
```
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
});
|
||||
pkgs = import patchedNixpkgs {
|
||||
inherit system;
|
||||
config.allowUnfree = true;
|
||||
};
|
||||
|
||||
# The contract dummies are used to show options for contracts.
|
||||
|
|
@ -82,6 +83,7 @@
|
|||
self.nixosModules.${system}.home-assistant
|
||||
self.nixosModules.${system}.jellyfin
|
||||
self.nixosModules.${system}.nextcloud-server
|
||||
self.nixosModules.${system}.open-webui
|
||||
self.nixosModules.${system}.pinchflat
|
||||
self.nixosModules.${system}.vaultwarden
|
||||
];
|
||||
|
|
@ -117,6 +119,7 @@
|
|||
nixosModules.home-assistant = modules/services/home-assistant.nix;
|
||||
nixosModules.jellyfin = modules/services/jellyfin.nix;
|
||||
nixosModules.nextcloud-server = modules/services/nextcloud-server.nix;
|
||||
nixosModules.open-webui = modules/services/open-webui.nix;
|
||||
nixosModules.pinchflat = modules/services/pinchflat.nix;
|
||||
nixosModules.vaultwarden = modules/services/vaultwarden.nix;
|
||||
|
||||
|
|
@ -146,6 +149,7 @@
|
|||
"services/home-assistant" = ./modules/services/home-assistant.nix;
|
||||
"services/jellyfin" = ./modules/services/jellyfin.nix;
|
||||
"services/nextcloud-server" = ./modules/services/nextcloud-server.nix;
|
||||
"services/open-webui" = ./modules/services/open-webui.nix;
|
||||
"services/pinchflat" = ./modules/services/pinchflat.nix;
|
||||
"services/vaultwarden" = ./modules/services/vaultwarden.nix;
|
||||
"contracts/backup" = ./modules/contracts/backup/dummyModule.nix;
|
||||
|
|
@ -263,6 +267,7 @@
|
|||
// (vm_test "jellyfin" ./test/services/jellyfin.nix)
|
||||
// (vm_test "monitoring" ./test/services/monitoring.nix)
|
||||
// (vm_test "nextcloud" ./test/services/nextcloud.nix)
|
||||
// (vm_test "open-webui" ./test/services/open-webui.nix)
|
||||
// (vm_test "pinchflat" ./test/services/pinchflat.nix)
|
||||
// (vm_test "vaultwarden" ./test/services/vaultwarden.nix)
|
||||
|
||||
|
|
|
|||
244
modules/services/open-webui.nix
Normal file
244
modules/services/open-webui.nix
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.shb.open-webui;
|
||||
|
||||
contracts = pkgs.callPackage ../contracts {};
|
||||
shblib = pkgs.callPackage ../../lib {};
|
||||
in
|
||||
{
|
||||
options.shb.open-webui = {
|
||||
enable = lib.mkEnableOption "the Open-WebUI service";
|
||||
|
||||
subdomain = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Subdomain under which Karakeep will be served.";
|
||||
default = "pinchflat";
|
||||
};
|
||||
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "domain under which Karakeep will be served.";
|
||||
example = "mydomain.com";
|
||||
};
|
||||
|
||||
ssl = lib.mkOption {
|
||||
description = "Path to SSL files";
|
||||
type = lib.types.nullOr contracts.ssl.certs;
|
||||
default = null;
|
||||
};
|
||||
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
description = "Port Karakeep listens to incoming requests.";
|
||||
default = 12444;
|
||||
};
|
||||
|
||||
environment = lib.mkOption {
|
||||
type = lib.types.attrsOf lib.types.str;
|
||||
description = "Extra environment variables. See https://docs.openwebui.com/getting-started/env-configuration";
|
||||
example = ''
|
||||
{
|
||||
WEBUI_NAME = "SelfHostBlocks";
|
||||
|
||||
OLLAMA_BASE_URL = "http://127.0.0.1:''${toString config.services.ollama.port}";
|
||||
RAG_EMBEDDING_MODEL = "nomic-embed-text:v1.5";
|
||||
|
||||
ENABLE_OPENAI_API = "True";
|
||||
OPENAI_API_BASE_URL = "http://127.0.0.1:''${toString config.services.llama-cpp.port}";
|
||||
ENABLE_WEB_SEARCH = "True";
|
||||
RAG_EMBEDDING_ENGINE = "openai";
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
ldap = lib.mkOption {
|
||||
description = ''
|
||||
Setup LDAP integration.
|
||||
'';
|
||||
default = {};
|
||||
type = lib.types.submodule {
|
||||
options = {
|
||||
userGroup = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Group users must belong to to be able to login.";
|
||||
default = "open-webui_user";
|
||||
};
|
||||
|
||||
adminGroup = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Group users must belong to to have administrator privileges.";
|
||||
default = "open-webui_admin";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
sso = lib.mkOption {
|
||||
description = ''
|
||||
Setup SSO integration.
|
||||
'';
|
||||
default = {};
|
||||
type = lib.types.submodule {
|
||||
options = {
|
||||
enable = lib.mkEnableOption "SSO integration.";
|
||||
|
||||
authEndpoint = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
description = "Endpoint to the SSO provider. Leave null to not have SSO configured.";
|
||||
example = "https://authelia.example.com";
|
||||
};
|
||||
|
||||
clientID = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Client ID for the OIDC endpoint.";
|
||||
default = "open-webui";
|
||||
};
|
||||
|
||||
authorization_policy = lib.mkOption {
|
||||
type = lib.types.enum [ "one_factor" "two_factor" ];
|
||||
description = "Require one factor (password) or two factor (device) authentication.";
|
||||
default = "one_factor";
|
||||
};
|
||||
|
||||
sharedSecret = lib.mkOption {
|
||||
description = "OIDC shared secret for Audiobookshelf.";
|
||||
type = lib.types.submodule {
|
||||
options = contracts.secret.mkRequester {
|
||||
owner = "open-webui";
|
||||
restartUnits = [ "open-webui.service" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
sharedSecretForAuthelia = lib.mkOption {
|
||||
description = "OIDC shared secret for Authelia. Must be the same as `sharedSecret`";
|
||||
type = lib.types.submodule {
|
||||
options = contracts.secret.mkRequester {
|
||||
mode = "0400";
|
||||
ownerText = "config.shb.authelia.autheliaUser";
|
||||
owner = config.shb.authelia.autheliaUser;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
backup = lib.mkOption {
|
||||
description = ''
|
||||
Backup state directory.
|
||||
'';
|
||||
default = {};
|
||||
type = lib.types.submodule {
|
||||
options = contracts.backup.mkRequester {
|
||||
user = "open-webui";
|
||||
sourceDirectories = [
|
||||
config.services.open-webui.stateDir
|
||||
];
|
||||
sourceDirectoriesText = "[ config.services.open-webui.stateDir ]";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = (lib.mkMerge [
|
||||
(lib.mkIf cfg.enable {
|
||||
users.users.open-webui = {
|
||||
isSystemUser = true;
|
||||
group = "open-webui";
|
||||
};
|
||||
users.groups.open-webui = {};
|
||||
|
||||
services.open-webui = {
|
||||
enable = true;
|
||||
|
||||
host = "127.0.0.1";
|
||||
inherit (cfg) port;
|
||||
|
||||
environment = {
|
||||
WEBUI_URL = "https://${cfg.subdomain}.${cfg.domain}";
|
||||
|
||||
ENABLE_PERSISTENT_CONFIG = "False";
|
||||
|
||||
ANONYMIZED_TELEMETRY = "False";
|
||||
DO_NOT_TRACK = "True";
|
||||
SCARF_NO_ANALYTICS = "True";
|
||||
|
||||
ENABLE_VERSION_UPDATE_CHECK = "False";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.open-webui.path = [
|
||||
pkgs.ffmpeg-headless
|
||||
];
|
||||
|
||||
shb.nginx.vhosts = [
|
||||
{
|
||||
inherit (cfg) subdomain domain ssl;
|
||||
upstream = "http://127.0.0.1:${toString cfg.port}/";
|
||||
extraConfig = ''
|
||||
proxy_read_timeout 300s;
|
||||
proxy_send_timeout 300s;
|
||||
'';
|
||||
}
|
||||
];
|
||||
})
|
||||
(lib.mkIf (cfg.enable && cfg.sso.enable) {
|
||||
shb.lldap.ensureGroups = {
|
||||
${cfg.ldap.userGroup} = {};
|
||||
${cfg.ldap.adminGroup} = {};
|
||||
};
|
||||
|
||||
shb.authelia.oidcClients = [
|
||||
{
|
||||
client_id = cfg.sso.clientID;
|
||||
client_secret.source = cfg.sso.sharedSecretForAuthelia.result.path;
|
||||
scopes = [ "openid" "email" "profile" ];
|
||||
redirect_uris = [
|
||||
"https://${cfg.subdomain}.${cfg.domain}/oauth/oidc/callback"
|
||||
];
|
||||
}
|
||||
];
|
||||
services.open-webui = {
|
||||
package = pkgs.open-webui.overrideAttrs (finalAttrs: {
|
||||
patches = [
|
||||
../../patches/0001-selfhostblocks-never-onboard.patch
|
||||
];
|
||||
});
|
||||
environment = {
|
||||
ENABLE_SIGNUP = "False";
|
||||
WEBUI_AUTH = "True";
|
||||
ENABLE_FORWARD_USER_INFO_HEADERS = "True";
|
||||
ENABLE_OAUTH_SIGNUP = "True";
|
||||
OAUTH_UPDATE_PICTURE_ON_LOGIN = "True";
|
||||
OAUTH_CLIENT_ID = cfg.sso.clientID;
|
||||
OPENID_PROVIDER_URL = "${cfg.sso.authEndpoint}/.well-known/openid-configuration";
|
||||
OAUTH_PROVIDER_NAME = "Single Sign-On";
|
||||
OAUTH_SCOPES = "openid email profile";
|
||||
OAUTH_ALLOWED_ROLES = cfg.ldap.userGroup;
|
||||
OAUTH_ADMIN_ROLES = cfg.ldap.adminGroup;
|
||||
ENABLE_OAUTH_ROLE_MANAGEMENT = "True";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.open-webui.serviceConfig.EnvironmentFile = "/run/open-webui/secrets.env";
|
||||
systemd.tmpfiles.rules = [
|
||||
"d '/run/open-webui' 0750 root root - -"
|
||||
];
|
||||
systemd.services.open-webui-pre = {
|
||||
script = shblib.replaceSecrets {
|
||||
userConfig = {
|
||||
OAUTH_CLIENT_SECRET.source = cfg.sso.sharedSecret.result.path;
|
||||
};
|
||||
resultPath = "/run/open-webui/secrets.env";
|
||||
generator = shblib.toEnvVar;
|
||||
};
|
||||
serviceConfig.Type = "oneshot";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
before = [ "open-webui.service" ];
|
||||
requiredBy = [ "open-webui.service" ];
|
||||
};
|
||||
})
|
||||
]);
|
||||
}
|
||||
121
modules/services/open-webui/docs/default.md
Normal file
121
modules/services/open-webui/docs/default.md
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
# Open-WebUI Service {#services-open-webui}
|
||||
|
||||
Defined in [`/modules/blocks/open-webui.nix`](@REPO@/modules/blocks/open-webui.nix),
|
||||
found in the `selfhostblocks.nixosModules.open-webui` module.
|
||||
See [the manual](usage.html#usage-flake) for how to import the module in your code.
|
||||
|
||||
This service sets up [Open WebUI][] which provides a frontend to various LLMs.
|
||||
|
||||
[Open WebUI]: https://docs.openwebui.com/
|
||||
|
||||
## Features {#services-open-webui-features}
|
||||
|
||||
- Telemetry disabled.
|
||||
- Declarative [LDAP](#services-open-webui-options-shb.open-webui.ldap) Configuration.
|
||||
Needed LDAP groups are created automatically.
|
||||
- Declarative [SSO](#services-open-webui-options-shb.open-webui.sso) Configuration.
|
||||
When SSO is enabled, login with user and password is disabled.
|
||||
Registration is enabled through SSO.
|
||||
- Access through [subdomain](#services-open-webui-options-shb.open-webui.subdomain) using reverse proxy.
|
||||
- Access through [HTTPS](#services-open-webui-options-shb.open-webui.ssl) using reverse proxy.
|
||||
- [Backup](#services-open-webui-options-shb.open-webui.sso) through the [backup block](./blocks-backup.html).
|
||||
|
||||
## Usage {#services-open-webui-usage}
|
||||
|
||||
The following snippet assumes a few blocks have been setup already:
|
||||
|
||||
- the [secrets block](usage.html#usage-secrets) with SOPS,
|
||||
- the [`shb.ssl` block](blocks-ssl.html#usage),
|
||||
- the [`shb.lldap` block](blocks-lldap.html#blocks-lldap-global-setup).
|
||||
- the [`shb.authelia` block](blocks-authelia.html#blocks-sso-global-setup).
|
||||
|
||||
```nix
|
||||
{
|
||||
shb.open-webui = {
|
||||
enable = true;
|
||||
domain = "example.com";
|
||||
subdomain = "open-webui";
|
||||
|
||||
ssl = config.shb.certs.certs.letsencrypt.${domain};
|
||||
|
||||
sso = {
|
||||
enable = true;
|
||||
authEndpoint = "https://${config.shb.authelia.subdomain}.${config.shb.authelia.domain}";
|
||||
|
||||
sharedSecret.result = config.shb.sops.secret.oidcSecret.result;
|
||||
sharedSecretForAuthelia.result = config.shb.sops.secret.oidcAutheliaSecret.result;
|
||||
};
|
||||
};
|
||||
|
||||
shb.sops.secret.oidcSecret.request = config.shb.open-webui.sso.sharedSecret.request;
|
||||
shb.sops.secret.oidcAutheliaSecret.request = config.shb.open-webui.sso.sharedSecretForAuthelia.request;
|
||||
}
|
||||
```
|
||||
|
||||
Secrets can be randomly generated with `nix run nixpkgs#openssl -- rand -hex 64`.
|
||||
|
||||
The [user](#services-open-webui-options-shb.open-webui.ldap.userGroup)
|
||||
and [admin](#services-open-webui-options-shb.open-webui.ldap.adminGroup)
|
||||
LDAP groups are created automatically.
|
||||
|
||||
## Integration with OLLAMA {#services-open-webui-ollama}
|
||||
|
||||
Assuming ollama is enabled, it will be available on port `config.services.ollama.port`.
|
||||
The following snippet sets up acceleration using an AMD (i)GPU and loads some models.
|
||||
|
||||
```nix
|
||||
{
|
||||
services.ollama = {
|
||||
enable = true;
|
||||
|
||||
# https://wiki.nixos.org/wiki/Ollama#AMD_GPU_with_open_source_driver
|
||||
acceleration = "rocm";
|
||||
|
||||
# https://ollama.com/library
|
||||
loadModels = [
|
||||
"deepseek-r1:1.5b"
|
||||
"llama3.2:3b"
|
||||
"llava:7b"
|
||||
"mxbai-embed-large:335m"
|
||||
"nomic-embed-text:v1.5"
|
||||
];
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Integrating with the ollama service is done with:
|
||||
|
||||
```nix
|
||||
{
|
||||
services.open-webui = {
|
||||
environment.OLLAMA_BASE_URL = "http://127.0.0.1:${toString config.services.ollama.port}";
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Notice we're using the upstream service here `services.open-webui`, not `shb.open-webui`.
|
||||
|
||||
## Backup {#services-open-webui-usage-backup}
|
||||
|
||||
Backing up Open-Webui using the [Restic block](blocks-restic.html) is done like so:
|
||||
|
||||
```nix
|
||||
shb.restic.instances."open-webui" = {
|
||||
request = config.shb.open-webui.backup;
|
||||
settings = {
|
||||
enable = true;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
The name `"open-webui"` in the `instances` can be anything.
|
||||
The `config.shb.open-webui.backup` option provides what directories to backup.
|
||||
You can define any number of Restic instances to backup Open WebUI multiple times.
|
||||
|
||||
## Options Reference {#services-open-webui-options}
|
||||
|
||||
```{=include=} options
|
||||
id-prefix: services-open-webui-options-
|
||||
list-id: selfhostblocks-services-open-webui-options
|
||||
source: @OPTIONS_JSON@
|
||||
```
|
||||
29
patches/0001-selfhostblocks-never-onboard.patch
Normal file
29
patches/0001-selfhostblocks-never-onboard.patch
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
From 6897dd86a41b336c7c03a466990f7e981c5c649c Mon Sep 17 00:00:00 2001
|
||||
From: ibizaman <ibizaman@tiserbox.com>
|
||||
Date: Tue, 23 Sep 2025 11:36:24 +0200
|
||||
Subject: [PATCH] selfhostblocks: never onboard
|
||||
|
||||
---
|
||||
backend/open_webui/main.py | 4 +---
|
||||
1 file changed, 1 insertion(+), 3 deletions(-)
|
||||
|
||||
diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py
|
||||
index 5630a5883..5c7c88a64 100644
|
||||
--- a/backend/open_webui/main.py
|
||||
+++ b/backend/open_webui/main.py
|
||||
@@ -1654,11 +1654,9 @@ async def get_app_config(request: Request):
|
||||
user = Users.get_user_by_id(data["id"])
|
||||
|
||||
user_count = Users.get_num_users()
|
||||
+ # Never onboard
|
||||
onboarding = False
|
||||
|
||||
- if user is None:
|
||||
- onboarding = user_count == 0
|
||||
-
|
||||
return {
|
||||
**({"onboarding": True} if onboarding else {}),
|
||||
"status": True,
|
||||
--
|
||||
2.50.1
|
||||
|
||||
190
test/services/open-webui.nix
Normal file
190
test/services/open-webui.nix
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
{ pkgs, ... }:
|
||||
let
|
||||
oidcSecret = "oidcSecret";
|
||||
|
||||
testLib = pkgs.callPackage ../common.nix {};
|
||||
|
||||
commonTestScript = testLib.mkScripts {
|
||||
hasSSL = { node, ... }: !(isNull node.config.shb.open-webui.ssl);
|
||||
waitForServices = { ... }: [
|
||||
"open-webui.service"
|
||||
"nginx.service"
|
||||
];
|
||||
waitForPorts = { node, ... }: [
|
||||
node.config.shb.open-webui.port
|
||||
];
|
||||
};
|
||||
|
||||
basic = { config, ... }: {
|
||||
imports = [
|
||||
testLib.baseModule
|
||||
../../modules/blocks/hardcodedsecret.nix
|
||||
../../modules/blocks/lldap.nix
|
||||
../../modules/services/open-webui.nix
|
||||
];
|
||||
|
||||
test = {
|
||||
subdomain = "o";
|
||||
};
|
||||
|
||||
shb.open-webui = {
|
||||
enable = true;
|
||||
inherit (config.test) subdomain domain;
|
||||
};
|
||||
|
||||
networking.hosts = {
|
||||
"127.0.0.1" = [ "${config.test.subdomain}.${config.test.domain}" ];
|
||||
};
|
||||
};
|
||||
|
||||
https = { config, ... }: {
|
||||
shb.open-webui = {
|
||||
ssl = config.shb.certs.certs.selfsigned.n;
|
||||
};
|
||||
|
||||
systemd.services.open-webui.environment = {
|
||||
# Needed for open-webui to be able to talk to auth server.
|
||||
SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt";
|
||||
};
|
||||
};
|
||||
|
||||
ldap = { config, ... }: {
|
||||
shb.open-webui = {
|
||||
ldap = {
|
||||
userGroup = "user_group";
|
||||
adminGroup = "admin_group";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
clientLoginSso = { config, ... }: {
|
||||
imports = [
|
||||
testLib.baseModule
|
||||
testLib.clientLoginModule
|
||||
];
|
||||
virtualisation.memorySize = 4096;
|
||||
test = {
|
||||
subdomain = "o";
|
||||
};
|
||||
|
||||
test.login = {
|
||||
startUrl = "https://${config.test.fqdn}/auth";
|
||||
beforeHook = ''
|
||||
page.get_by_role("button", name="continue").click()
|
||||
'';
|
||||
usernameFieldLabelRegex = "Username";
|
||||
passwordFieldLabelRegex = "Password";
|
||||
loginButtonNameRegex = "[sS]ign [iI]n";
|
||||
testLoginWith = [
|
||||
{ username = "alice"; password = "NotAlicePassword"; nextPageExpect = [
|
||||
"expect(page.get_by_text(re.compile('[Ii]ncorrect'))).to_be_visible()"
|
||||
]; }
|
||||
{ username = "alice"; password = "AlicePassword"; nextPageExpect = [
|
||||
"page.get_by_role('button', name=re.compile('Accept')).click()"
|
||||
"expect(page.get_by_text(re.compile('[Ii]ncorrect'))).not_to_be_visible()"
|
||||
"expect(page.get_by_role('button', name=re.compile('Sign In'))).not_to_be_visible()"
|
||||
"expect(page.get_by_text('logged in')).to_be_visible()"
|
||||
]; }
|
||||
{ username = "bob"; password = "NotBobPassword"; nextPageExpect = [
|
||||
"expect(page.get_by_text(re.compile('[Ii]ncorrect'))).to_be_visible()"
|
||||
]; }
|
||||
{ username = "bob"; password = "BobPassword"; nextPageExpect = [
|
||||
"page.get_by_role('button', name=re.compile('Accept')).click()"
|
||||
"expect(page.get_by_text(re.compile('[Ii]ncorrect'))).not_to_be_visible()"
|
||||
"expect(page.get_by_role('button', name=re.compile('Sign In'))).not_to_be_visible()"
|
||||
"expect(page.get_by_text('logged in')).to_be_visible()"
|
||||
]; }
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
sso = { config, ... }: {
|
||||
shb.open-webui = {
|
||||
sso = {
|
||||
enable = true;
|
||||
authEndpoint = "https://${config.shb.authelia.subdomain}.${config.shb.authelia.domain}";
|
||||
clientID = "open-webui";
|
||||
|
||||
sharedSecret.result = config.shb.hardcodedsecret.oidcSecret.result;
|
||||
sharedSecretForAuthelia.result = config.shb.hardcodedsecret.oidcAutheliaSecret.result;
|
||||
};
|
||||
};
|
||||
|
||||
shb.hardcodedsecret.oidcSecret = {
|
||||
request = config.shb.open-webui.sso.sharedSecret.request;
|
||||
settings.content = oidcSecret;
|
||||
};
|
||||
shb.hardcodedsecret.oidcAutheliaSecret = {
|
||||
request = config.shb.open-webui.sso.sharedSecretForAuthelia.request;
|
||||
settings.content = oidcSecret;
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
basic = pkgs.testers.runNixOSTest {
|
||||
name = "open-webui_basic";
|
||||
|
||||
nodes.client = {};
|
||||
nodes.server = {
|
||||
imports = [
|
||||
basic
|
||||
];
|
||||
};
|
||||
|
||||
testScript = commonTestScript.access;
|
||||
};
|
||||
|
||||
backup = pkgs.testers.runNixOSTest {
|
||||
name = "open-webui_backup";
|
||||
|
||||
nodes.server = { config, ... }: {
|
||||
imports = [
|
||||
basic
|
||||
(testLib.backup config.shb.open-webui.backup)
|
||||
];
|
||||
};
|
||||
|
||||
nodes.client = {};
|
||||
|
||||
testScript = commonTestScript.backup;
|
||||
};
|
||||
|
||||
https = pkgs.testers.runNixOSTest {
|
||||
name = "open-webui_https";
|
||||
|
||||
nodes.client = {};
|
||||
nodes.server = {
|
||||
imports = [
|
||||
basic
|
||||
testLib.certs
|
||||
https
|
||||
];
|
||||
};
|
||||
|
||||
testScript = commonTestScript.access;
|
||||
};
|
||||
|
||||
sso = pkgs.testers.runNixOSTest {
|
||||
name = "open-webui_sso";
|
||||
interactive.sshBackdoor.enable = true;
|
||||
|
||||
nodes.client = {
|
||||
imports = [
|
||||
clientLoginSso
|
||||
];
|
||||
};
|
||||
nodes.server = { config, pkgs, ... }: {
|
||||
imports = [
|
||||
basic
|
||||
testLib.certs
|
||||
https
|
||||
testLib.ldap
|
||||
ldap
|
||||
(testLib.sso config.shb.certs.certs.selfsigned.n)
|
||||
sso
|
||||
];
|
||||
};
|
||||
|
||||
testScript = commonTestScript.access;
|
||||
};
|
||||
}
|
||||
Loading…
Reference in a new issue