I should commit more frequently 🫣

This commit is contained in:
Andrea Ciceri 2023-09-22 18:45:43 +02:00
parent d7e82621c8
commit f1483e45f0
Signed by: aciceri
SSH key fingerprint: SHA256:/AagBweyV4Hlfg9u092n8hbHwD5fcB6A3qhDiDA65Rg
70 changed files with 2344 additions and 599 deletions

View file

@ -0,0 +1,11 @@
{
services.adguardhome = {
enable = true;
settings = {
bind_port = 3000;
openFirewall = true;
};
};
networking.firewall.allowedTCPPorts = [3000 53];
networking.firewall.allowedUDPPorts = [53];
}

View file

@ -4,7 +4,7 @@
pkgs,
...
}: {
boot.binfmt.emulatedSystems = ["aarch64-linux" "i686-linux"];
boot.binfmt.emulatedSystems = ["aarch64-linux" "i686-linux" "riscv64-linux"];
nix.extraOptions = ''
extra-platforms = aarch64-linux arm-linux i686-linux
'';

View file

@ -52,10 +52,12 @@
description = "Andrea Ciceri";
isNormalUser = true;
inherit (config.ccr) extraGroups;
shell = pkgs.nushell;
shell = pkgs.zsh;
openssh.authorizedKeys.keys = config.ccr.authorizedKeys;
};
programs.zsh.enable = true;
services.getty.autologinUser =
if config.ccr.autologin
then "ccr"

View file

@ -5,8 +5,10 @@
...
}: {
virtualisation.docker.enable = true;
virtualisation.podman.enable = true;
users.users.ccr.extraGroups = ["docker"];
environment.systemPackages = with pkgs; [
docker-compose
podman-compose
];
}

View file

@ -1,6 +1,6 @@
{pkgs, ...}: {
fonts = {
fonts = with pkgs; [powerline-fonts dejavu_fonts fira-code fira-code-symbols emacs-all-the-icons-fonts nerdfonts joypixels etBook];
fonts = with pkgs; [powerline-fonts dejavu_fonts fira-code fira-code-symbols iosevka iosevka-comfy.comfy emacs-all-the-icons-fonts nerdfonts joypixels etBook];
fontconfig.defaultFonts = {
monospace = ["DejaVu Sans Mono for Powerline"];
sansSerif = ["DejaVu Sans"];

View file

@ -0,0 +1,3 @@
{
services.gnome.gnome-keyring.enable = true;
}

45
modules/grocy/default.nix Normal file
View file

@ -0,0 +1,45 @@
{
pkgs,
config,
...
}: {
nixpkgs.overlays = [
(self: super: {
grocy = super.grocy.overrideAttrs (old: {
meta.broken = false;
version = "4.0.1";
src = pkgs.fetchFromGitHub {
owner = "grocy";
repo = "grocy";
rev = "v4.0.1";
hash = "sha256-bCUH2dRCSNkpWyUxGdTdjgVsagbBghcHsBX01+NuHGc=";
};
});
})
];
services.grocy = {
enable = true;
hostName = "grocy.aciceri.dev";
nginx.enableSSL = false;
settings = {
culture = "it";
currency = "EUR";
calendar = {
firstDayOfWeek = 1;
showWeekNumber = true;
};
};
};
services.nginx.virtualHosts.${config.services.grocy.hostName}.listen = [
{
addr = "0.0.0.0";
port = 6789;
ssl = false;
}
];
networking.firewall.interfaces."wg0" = {
allowedTCPPorts = [
6789
];
};
}

View file

@ -0,0 +1,174 @@
{
pkgs,
config,
...
}: let
smartthings-fork = pkgs.fetchFromGitHub {
owner = "veista";
repo = "smartthings";
rev = "ba1a6f33c6ac37d81f4263073571628803e79697";
sha256 = "sha256-X3SYkg0B5pzEich7/4iUmlADJneVuT8HTVnIiC7odRE=";
};
pun_sensor = pkgs.fetchFromGitHub {
owner = "virtualdj";
repo = "pun_sensor";
rev = "19f270b353594ab536f9dc42adf31427e7a81562";
hash = "sha256-3pL+8CXzjmR54Ff9qLhHzC/C+uns0qWEgJFHv+K4MFs=";
};
cozy_life = pkgs.fetchFromGitHub {
owner = "yangqian";
repo = "hass-cozylife";
rev = "9a40a2fa09b0f74aee0b278e2858f5600b3487a9";
hash = "sha256-i+82EUamV1Fhwhb1vhRqn9aA9dJ0FxSSMD734domyhw=";
};
localtuya = pkgs.fetchFromGitHub {
owner = "rospogrigio";
repo = "localtuya";
rev = "f06e4848e67997edfa696aa9a89372fb17077bd0";
hash = "sha256-hA/1FxH0wfM0jz9VqGCT95rXlrWjxV5oIkSiBf0G0ac=";
};
in {
services.home-assistant = {
enable = true;
openFirewall = true;
package = pkgs.home-assistant.overrideAttrs (old: {
doInstallCheck = false;
prePatch =
''
rm -rf homeassistant/components/smartthings
cp -r ${smartthings-fork}/custom_components/smartthings homeassistant/components/smartthings
''
+ old.prePatch;
});
extraComponents = [
# components required to complete the onboarding
# "esphome"
"met"
"radio_browser"
"frontend"
"cloud"
"google_translate"
"smartthings" # samsung devices
"tuya"
"timer"
"cast"
"weather"
"backup"
"brother"
"webostv"
"media_player"
"wyoming"
];
extraPackages = python3Packages:
with python3Packages; [
# used by pun_sensor
holidays
beautifulsoup4
];
config = {
default_config = {};
http = {
use_x_forwarded_for = true;
trusted_proxies = ["10.100.0.1"];
};
# ffmpeg = {};
# camera = [
# {
# name = "EyeToy";
# platform = "ffmpeg";
# input = "/dev/video1";
# extra_arguments = "-vcodec h264";
# }
# ];
homeassistant = {
unit_system = "metric";
time_zone = "Europe/Rome";
temperature_unit = "C";
external_url = "https://home.aciceri.dev";
internal_url = "http://rock5b.fleet:8123";
};
logger.default = "WARNING";
# backup = {};
# media_player = [{
# platform = "webostv";
# host = "10.1.1.213";
# name = "TV";
# timeout = "5";
# turn_on_action = {
# service = "wake_on_lan.send_magic_packet";
# data.mac = "20:28:bc:74:14:c2";
# };
# }];
};
};
# services.avahi.enable = true;
# services.avahi.nssmdns = true;
# systemd.services.home-assistant.serviceConfig = {
# SupplementaryGroups = ["video"];
# DeviceAllow = ["/dev/video1"];
# };
# users.users.hass.extraGroups = ["video"];
systemd.tmpfiles.rules = [
"d ${config.services.home-assistant.configDir}/custom_components 770 hass hass"
"L+ ${config.services.home-assistant.configDir}/custom_components/pun_sensor - - - - ${pun_sensor}/custom_components/pun_sensor"
"L+ ${config.services.home-assistant.configDir}/custom_components/cozy_life - - - - ${cozy_life}/custom_components/cozylife"
"L+ ${config.services.home-assistant.configDir}/custom_components/localtuya - - - - ${localtuya}/custom_components/localtuya"
"d ${config.services.home-assistant.configDir}/www 770 hass hass"
"C ${config.services.home-assistant.configDir}/www/home.png - - - - ${config.age.secrets.home-planimetry.path}"
];
networking.firewall.interfaces."wg0" = {
allowedTCPPorts = [
config.services.home-assistant.config.http.server_port
56000
];
};
# virtualisation.oci-containers.containers = {
# cam2ip = {
# image = "gen2brain/cam2ip:arm";
# ports = ["56000:56000"];
# extraOptions = [ "--device=/dev/video1:/dev/video1"];
# environment.CAM2IP_INDEX = "1";
# };
# };
virtualisation.oci-containers = {
containers = {
whisper = {
image = "rhasspy/wyoming-whisper:latest";
ports = ["10300:10300"];
cmd = [
"--model"
"medium-int8"
"--language"
"it"
];
};
piper = {
image = "rhasspy/wyoming-piper:latest";
ports = ["10200:10200"];
cmd = [
"--voice"
"it_IT-riccardo-x_low"
];
};
};
};
# virtualisation.oci-containers = {
# backend = "podman";
# containers.homeassistant = {
# volumes = [ "home-assistant:/config" ];
# environment.TZ = "Europe/Rome";
# image = "ghcr.io/home-assistant/home-assistant:stable"; # Warning: if the tag does not change, the image will not be updated
# extraOptions = [
# "--network=host"
# "--device=/dev/ttyACM0:/dev/ttyACM0" # Example, change this to match your own hardware
# ];
# };
# };s
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 550 KiB

View file

@ -0,0 +1,81 @@
{
config,
fleetFlake,
pkgs,
lib,
...
}: let
typesenseApiKeyFile = pkgs.writeText "typesense-api-key" "12318551487654187654"; # api key not ime, stolen from upstram PR
pkgsImmich = fleetFlake.inputs.nixpkgsImmich.legacyPackages.${pkgs.system}.extend (final: prev: {
python = prev.python.override {
packageOverrides = final: prev: {
insightface = prev.insightface.overrideAttrs (_: {
pythonCatchConflictsPhase = "";
});
};
};
});
in {
imports = ["${fleetFlake.inputs.nixpkgsImmich}/nixos/modules/services/web-apps/immich.nix"];
services.immich = {
package = pkgsImmich.immich;
enable = true;
server.mediaDir = "/mnt/hd/immich";
server.typesense.apiKeyFile = typesenseApiKeyFile;
};
services.typesense = {
enable = true;
# In a real setup you should generate an api key for immich
# and not use the admin key!
apiKeyFile = typesenseApiKeyFile;
settings.server.api-address = "127.0.0.1";
};
systemd.tmpfiles.rules = [
"d /mnt/hd/immich 770 immich immich"
];
# networking.firewall.allowedTCPPorts = [8080];
# virtualisation.oci-containers.containers.immich = {
# image = "ghcr.io/imagegenius/immich:latest";
# extraOptions = ["--network=host"];
# volumes = [
# "/mnt/immich/photos:/photos"
# "/mnt/immich/config:/config"
# ];
# environment = {
# PUID=builtins.toString config.users.users.ccr.uid;
# PGID=builtins.toString config.users.groups.wheel.gid;
# TZ="Europe/Rome";
# DB_HOSTNAME="localhost";
# DB_USERNAME="postgres";
# DB_PASSWORD="postgres";
# DB_DATABASE_NAME="immich";
# DB_PORT="54320";
# REDIS_HOSTNAME="localhost";
# DISABLE_MACHINE_LEARNING="false";
# DISABLE_TYPESENSE="false";
# };
# };
# virtualisation.oci-containers.containers.immich-redis = {
# image = "redis";
# extraOptions = ["--network=host"];
# };
# virtualisation.oci-containers.containers.immich-postgres = {
# image = "postgres:14";
# extraOptions = ["--network=host"];
# environment = {
# POSTGRES_USER = "postgres";
# POSTGRES_PASSWORD = "postgres";
# POSTGRES_DB = "immich";
# PGPORT = "54320";
# };
# volumes = [
# "/mnt/immich/postgres:/var/lib/postgresql/data"
# ];
# };
}

608
modules/immich/module.nix Normal file
View file

@ -0,0 +1,608 @@
{
config,
lib,
pkgs,
...
}: let
inherit
(lib)
hasAttr
hasPrefix
maintainers
mapAttrs
mkDefault
mkEnableOption
mkIf
mkMerge
mkOption
mkPackageOption
optional
optionalAttrs
optionalString
types
;
cfg = config.services.immich;
serverCfg = config.services.immich.server;
backendCfg = serverCfg.backend;
microservicesCfg = serverCfg.microservices;
webCfg = cfg.web;
mlCfg = cfg.machineLearning;
isServerPostgresUnix = hasPrefix "/" serverCfg.postgres.host;
postgresEnv =
if isServerPostgresUnix
then {
# If passwordFile is given, this will be overwritten in ExecStart
DB_URL = "socket://${serverCfg.postgres.host}?dbname=${serverCfg.postgres.database}";
}
else {
DB_HOSTNAME = serverCfg.postgres.host;
DB_PORT = toString serverCfg.postgres.port;
DB_DATABASE_NAME = serverCfg.postgres.database;
DB_USERNAME = serverCfg.postgres.username;
};
typesenseEnv =
{
TYPESENSE_ENABLED = toString serverCfg.typesense.enable;
}
// optionalAttrs serverCfg.typesense.enable {
TYPESENSE_HOST = serverCfg.typesense.host;
TYPESENSE_PORT = toString serverCfg.typesense.port;
TYPESENSE_PROTOCOL = serverCfg.typesense.protocol;
};
# Don't start a redis instance if the user sets a custom redis connection
enableRedis = !hasAttr "REDIS_URL" serverCfg.extraConfig && !hasAttr "REDIS_SOCKET" serverCfg.extraConfig;
redisServerCfg = config.services.redis.servers.immich;
redisEnv = optionalAttrs enableRedis {
REDIS_SOCKET = redisServerCfg.unixSocket;
};
serverEnv =
postgresEnv
// typesenseEnv
// redisEnv
// {
NODE_ENV = "production";
IMMICH_MEDIA_LOCATION = serverCfg.mediaDir;
IMMICH_MACHINE_LEARNING_URL =
if serverCfg.machineLearningUrl != null
then serverCfg.machineLearningUrl
else "false";
};
serverStartWrapper = program: ''
set -euo pipefail
mkdir -p ${serverCfg.mediaDir}
${optionalString (serverCfg.postgres.passwordFile != null) (
if isServerPostgresUnix
then ''export DB_URL="socket://${serverCfg.postgres.username}:$(cat ${serverCfg.postgres.passwordFile})@${serverCfg.postgres.host}?dbname=${serverCfg.postgres.database}"''
else "export DB_PASSWORD=$(cat ${serverCfg.postgres.passwordFile})"
)}
${optionalString serverCfg.typesense.enable ''
export TYPESENSE_API_KEY=$(cat ${serverCfg.typesense.apiKeyFile})
''}
exec ${program}
'';
commonServiceConfig = {
Restart = "on-failure";
# Hardening
CapabilityBoundingSet = "";
LockPersonality = true;
MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
PrivateUsers = true;
PrivateTmp = true;
PrivateDevices = true;
PrivateMounts = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProcSubset = "pid";
# Would re-mount paths ignored by temporary root
# TODO ProtectSystem = "strict";
RemoveIPC = true;
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_UNIX"
];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
SystemCallFilter = [
"@system-service"
"~@privileged"
"@pkey"
];
UMask = "0077";
};
serverServiceConfig = {
DynamicUser = true;
User = "immich";
Group = "immich";
SupplementaryGroups = optional enableRedis redisServerCfg.user;
StateDirectory = "immich";
StateDirectoryMode = "0750";
WorkingDirectory = "/var/lib/immich";
MemoryDenyWriteExecute = false; # nodejs requires this.
EnvironmentFile = mkIf (serverCfg.environmentFile != null) serverCfg.environmentFile;
TemporaryFileSystem = "/:ro";
BindReadOnlyPaths =
[
"/nix/store"
"-/etc/resolv.conf"
"-/etc/nsswitch.conf"
"-/etc/hosts"
"-/etc/localtime"
"-/run/postgresql"
]
++ optional enableRedis redisServerCfg.unixSocket;
};
in {
options.services.immich = {
enable =
mkEnableOption "immich"
// {
description = ''
Enables immich which consists of a backend server, microservices,
machine-learning and web ui. You can disable or reconfigure components
individually using the subsections.
'';
};
package = mkPackageOption pkgs "immich" {};
server = {
mediaDir = mkOption {
type = types.str;
default = "/var/lib/immich/media";
description = "Directory used to store media files.";
};
backend = {
enable =
mkEnableOption "immich backend server"
// {
default = true;
};
port = mkOption {
type = types.port;
default = 3001;
description = "Port to bind to.";
};
openFirewall = mkOption {
default = false;
type = types.bool;
description = "Whether to open the firewall for the specified port.";
};
extraConfig = mkOption {
type = types.attrs;
default = {};
example = {
LOG_LEVEL = "debug";
};
description = ''
Extra configuration options (environment variables).
Refer to [the documented variables](https://documentation.immich.app/docs/install/environment-variables) tagged with 'server' for available options.
'';
};
environmentFile = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Environment file as defined in systemd.exec(5). May be used to provide
additional secret variables to the service without adding them to the
world-readable Nix store.
'';
};
};
microservices = {
enable =
mkEnableOption "immich microservices"
// {
default = true;
};
port = mkOption {
type = types.port;
default = 3002;
description = "Port to bind to.";
};
openFirewall = mkOption {
default = false;
type = types.bool;
description = "Whether to open the firewall for the specified port.";
};
extraConfig = mkOption {
type = types.attrs;
default = {};
example = {
REVERSE_GEOCODING_PRECISION = 1;
};
description = ''
Extra configuration options (environment variables).
Refer to [the documented variables](https://documentation.immich.app/docs/install/environment-variables) tagged with 'microservices' for available options.
'';
};
environmentFile = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Environment file as defined in systemd.exec(5). May be used to provide
additional secret variables to the service without adding them to the
world-readable Nix store.
'';
};
};
typesense = {
enable =
mkEnableOption "typesense"
// {
default = true;
};
host = mkOption {
type = types.str;
default = "127.0.0.1";
example = "typesense.example.com";
description = "Hostname/address of the typesense server to use.";
};
port = mkOption {
type = types.port;
default = 8108;
description = "The port of the typesense server to use.";
};
protocol = mkOption {
type = types.str;
default = "http";
description = "The protocol to use when connecting to the typesense server.";
};
apiKeyFile = mkOption {
type = types.path;
description = "Sets the api key for authentication with typesense.";
};
};
postgres = {
host = mkOption {
type = types.str;
default = "/run/postgresql";
description = "Hostname/address of the postgres server to use. If an absolute path is given here, it will be interpreted as a unix socket path.";
};
port = mkOption {
type = types.port;
default = 5432;
description = "The port of the postgres server to use.";
};
username = mkOption {
type = types.str;
default = "immich";
description = "The postgres username to use.";
};
passwordFile = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Sets the password for authentication with postgres.
May be unset when using socket authentication.
'';
};
database = mkOption {
type = types.str;
default = "immich";
description = "The postgres database to use.";
};
};
useMachineLearning = mkOption {
description = "Use the given machine learning server endpoint to enable ML functionality in immich.";
default = true;
type = types.bool;
};
machineLearningUrl = mkOption {
type = types.str;
default = "http://127.0.0.1:3003";
example = "https://immich-ml.internal.example.com";
description = "The machine learning server endpoint to use.";
};
extraConfig = mkOption {
type = types.attrs;
default = {};
example = {
REDIS_SOCKET = "/run/custom-redis";
};
description = ''
Extra configuration options (environment variables) for both backend and microservices.
Refer to [the documented variables](https://documentation.immich.app/docs/install/environment-variables) tagged with both 'server' and 'microservices' for available options.
'';
};
environmentFile = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Environment file as defined in systemd.exec(5). May be used to provide
additional secret variables to the backend and microservices servers without
adding them to the world-readable Nix store.
'';
};
};
web = {
enable =
mkEnableOption "immich web frontend"
// {
default = true;
};
port = mkOption {
type = types.port;
default = 3000;
description = "Port to bind to.";
};
openFirewall = mkOption {
default = false;
type = types.bool;
description = "Whether to open the firewall for the specified port.";
};
serverUrl = mkOption {
type = types.str;
default = "http://127.0.0.1:3001";
example = "https://immich-backend.internal.example.com";
description = "The backend server url to use.";
};
apiUrlExternal = mkOption {
type = types.str;
default = "/web";
description = "The api url to use for external requests.";
};
extraConfig = mkOption {
type = types.attrs;
default = {};
example = {
PUBLIC_LOGIN_PAGE_MESSAGE = "My awesome Immich instance!";
};
description = ''
Extra configuration options (environment variables).
Refer to [the documented variables](https://documentation.immich.app/docs/install/environment-variables) tagged with 'web' for available options.
'';
};
};
machineLearning = {
enable =
mkEnableOption "immich machine-learning server"
// {
default = true;
};
port = mkOption {
type = types.port;
default = 3003;
description = "Port to bind to.";
};
openFirewall = mkOption {
default = false;
type = types.bool;
description = "Whether to open the firewall for the specified port.";
};
extraConfig = mkOption {
type = types.attrs;
default = {};
example = {
MACHINE_LEARNING_MODEL_TTL = 600;
};
description = ''
Extra configuration options (environment variables).
Refer to [the documented variables](https://documentation.immich.app/docs/install/environment-variables) tagged with 'machine learning' for available options.
'';
};
};
};
config = mkIf cfg.enable {
assertions = [
{
assertion = !isServerPostgresUnix -> serverCfg.postgres.passwordFile != null;
message = "A database password must be provided when unix sockets are not used.";
}
];
networking.firewall.allowedTCPPorts = mkMerge [
(mkIf (backendCfg.enable && backendCfg.openFirewall) [backendCfg.port])
(mkIf (microservicesCfg.enable && microservicesCfg.openFirewall) [microservicesCfg.port])
(mkIf (webCfg.enable && webCfg.openFirewall) [webCfg.port])
(mkIf (mlCfg.enable && mlCfg.openFirewall) [mlCfg.port])
];
services.redis.servers.immich.enable = mkIf enableRedis true;
services.redis.vmOverCommit = mkIf enableRedis (mkDefault true);
systemd.services.immich-server = mkIf backendCfg.enable {
description = "Immich backend server (Self-hosted photo and video backup solution)";
after =
[
"network.target"
"typesense.service"
"postgresql.service"
"immich-machine-learning.service"
]
++ optional enableRedis "redis-immich.service";
wantedBy = ["multi-user.target"];
environment =
serverEnv
// {
SERVER_PORT = toString backendCfg.port;
}
// mapAttrs (_: toString) serverCfg.extraConfig
// mapAttrs (_: toString) backendCfg.extraConfig;
script = serverStartWrapper "${cfg.package}/bin/server";
serviceConfig = mkMerge [
(commonServiceConfig // serverServiceConfig)
{
EnvironmentFile = mkIf (backendCfg.environmentFile != null) backendCfg.environmentFile;
}
];
};
systemd.services.immich-microservices = mkIf microservicesCfg.enable {
description = "Immich microservices (Self-hosted photo and video backup solution)";
after =
[
"network.target"
"typesense.service"
"postgresql.service"
"immich-machine-learning.service"
]
++ optional enableRedis "redis-immich.service";
wantedBy = ["multi-user.target"];
environment =
serverEnv
// {
MICROSERVICES_PORT = toString microservicesCfg.port;
}
// mapAttrs (_: toString) serverCfg.extraConfig
// mapAttrs (_: toString) microservicesCfg.extraConfig;
script = serverStartWrapper "${cfg.package}/bin/microservices";
serviceConfig = mkMerge [
(commonServiceConfig // serverServiceConfig)
{
EnvironmentFile = mkIf (microservicesCfg.environmentFile != null) microservicesCfg.environmentFile;
}
];
};
systemd.services.immich-web = mkIf webCfg.enable {
description = "Immich web (Self-hosted photo and video backup solution)";
after = [
"network.target"
"immich-server.service"
];
wantedBy = ["multi-user.target"];
environment =
{
NODE_ENV = "production";
PORT = toString webCfg.port;
IMMICH_SERVER_URL = webCfg.serverUrl;
IMMICH_API_URL_EXTERNAL = webCfg.apiUrlExternal;
}
// mapAttrs (_: toString) webCfg.extraConfig;
script = ''
set -euo pipefail
export PUBLIC_IMMICH_SERVER_URL=$IMMICH_SERVER_URL
export PUBLIC_IMMICH_API_URL_EXTERNAL=$IMMICH_API_URL_EXTERNAL
exec ${cfg.package.web}/bin/web
'';
serviceConfig =
commonServiceConfig
// {
DynamicUser = true;
User = "immich-web";
Group = "immich-web";
MemoryDenyWriteExecute = false; # nodejs requires this.
TemporaryFileSystem = "/:ro";
BindReadOnlyPaths = [
"/nix/store"
"-/etc/resolv.conf"
"-/etc/nsswitch.conf"
"-/etc/hosts"
"-/etc/localtime"
];
};
};
systemd.services.immich-machine-learning = mkIf mlCfg.enable {
description = "Immich machine learning (Self-hosted photo and video backup solution)";
after = ["network.target"];
wantedBy = ["multi-user.target"];
environment =
{
NODE_ENV = "production";
MACHINE_LEARNING_PORT = toString mlCfg.port;
MACHINE_LEARNING_CACHE_FOLDER = "/var/cache/immich-ml";
TRANSFORMERS_CACHE = "/var/cache/immich-ml";
}
// mapAttrs (_: toString) mlCfg.extraConfig;
serviceConfig =
commonServiceConfig
// {
ExecStart = "${cfg.package.machine-learning}/bin/machine-learning";
DynamicUser = true;
User = "immich-ml";
Group = "immich-ml";
MemoryDenyWriteExecute = false; # onnxruntime_pybind11 requires this.
ProcSubset = "all"; # Needs /proc/cpuinfo
CacheDirectory = "immich-ml";
CacheDirectoryMode = "0700";
# TODO gpu access
TemporaryFileSystem = "/:ro";
BindReadOnlyPaths = [
"/nix/store"
"-/etc/resolv.conf"
"-/etc/nsswitch.conf"
"-/etc/hosts"
"-/etc/localtime"
];
};
};
meta.maintainers = with maintainers; [oddlama];
};
}

View file

@ -0,0 +1,15 @@
{
services.mediatomb = {
enable = true;
openFirewall = true;
serverName = "Rock 5B";
mediaDirectories = [
{
path = "/mnt/hd/torrent";
recursive = true;
}
];
};
users.users.mediatomb.extraGroups = ["transmission"];
}

View file

@ -6,7 +6,7 @@
friendly_name = config.networking.hostName;
inotify = "yes";
media_dir = [
"/mnt/raid/torrent"
"/mnt/torrent"
];
};
};

View file

@ -0,0 +1,27 @@
{
config,
pkgs,
...
}: let
cfg = config.services.nextcloud;
in {
systemd.tmpfiles.rules = [
"d /mnt/raid/nextcloud 770 nextcloud nextcloud"
];
ccr.extraGroups = ["nextcloud"];
services.nextcloud = {
enable = true;
package = pkgs.nextcloud26;
database.createLocally = true;
home = "/mnt/raid/nextcloud";
hostName = "nextcloud.aciceri.dev";
config = {
adminpassFile = config.age.secrets.nextcloud-admin-pass.path;
overwriteProtocol = "https";
};
};
networking.firewall.allowedTCPPorts = [80];
}

View file

@ -15,8 +15,8 @@ in {
config = {
services.nix-serve = {
enable = true;
secretKeyFile = config.age.secrets.cache-private-key.path;
# Public key: cache.aciceri.dev-1:aNP6f+rRTuDHi/45L1VBzlGchuj54/mI2N/22qTWgzE=
# secretKeyFile = config.age.secrets.cache-private-key.path;
# Public key: cache.aciceri.dev-1B:aNP6f+BrRTuDHi/45L1VBzlGchuj54/mI2N/22qTWgzE=
};
services.nginx.virtualHosts."${cfg.domain}" = {
forceSSL = true;

View file

@ -0,0 +1,20 @@
{...}: {
networking.firewall.interfaces."wg0" = {
allowedTCPPorts = [
35901
];
};
imports = [../nginx-base];
services.nginx.virtualHosts = {
"roam.aciceri.dev" = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:35901";
proxyWebsockets = true;
};
};
};
# TODO use oauth2 proxy
}

View file

@ -8,5 +8,17 @@
# Important to resolve .local domains of printers, otherwise you get an error
# like "Impossible to connect to XXX.local: Name or service not known"
services.avahi.nssmdns = true;
services.printing.enable = true;
hardware.sane.enable = true;
services.paperless = {
enable = true;
passwordFile = builtins.toFile "password" "admin";
};
services.printing = {
enable = true;
drivers = [
(pkgs.callPackage ./driver.nix {})
];
};
}

View file

@ -0,0 +1,86 @@
{
lib,
stdenv,
fetchurl,
dpkg,
autoPatchelfHook,
makeWrapper,
perl,
gnused,
ghostscript,
file,
coreutils,
gnugrep,
which,
}: let
arches = ["x86_64" "i686" "armv7l"];
runtimeDeps = [
ghostscript
file
gnused
gnugrep
coreutils
which
];
in
stdenv.mkDerivation rec {
pname = "cups-brother-mfcl2710dw";
version = "4.0.0-1";
nativeBuildInputs = [dpkg makeWrapper autoPatchelfHook];
buildInputs = [perl];
dontUnpack = true;
src = fetchurl {
url = "https://download.brother.com/welcome/dlf103526/mfcl2710dwpdrv-${version}.i386.deb";
hash = "sha256-OOTvbCuyxw4k01CTMuBqG2boMN13q5xC7LacaweGmyw=";
};
installPhase =
''
runHook preInstall
mkdir -p $out
dpkg-deb -x $src $out
# delete unnecessary files for the current architecture
''
+ lib.concatMapStrings (arch: ''
echo Deleting files for ${arch}
rm -r "$out/opt/brother/Printers/MFCL2710DW/lpd/${arch}"
'') (builtins.filter (arch: arch != stdenv.hostPlatform.linuxArch) arches)
+ ''
# bundled scripts don't understand the arch subdirectories for some reason
ln -s \
"$out/opt/brother/Printers/MFCL2710DW/lpd/${stdenv.hostPlatform.linuxArch}/"* \
"$out/opt/brother/Printers/MFCL2710DW/lpd/"
# Fix global references and replace auto discovery mechanism with hardcoded values
substituteInPlace $out/opt/brother/Printers/MFCL2710DW/lpd/lpdfilter \
--replace /opt "$out/opt" \
--replace "my \$BR_PRT_PATH =" "my \$BR_PRT_PATH = \"$out/opt/brother/Printers/MFCL2710DW\"; #" \
--replace "PRINTER =~" "PRINTER = \"MFCL2710DW\"; #"
# Make sure all executables have the necessary runtime dependencies available
find "$out" -executable -and -type f | while read file; do
wrapProgram "$file" --prefix PATH : "${lib.makeBinPath runtimeDeps}"
done
# Symlink filter and ppd into a location where CUPS will discover it
mkdir -p $out/lib/cups/filter
mkdir -p $out/share/cups/model
ln -s \
$out/opt/brother/Printers/MFCL2710DW/lpd/lpdfilter \
$out/lib/cups/filter/brother_lpdwrapper_MFCL2710DW
ln -s \
$out/opt/brother/Printers/MFCL2710DW/cupswrapper/brother-MFCL2710DW-cups-en.ppd \
$out/share/cups/model/
runHook postInstall
'';
}

View file

@ -1,4 +1,4 @@
{
{config, ...}: {
imports = [../nginx-base];
services.nginx.virtualHosts = {
"bubbleupnp.mothership.aciceri.dev" = {
@ -8,6 +8,18 @@
proxyPass = "http://rock5b.fleet:58050";
};
};
"home.aciceri.dev" = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://rock5b.fleet:8123";
proxyWebsockets = true;
};
extraConfig = ''
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
'';
};
"transmission.mothership.aciceri.dev" = {
forceSSL = true;
enableACME = true;
@ -15,5 +27,13 @@
proxyPass = "http://rock5b.fleet:9091";
};
};
"photos.aciceri.dev" = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:8080";
proxyWebsockets = true;
};
};
};
}

View file

@ -4,8 +4,8 @@
openRPCPort = true;
openPeerPorts = true;
settings = {
download-dir = "/mnt/raid/torrent";
incomplete-dir = "/mnt/raid/torrent/.incomplete";
download-dir = "/mnt/hd/torrent";
incomplete-dir = "/mnt/hd/torrent/.incomplete";
rpc-bind-address = "0.0.0.0";
peer-port = 51413; # Forward both TCP and UDP on router traffic from router
@ -36,8 +36,8 @@
];
systemd.tmpfiles.rules = [
"d /mnt/raid/torrent 770 transmission transmission"
"d /mnt/raid/torrent/.incomplete 770 transmission transmission"
"d /mnt/hd/torrent 770 transmission transmission"
"d /mnt/hd/torrent/.incomplete 770 transmission transmission"
];
ccr.extraGroups = ["transmission"];

Binary file not shown.

View file

@ -1,5 +1,5 @@
{
# config,
config,
pkgs,
# lib,
# fleetFlake,
@ -29,12 +29,27 @@
];
};
# boot.kernelParams = [
# "intel_iommu=on"
# # "iommu=pt"
# "i915.enable_guc=3"
# "i915.max_vfs=7"
# ];
hardware.opengl.enable = true;
virtualisation.spiceUSBRedirection.enable = true;
boot = {
initrd.kernelModules = [
"vfio_pci"
"vfio"
"vfio_iommu_type1"
"vfio_virqfd"
# "i915"
];
};
boot.kernelParams = [
"intel_iommu=on"
"vfio-pci.ids=8086:4680"
# "iommu=pt"
"i915.enable_guc=3"
"i915.max_vfs=7"
];
# boot.blacklistedKernelModules = ["i915"];
@ -47,9 +62,9 @@
# (config.boot.kernelPackages.callPackage ./i915-sriov-dkms.nix {} )
# ];
# boot.initrd.availableKernelModules = [
# "i915"
# ];
boot.initrd.availableKernelModules = [
# "i915"
];
# boot.initrd.kernelModules = [
# "i915"
@ -94,9 +109,9 @@
# }
];
# boot.kernel.sysctl = {
# "devices/pci0000:00/0000:00:02.0/sriov_numvfs" = 7;
# };
boot.kernel.sysctl = {
"devices/pci0000:00/0000:00:02.0/sriov_numvfs" = 7;
};
# -vnc :0 \
# -audiodev alsa,id=snd0,out.try-poll=off -device ich9-intel-hda -device hda-output,audiodev=snd0 \
@ -112,7 +127,7 @@
qemu-system-x86_64 \
-enable-kvm \
-cpu host,kvm=on,hv-vendor_id="GenuineIntel" \
-cpu host,kvm=off,hv-spinlocks=819,hv-vapic=on,hv-relaxed=on,hv-vendor-id="IrisXE" \
-smp 4 \
-m 8192 \
-nic user,model=virtio-net-pci,hostfwd=tcp::3389-:3389,hostfwd=tcp::47989-:47989,hostfwd=tcp::47990-:47990,hostfwd=tcp::47984-:47984,hostfwd=tcp::48010-:48010,hostfwd=udp::47998-:47988,hostfwd=udp::47999-:47999,hostfwd=udp::48000-:48000,hostfwd=udp::48002-:48002,hostfwd=udp::48003-:48003,hostfwd=udp::48004-:48004,hostfwd=udp::48005-:48005,hostfwd=udp::48006-:48006,hostfwd=udp::48007-:48007,hostfwd=udp::48008-:48008,hostfwd=udp::48009-:48009,hostfwd=udp::48010-:48010 \
@ -121,7 +136,9 @@
-device usb-tablet \
-vnc :0 \
-nographic \
-drive file=/var/lib/vm-mara/w10.qcow2
-vga none \
-drive file=/var/lib/vm-mara/w10.qcow2 \
-device vfio-pci,host=00:02.0,addr=03.0,x-vga=on,multifunction=on,romfile=${./adls_dmc_ver2_01.bin}
'';
};
in {

View file

@ -12,8 +12,8 @@
src = fetchFromGitHub {
owner = "strongtz";
repo = "i915-sriov-dkms";
rev = version;
sha256 = "sha256-LNKDNi7oEhO3FY47oKYbg8wt+268GlBVxQpHdNLNrwM=";
rev = "db4e8ccd9bd31fad79361e27afc032487426fe6a";
hash = "sha256-WCDwy39jpnc2wkM/883gFwChVD7wAP2nCR8Aw+CfDw8=";
};
nativeBuildInputs = kernel.moduleBuildDependencies;

View file

@ -0,0 +1,34 @@
{pkgs, ...}: {
virtualisation.libvirtd.enable = true;
networking.firewall.interfaces."wg0" = {
allowedTCPPorts = [
5900 # vnc by QEMU
2233
60022
8545
];
};
systemd.services.vm-ubuntu = let
start-vm = pkgs.writeShellApplication {
name = "start-vm";
runtimeInputs = with pkgs; [qemu];
text = ''
qemu-system-x86_64 \
-enable-kvm \
-cpu host,kvm=on,hv-vendor_id="GenuineIntel" \
-smp 4 \
-m 8192 \
-nic user,model=virtio-net-pci,hostfwd=tcp::60022-:22,hostfwd=tcp::8545-:8545 \
-drive file=/var/lib/vm-ubuntu/ubuntu.qcow2
'';
};
in {
wantedBy = ["multi-user.target"];
after = ["network.target"];
serviceConfig = {
ExecStart = "${start-vm}/bin/start-vm";
};
};
}

View file

@ -0,0 +1,3 @@
{
virtualisation.waydroid.enable = true;
}