···
1
+
{ lib, config, pkgs, options, ... }:
3
+
cfg = config.services.invidious;
4
+
# To allow injecting secrets with jq, json (instead of yaml) is used
5
+
settingsFormat = pkgs.formats.json { };
8
+
settingsFile = settingsFormat.generate "invidious-settings" cfg.settings;
11
+
systemd.services.invidious = {
12
+
description = "Invidious (An alternative YouTube front-end)";
13
+
wants = [ "network-online.target" ];
14
+
after = [ "syslog.target" "network-online.target" ];
15
+
wantedBy = [ "multi-user.target" ];
20
+
+ lib.optionalString (cfg.database.host != null) "[0].db.password = \"'\"'\"$(cat ${lib.escapeShellArg cfg.database.passwordFile})\"'\"'\""
22
+
+ lib.optionalString (cfg.extraSettingsFile != null) " * .[1]";
23
+
jqFiles = [ settingsFile ] ++ lib.optional (cfg.extraSettingsFile != null) cfg.extraSettingsFile;
26
+
export INVIDIOUS_CONFIG="$(${pkgs.jq}/bin/jq -s "${jqFilter}" ${lib.escapeShellArgs jqFiles})"
27
+
exec ${cfg.package}/bin/invidious
34
+
CapabilityBoundingSet = "";
35
+
PrivateDevices = true;
36
+
PrivateUsers = true;
38
+
ProtectKernelLogs = true;
39
+
ProtectProc = "invisible";
40
+
RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
41
+
RestrictNamespaces = true;
42
+
SystemCallArchitectures = "native";
43
+
SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ];
47
+
services.invidious.settings = {
50
+
# Automatically initialises and migrates the database if necessary
51
+
check_tables = true;
54
+
user = lib.mkDefault "kemal";
55
+
dbname = lib.mkDefault "invidious";
56
+
port = cfg.database.port;
57
+
# Blank for unix sockets, see
58
+
# https://github.com/will/crystal-pg/blob/1548bb255210/src/pq/conninfo.cr#L100-L108
59
+
host = if cfg.database.host == null then "" else cfg.database.host;
60
+
# Not needed because peer authentication is enabled
61
+
password = lib.mkIf (cfg.database.host == null) "";
63
+
} // (lib.optionalAttrs (cfg.domain != null) {
64
+
inherit (cfg) domain;
68
+
assertion = cfg.database.host != null -> cfg.database.passwordFile != null;
69
+
message = "If database host isn't null, database password needs to be set";
73
+
# Settings necessary for running with an automatically managed local database
74
+
localDatabaseConfig = lib.mkIf cfg.database.createLocally {
75
+
# Default to using the local database if we create it
76
+
services.invidious.database.host = lib.mkDefault null;
78
+
services.postgresql = {
80
+
ensureDatabases = lib.singleton cfg.settings.db.dbname;
81
+
ensureUsers = lib.singleton {
82
+
name = cfg.settings.db.user;
83
+
ensurePermissions = {
84
+
"DATABASE ${cfg.settings.db.dbname}" = "ALL PRIVILEGES";
87
+
# This is only needed because the unix user invidious isn't the same as
88
+
# the database user. This tells postgres to map one to the other.
90
+
invidious invidious ${cfg.settings.db.user}
92
+
# And this specifically enables peer authentication for only this
93
+
# database, which allows passwordless authentication over the postgres
94
+
# unix socket for the user map given above.
96
+
local ${cfg.settings.db.dbname} ${cfg.settings.db.user} peer map=invidious
100
+
systemd.services.invidious-db-clean = {
101
+
description = "Invidious database cleanup";
102
+
documentation = [ "https://docs.invidious.io/Database-Information-and-Maintenance.md" ];
103
+
startAt = lib.mkDefault "weekly";
104
+
path = [ config.services.postgresql.package ];
106
+
psql ${cfg.settings.db.dbname} ${cfg.settings.db.user} -c "DELETE FROM nonces * WHERE expire < current_timestamp"
107
+
psql ${cfg.settings.db.dbname} ${cfg.settings.db.user} -c "TRUNCATE TABLE videos"
110
+
DynamicUser = true;
111
+
User = "invidious";
115
+
systemd.services.invidious = {
116
+
requires = [ "postgresql.service" ];
117
+
after = [ "postgresql.service" ];
120
+
User = "invidious";
125
+
nginxConfig = lib.mkIf cfg.nginx.enable {
126
+
services.invidious.settings = {
127
+
https_only = config.services.nginx.virtualHosts.${cfg.domain}.forceSSL;
128
+
external_port = 80;
133
+
virtualHosts.${cfg.domain} = {
134
+
locations."/".proxyPass = "http://127.0.0.1:${toString cfg.port}";
136
+
enableACME = lib.mkDefault true;
137
+
forceSSL = lib.mkDefault true;
142
+
assertion = cfg.domain != null;
143
+
message = "To use services.invidious.nginx, you need to set services.invidious.domain";
148
+
options.services.invidious = {
149
+
enable = lib.mkEnableOption "Invidious";
151
+
package = lib.mkOption {
152
+
type = types.package;
153
+
default = pkgs.invidious;
154
+
defaultText = "pkgs.invidious";
155
+
description = "The Invidious package to use.";
158
+
settings = lib.mkOption {
159
+
type = settingsFormat.type;
162
+
The settings Invidious should use.
164
+
See <link xlink:href="https://github.com/iv-org/invidious/blob/master/config/config.example.yml">config.example.yml</link> for a list of all possible options.
168
+
extraSettingsFile = lib.mkOption {
169
+
type = types.nullOr types.str;
172
+
A file including Invidious settings.
174
+
It gets merged with the setttings specified in <option>services.invidious.settings</option>
175
+
and can be used to store secrets like <literal>hmac_key</literal> outside of the nix store.
179
+
# This needs to be outside of settings to avoid infinite recursion
180
+
# (determining if nginx should be enabled and therefore the settings
182
+
domain = lib.mkOption {
183
+
type = types.nullOr types.str;
186
+
The FQDN Invidious is reachable on.
188
+
This is used to configure nginx and for building absolute URLs.
192
+
port = lib.mkOption {
194
+
# Default from https://docs.invidious.io/Configuration.md
197
+
The port Invidious should listen on.
199
+
To allow access from outside,
200
+
you can use either <option>services.invidious.nginx</option>
201
+
or add <literal>config.services.invidious.port</literal> to <option>networking.firewall.allowedTCPPorts</option>.
206
+
createLocally = lib.mkOption {
210
+
Whether to create a local database with PostgreSQL.
214
+
host = lib.mkOption {
215
+
type = types.nullOr types.str;
218
+
The database host Invidious should use.
220
+
If <literal>null</literal>, the local unix socket is used. Otherwise
225
+
port = lib.mkOption {
227
+
default = options.services.postgresql.port.default;
229
+
The port of the database Invidious should use.
231
+
Defaults to the the default postgresql port.
235
+
passwordFile = lib.mkOption {
236
+
type = types.nullOr types.str;
237
+
apply = lib.mapNullable toString;
240
+
Path to file containing the database password.
245
+
nginx.enable = lib.mkOption {
249
+
Whether to configure nginx as a reverse proxy for Invidious.
251
+
It serves it under the domain specified in <option>services.invidious.settings.domain</option> with enabled TLS and ACME.
252
+
Further configuration can be done through <option>services.nginx.virtualHosts.''${config.services.invidious.settings.domain}.*</option>,
253
+
which can also be used to disable AMCE and TLS.
258
+
config = lib.mkIf cfg.enable (lib.mkMerge [
260
+
localDatabaseConfig