diff --git a/hosts/deadbeef/default.nix b/hosts/deadbeef/default.nix new file mode 100644 index 0000000..1ff347f --- /dev/null +++ b/hosts/deadbeef/default.nix @@ -0,0 +1,60 @@ +{ config, pkgs, home-manager, ... }: +{ + imports = [ + ./hardware-configuration.nix + home-manager.nixosModules.home-manager + ../../modules + # "${config.inputs.self}/modules" + ]; + + # Don't use the systemd-boot EFI boot loader. + # Necessary for dual boot Windows + boot.loader.systemd-boot.enable = false; + + boot.loader = { + efi = { + canTouchEfiVariables = true; + efiSysMountPoint = "/boot"; + }; + grub = { + enable = true; + devices = [ "nodev" ]; + efiSupport = true; + extraEntries = '' + menuentry "Windows" { + insmod part_gpt + insmod fat + insmod search_fs_uuid + insmod chain + search --fs-uuid --set=root FE74-E293 + chainloader /EFI/Microsoft/Boot/bootmgfw.efi + } + ''; + }; + }; + + mainUser = { + enable = true; + userName = "pazpi"; + hashedPassword = "$y$j9T$dA94KVg1/jYLqclQQbTDk.$cnfxBWUN8P4shr8Kkipv5bU/RCtQNoAwYFDZ0X/BYs5"; + }; + + audio.enable = false; + + # podman.enable = true; + + download-pod.enable = true; + + # Vedi https://github.com/TLATER/dotfiles/blob/f989a86890f27f6b089e9d74b7e8356ec8e5683d/home-config/hosts/yui.nix + # home-manager.users.pazpi = import "${flake-inputs.self}/home-config/hosts/deadbeef.nix"; + + # flake-inputs.self cartella di partenza dove c'è flake.nix + + # This value determines the NixOS release from which the default + # settings for stateful data, like file locations and database versions + # on your system were taken. It's perfectly fine and recommended to leave + # this value at the release version of the first install of this system. + # Before changing this value read the documentation for this option + # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). + system.stateVersion = "24.05"; # Did you read the comment? +} diff --git a/hosts/deadbeef/hardware-configuration.nix b/hosts/deadbeef/hardware-configuration.nix new file mode 100644 index 0000000..e27b461 --- /dev/null +++ b/hosts/deadbeef/hardware-configuration.nix @@ -0,0 +1,57 @@ +# Do not modify this file! It was generated by ‘nixos-generate-config’ +# and may be overwritten by future invocations. Please make changes +# to /etc/nixos/configuration.nix instead. +{ config, lib, modulesPath, ... }: + +{ + imports = + [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usb_storage" "usbhid" "sd_mod" "rtsx_pci_sdmmc" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { + device = "/dev/disk/by-label/NixOS"; + fsType = "btrfs"; + options = [ "subvol=root" "compress=zstd" "discard=async" ]; + }; + + fileSystems."/home" = + { + device = "/dev/disk/by-label/NixOS"; + fsType = "btrfs"; + options = [ "subvol=home" "compress=zstd" "discard=async" ]; + }; + + fileSystems."/nix" = + { + device = "/dev/disk/by-label/NixOS"; + fsType = "btrfs"; + options = [ "subvol=nix" "noatime" "discard=async" ]; + }; + + fileSystems."/boot" = + { + device = "/dev/disk/by-label/BOOT"; + fsType = "vfat"; + }; + + swapDevices = [{ device = "/dev/disk/by-label/SWAP"; }]; + + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces..useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.interfaces.enp62s0u1u4.useDHCP = lib.mkDefault true; + # networking.interfaces.wlp2s0.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + powerManagement.cpuFreqGovernor = lib.mkDefault "powersave"; + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/modules/audio.nix b/modules/audio.nix new file mode 100644 index 0000000..dfd580c --- /dev/null +++ b/modules/audio.nix @@ -0,0 +1,24 @@ +{ lib, config, pkgs, ... }: +let + cfg = config.audio; +in +{ + options.audio = { + enable = lib.mkEnableOption "Enable sound module"; + }; + + config = lib.mkIf cfg.enable { + # Enable sound with pipewire. + services.pipewire = { + enable = true; + audio.enable = true; + alsa.enable = true; + alsa.support32Bit = true; + pulse.enable = true; + }; + + hardware.pulseaudio.enable = false; + + }; + +} diff --git a/modules/btrfs-autoscrub.nix b/modules/btrfs-autoscrub.nix new file mode 100644 index 0000000..0a54b1c --- /dev/null +++ b/modules/btrfs-autoscrub.nix @@ -0,0 +1,31 @@ +{ lib, config, pkgs, ... }: +let + cfg = config.btrfsAutoscrub; +in +{ + options.btrfsAutoscrub = { + enable = lib.mkEnableOption "Enable BTRFS Auto Scrub module"; + + interval = lib.mkOption { + default = "weekly"; + type = config.services.btrfs.autoScrub.interval.type; + description = config.services.btrfs.autoScrub.interval.description; + }; + + fileSystems = lib.mkOption { + default = [ ]; + type = config.services.btrfs.autoScrub.fileSystems.type; + description = config.services.btrfs.autoScrub.fileSystems.description; + }; + + }; + + config = lib.mkIf cfg.enable { + services.btrfs.autoScrub = { + enable = true; + interval = cfg.interval; + fileSystems = cfg.interval; + }; + }; + +} diff --git a/modules/default.nix b/modules/default.nix new file mode 100644 index 0000000..63701dd --- /dev/null +++ b/modules/default.nix @@ -0,0 +1,15 @@ +{ + imports = [ + # Folders + ./desktop + ./networking + ./services + ./virtualisation + + # Files + ./btrfs-autoscrub.nix + ./main-user.nix + ./audio.nix + + ]; +} diff --git a/modules/desktop/default.nix b/modules/desktop/default.nix new file mode 100644 index 0000000..0db6e34 --- /dev/null +++ b/modules/desktop/default.nix @@ -0,0 +1,7 @@ +{ + imports = [ + ./gnome.nix + ./plymouth.nix + ./steam.nix + ]; +} diff --git a/modules/desktop/gnome.nix b/modules/desktop/gnome.nix new file mode 100644 index 0000000..8470719 --- /dev/null +++ b/modules/desktop/gnome.nix @@ -0,0 +1,110 @@ +{ lib, config, pkgs, ... }: +let + cfg = config.gnome; +in +{ + options.gnome = { + enable = lib.mkEnableOption "Enable Gnome and apply customization module"; + + keyboardLayout = lib.mkOption { + default = "it"; + type = lib.types.str; + description = config.services.xserver.xkb.layout.description; + }; + + disableXTerm = lib.mkOption { + default = true; + type = lib.types.bool; + description = '' + Disable the default terminal. Xterm. + If disable don't forget to install one terminal! + ''; + }; + + # extraExtension = lib.mkOption { + # default = [ ]; + # description = '' + # Extra extensions to add without polluting the main module + # ''; + # }; + + }; + + config = lib.mkIf cfg.enable { + services = { + xserver = { + enable = true; + + xkb.layout = cfg.keyboardLayout; + + displayManager.gdm.enable = true; + desktopManager.gnome.enable = true; + + desktopManager.xterm.enable = !cfg.disableXTerm; + excludePackages = lib.mkIf cfg.disableXTerm [ pkgs.xterm ]; + }; + + libinput.enable = true; + + }; + + environment = { + systemPackages = with pkgs; [ + dconf + dconf2nix + gnome-extension-manager + gnome.dconf-editor + gnome.gnome-tweaks + ]; + + # Remove standard Gnome Packages + gnome.excludePackages = (with pkgs; [ + gedit # text editor + gnome-photos + gnome-tour + gnome-connections + gnome-photos + ]) ++ (with pkgs.gnome; [ + atomix # puzzle game + cheese # webcam tool + epiphany # web browser + evince # document viewer + geary # email reader + gnome-calendar + gnome-characters + gnome-clocks + gnome-contacts + gnome-font-viewer + gnome-maps + gnome-music + gnome-terminal + gnome-weather + hitori # sudoku game + iagno # go game + tali # poker game + totem # video player + yelp # help viewer + ]); + + }; + + # gnome-extensions + # home.packages = with pkgs; + # lib.mkIf cfg.enableExtension + # [ + # gnomeExtensions.blur-my-shell + # gnomeExtensions.caffeine + # gnomeExtensions.dash-to-dock + # gnomeExtensions.gsconnect + # gnomeExtensions.gtile + # gnomeExtensions.just-perfection + # gnomeExtensions.pano + # gnomeExtensions.quick-settings-tweaker + # gnomeExtensions.rounded-window-corners + # gnomeExtensions.tailscale-status + # gnomeExtensions.vitals + # ] ++ cfg.extraExtension; + + }; + +} diff --git a/modules/desktop/plymouth.nix b/modules/desktop/plymouth.nix new file mode 100644 index 0000000..983bb67 --- /dev/null +++ b/modules/desktop/plymouth.nix @@ -0,0 +1,18 @@ +{ lib, config, pkgs, ... }: +let + cfg = config.plymouth; +in +{ + options.plymouth = { + enable = lib.mkEnableOption "Enable Plymouth and apply customization module"; + }; + + config = lib.mkIf cfg.enable { + boot.plymouth = { + enable = true; + theme = "colorful_loop"; + themePackages = [ (pkgs.adi1090x-plymouth-themes.override { selected_themes = [ "colorful_loop" ]; }) ]; + }; + }; + +} diff --git a/modules/desktop/steam.nix b/modules/desktop/steam.nix new file mode 100644 index 0000000..9303853 --- /dev/null +++ b/modules/desktop/steam.nix @@ -0,0 +1,18 @@ +{ lib, config, pkgs, ... }: +let + cfg = config.steam; +in +{ + options.steam = { + enable = lib.mkEnableOption "Enable Steam and apply customization module"; + }; + + config = lib.mkIf cfg.enable { + programs.steam.enable = true; + + # Necessary for Steam + hardware.opengl.driSupport32Bit = true; + + }; + +} diff --git a/modules/esphome.nix b/modules/esphome.nix new file mode 100644 index 0000000..77ba9bd --- /dev/null +++ b/modules/esphome.nix @@ -0,0 +1,2 @@ +# Aggiungere l'user al gruppo "dialout" +{ } diff --git a/modules/main-user.nix b/modules/main-user.nix new file mode 100644 index 0000000..b3f78c5 --- /dev/null +++ b/modules/main-user.nix @@ -0,0 +1,56 @@ +{ lib, config, pkgs, ... }: +let + cfg = config.mainUser; +in +{ + options.mainUser = { + enable = lib.mkEnableOption "Enable user module"; + + userName = lib.mkOption { + default = "pazpi"; + type = lib.types.str; + description = config.users.users."".userName.description; + }; + + description = lib.mkOption { + default = "Davide Pasetto"; + type = lib.types.str; + description = config.users.users."".description; + }; + + hashedPassword = lib.mkOption { + default = ""; + type = lib.types.str; + description = config.users.users."".initialHashedPassword.description; + }; + + flatpak = lib.mkOption { + default = false; + type = lib.types.bool; + description = '' + Enable the flatpak engine for the user + ''; + }; + + }; + + config = lib.mkIf cfg.enable { + users.users.${cfg.userName} = { + description = cfg.description; + extraGroups = [ "users" "wheel" ]; + initialHashedPassword = cfg.hashedPassword; + isNormalUser = true; + isSystemUser = false; + shell = pkgs.zsh; + uid = 1000; + + packages = with pkgs; lib.mkIf cfg.flatpak [ flatpak gnome.gnome-software ]; + }; + + console.keyMap = "it"; + + programs.zsh.enable = true; + + }; + +} diff --git a/modules/networking/avahi.nix b/modules/networking/avahi.nix new file mode 100644 index 0000000..29e6d52 --- /dev/null +++ b/modules/networking/avahi.nix @@ -0,0 +1,18 @@ +{ lib, config, pkgs, ... }: +let + cfg = config.avahi; +in +{ + options.avahi = { + enable = lib.mkEnableOption "Enable Avahi and apply customization module"; + }; + + config = lib.mkIf cfg.enable { + services.avahi = { + enable = true; + nssmdns = true; + openFirewall = true; + }; + }; + +} diff --git a/modules/networking/default.nix b/modules/networking/default.nix new file mode 100644 index 0000000..ec009b6 --- /dev/null +++ b/modules/networking/default.nix @@ -0,0 +1,6 @@ +{ + imports = [ + ./avahi.nix + ./tailscale.nix + ]; +} diff --git a/modules/networking/tailscale.nix b/modules/networking/tailscale.nix new file mode 100644 index 0000000..58576ee --- /dev/null +++ b/modules/networking/tailscale.nix @@ -0,0 +1,39 @@ +{ lib, config, pkgs, ... }: +let + cfg = config.tailscale; +in +{ + options.tailscale = { + enable = lib.mkEnableOption "Enable Tailscale module"; + + authKeyFile = lib.mkOption { + default = ""; + type = config.services.tailscale.authKeyFile.type; + description = config.services.tailscale.authKeyFile.description; + }; + + exitNode = lib.mkOption { + default = ""; + description = '' + The tailscale IP of the optional exit node. + ''; + }; + + extraUpFlags = lib.mkOption { + default = ""; + type = config.services.tailscale.extraUpFlags.type; + description = config.services.tailscale.extraUpFlags.description; + }; + + }; + + config = lib.mkIf cfg.enable { + services.tailscale = { + enable = true; + authKeyFile = cfg.authKeyFile; + useRoutingFeatures = if cfg.exitNode == "" then "none" else "both"; + extraUpFlags = [ "--exit-node=${cfg.exitNode}" ] ++ cfg.extraUpFlags; + }; + }; + +} diff --git a/modules/services/default.nix b/modules/services/default.nix new file mode 100644 index 0000000..87b15f9 --- /dev/null +++ b/modules/services/default.nix @@ -0,0 +1,6 @@ +{ + imports = [ + ./download-pod.nix + ./nextcloud-podman.nix + ]; +} diff --git a/modules/services/download-pod-old.nix b/modules/services/download-pod-old.nix new file mode 100644 index 0000000..7ae2063 --- /dev/null +++ b/modules/services/download-pod-old.nix @@ -0,0 +1,139 @@ +{ lib, config, pkgs, ... }: +let + cfg = config.download-pod-old; +in +{ + options.download-pod = { + enable = lib.mkEnableOption "Enable download services module"; + + proxy = { + enable = lib.mkEnableOption "Enable proxy for the services"; + + hostName = lib.mkOption { + default = "example.com"; + type = lib.types.str; + description = '' + Top level hostname + ''; + }; + + serverName = lib.mkOption { + default = "localhost"; + type = lib.types.str; + description = '' + Server name where Caddy is + ''; + }; + + }; + + dataDir = lib.mkOption { + default = "/mnt/data"; + type = lib.types.str; + description = '' + Base download dir for stuff + ''; + }; + + }; + + config = lib.mkIf cfg.enable + { + podman.enable = true; + + systemd.services.pod-download = { + description = "Start podman 'download' pod"; + wants = [ "network-online.target" ]; + after = [ "network-online.target" ]; + requiredBy = [ + "podman-jackett.service" + "podman-radarr.service" + "podman-sabnzbd.service" + "podman-sonarr.service" + ]; + unitConfig = { + RequiresMountsFor = "/run/containers"; + }; + serviceConfig = { + Type = "oneshot"; + ExecStart = "-${pkgs.podman}/bin/podman pod create -p 9117:9117 -p 7878:7878 -p 8080:8080 -p 8989:8989 download"; + }; # -p 9117:9117 -p 7878:7878 -p 8080:8080 -p 8989:8989 + #--share cgroup,ipc,uts + path = [ pkgs.podman ]; + }; + + virtualisation.oci-containers.containers = { + jackett = { + image = "linuxserver/jackett"; + autoStart = true; + user = "1000:100"; + ports = [ "9117:9117" ]; + extraOptions = [ + "--init=true" + "--pod=download" + ]; + volumes = [ "jackett_config:/config" "${cfg.dataDir}:/data" ]; + }; + + radarr = { + image = "linuxserver/radarr"; + autoStart = true; + user = "1000:100"; + ports = [ "7878:7878" ]; + extraOptions = [ + "--init=true" + "--pod=download" + ]; + volumes = [ "radarr_config:/config" "${cfg.dataDir}:/data" ]; + }; + + sabnzbd = { + image = "linuxserver/sabnzbd"; + autoStart = true; + user = "1000:100"; + ports = [ "8080:8080" ]; + extraOptions = [ + "--init=true" + "--pod=download" + ]; + volumes = [ "sabnzbd_config:/config" "${cfg.dataDir}:/data" ]; + }; + + sonarr = { + image = "linuxserver/sonarr"; + autoStart = true; + user = "1000:100"; + ports = [ "8989:8989" ]; + extraOptions = [ + "--init=true" + "--pod=download" + ]; + volumes = [ "sonarr_config:/config" "${cfg.dataDir}:/data" ]; + }; + + }; + + services.caddy = lib.mkIf cfg.proxy.enable { + enable = true; + enableReload = false; + virtualHosts = { + "jackett.${cfg.proxy.hostName}".extraConfig = '' + reverse_proxy http://${cfg.proxy.serverName}:9117 + ''; + "radarr.${cfg.proxy.hostName}".extraConfig = '' + reverse_proxy http://${cfg.proxy.serverName}:7878 + ''; + "sabnzbd.${cfg.proxy.hostName}".extraConfig = '' + reverse_proxy http://${cfg.proxy.serverName}:8080 + ''; + "sonarr.${cfg.proxy.hostName}".extraConfig = '' + reverse_proxy http://${cfg.proxy.serverName}:8989 + ''; + }; + + }; + + + }; + +} diff --git a/modules/services/download-pod.nix b/modules/services/download-pod.nix new file mode 100644 index 0000000..0ed77eb --- /dev/null +++ b/modules/services/download-pod.nix @@ -0,0 +1,15 @@ +{ config, lib, ... }: +let + cfg = config.download-pod; +in +{ + options.download-pod = { + enable = lib.mkEnableOption "Enable the download searcher stack"; + }; + + config = lib.mkIf cfg.enable { + oci-containers.pods.download = { }; + }; + + +} diff --git a/modules/services/nextcloud-podman.nix b/modules/services/nextcloud-podman.nix new file mode 100644 index 0000000..ab49d02 --- /dev/null +++ b/modules/services/nextcloud-podman.nix @@ -0,0 +1,17 @@ +{ lib, config, pkgs, ... }: +let + cfg = config.nextcloud-pd; +in +{ + options.nextcloud-pd = { + enable = lib.mkEnableOption "Enable Nextcloud module"; + }; + + config = lib.mkIf cfg.enable { + podman.enable = true; + + virtualisation.oci-containers.containers = { }; + + }; + +} diff --git a/modules/virtualisation/default.nix b/modules/virtualisation/default.nix new file mode 100644 index 0000000..643e997 --- /dev/null +++ b/modules/virtualisation/default.nix @@ -0,0 +1,9 @@ +{ + imports = [ + ./docker.nix + ./libvirtd.nix + ./lxc.nix + ./podman.nix + ./oci-containers + ]; +} diff --git a/modules/virtualisation/docker.nix b/modules/virtualisation/docker.nix new file mode 100644 index 0000000..71998db --- /dev/null +++ b/modules/virtualisation/docker.nix @@ -0,0 +1,22 @@ +{ lib, config, pkgs, ... }: +let + cfg = config.docker; +in +{ + options.docker = { + enable = lib.mkEnableOption "Enable Docker module"; + }; + + config = lib.mkIf cfg.enable { + virtualisation = { + docker = { + storageDriver = "btrfs"; + rootless = { + enable = true; + setSocketVariable = true; + }; + }; + }; + }; + +} diff --git a/modules/virtualisation/libvirtd.nix b/modules/virtualisation/libvirtd.nix new file mode 100644 index 0000000..a8a58c5 --- /dev/null +++ b/modules/virtualisation/libvirtd.nix @@ -0,0 +1,16 @@ +{ lib, config, pkgs, ... }: +let + cfg = config.libvirtd; +in +{ + options.libvirtd = { + enable = lib.mkEnableOption "Enable libvirtd module"; + }; + + config = lib.mkIf cfg.enable { + virtualisation.libvirtd.enable = true; + programs.virt-manager.enable = true; + users.users.${config.mainUser.userName}.extraGroups = [ "libvirtd" ]; + }; + +} diff --git a/modules/virtualisation/lxc.nix b/modules/virtualisation/lxc.nix new file mode 100644 index 0000000..93a05e4 --- /dev/null +++ b/modules/virtualisation/lxc.nix @@ -0,0 +1,46 @@ +{ lib, config, pkgs, ... }: +let + cfg = config.lxc; +in +{ + options.lxc = { + enable = lib.mkEnableOption "Enable LXC module"; + }; + + config = lib.mkIf cfg.enable { + virtualisation = { + # Enable LXC containers + lxd = { + enable = true; + + # This turns on a few sysctl settings that the LXD documentation recommends + # for running in production. + recommendedSysctlSettings = true; + }; + + # This enables lxcfs, which is a FUSE fs that sets up some things so that + # things like /proc and cgroups work better in lxd containers. + # See https://linuxcontainers.org/lxcfs/introduction/ for more info. + # + # Also note that the lxcfs NixOS option says that in order to make use of + # lxcfs in the container, you need to include the following NixOS setting + # in the NixOS container guest configuration: + # + # virtualisation.lxc.defaultConfig = "lxc.include = ''${pkgs.lxcfs}/share/lxc/config/common.conf.d/00-lxcfs.conf"; + lxc.lxcfs.enable = true; + }; + + # ip forwarding is needed for NAT'ing to work. + boot.kernel.sysctl = { + "net.ipv4.conf.all.forwarding" = true; + "net.ipv4.conf.default.forwarding" = true; + }; + + # kernel module for forwarding to work + boot.kernelModules = [ "nf_nat_ftp" ]; + + users.users.${config.mainUser.userName}.extraGroups = [ "lxd" ]; + + }; + +} diff --git a/modules/virtualisation/oci-containers/default.nix b/modules/virtualisation/oci-containers/default.nix new file mode 100644 index 0000000..ea26ce8 --- /dev/null +++ b/modules/virtualisation/oci-containers/default.nix @@ -0,0 +1,122 @@ +# ```admonish note title="To do" +# Run podman containers run as a non-root user. +# ``` +# +# An abstraction over running containers as systemd units, enforcing some good +# practices: +# +# - Container DNS behaves the same under docker and podman. +# - Ports are exposed on `127.0.0.1`, rather than `0.0.0.0`. +# - Volumes are backed up by bind-mounts to the host filesystem. +# +# Switching between using docker or podman for the container runtime should be +# totally transparent. +# +# **Erase your darlings:** overrides the `volumeBaseDir`. +{ config, lib, pkgs, ... }: + +with lib; +let + mkPortDef = { host, inner }: "127.0.0.1:${toString host}:${toString inner}"; + + mkVolumeDef = container: { name, host, inner }: + if host != null + then "${host}:${inner}" + else "${cfg.volumeBaseDir}/${container.volumeSubDir}/${name}:${inner}"; + + shouldPreStart = _name: container: container.pullOnStart; + mkPreStart = name: container: nameValuePair "${cfg.backend}-${name}" { + preStart = if container.pullOnStart then "${cfg.backend} pull ${container.image}" else ""; + }; + + shouldNetworkService = _name: container: container.network != null; + mkNetworkService = _name: container: + let package = if cfg.backend == "docker" then pkgs.docker else pkgs.podman; + in nameValuePair "${cfg.backend}-net-${container.network}" { + description = "Manage the ${container.network} network for ${cfg.backend}"; + preStart = "${package}/bin/${cfg.backend} network rm ${container.network} || true"; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${package}/bin/${cfg.backend} network create -d bridge ${container.network}"; + ExecStop = "${package}/bin/${cfg.backend} network rm ${container.network}"; + RemainAfterExit = "yes"; + }; + }; + + mkPodService = name: pod: + let + package = if cfg.backend == "podman" then pkgs.podman else throw "mkPodService only supports podman"; + aliases = map (cn: "${name}-${cn}") (attrNames pod.containers); + ports = concatLists (catAttrs "ports" (attrValues pod.containers)); + in + nameValuePair "${cfg.backend}-pod-${name}" { + description = "Manage the ${name} pod for ${cfg.backend}"; + preStart = "${package}/bin/${cfg.backend} pod rm --force --ignore ${name} || true"; + serviceConfig = { + Type = "oneshot"; + ExecStart = + let args = map (n: "--network-alias=${n}") aliases ++ map (pd: "-p ${mkPortDef pd}") ports; + in "${package}/bin/${cfg.backend} pod create ${concatStringsSep " " args} ${name}"; + ExecStop = "${package}/bin/${cfg.backend} pod rm ${name}"; + RemainAfterExit = "yes"; + }; + }; + + mkContainer = _name: container: with container; + let + hasNetwork = container.network != null; + hasPod = container.pod != null; + in + { + inherit autoStart cmd environment environmentFiles image login; + dependsOn = + container.dependsOn ++ + (if hasNetwork then [ "net-${container.network}" ] else [ ]) ++ + (if hasPod then [ "pod-${container.pod}" ] else [ ]); + extraOptions = + container.extraOptions ++ + (if hasNetwork then [ "--network=${container.network}" ] else [ ]) ++ + (if hasPod then [ "--pod=${container.pod}" ] else [ ]); + /* ports are defined at the pod level */ + ports = if hasPod then [ ] else map mkPortDef ports; + volumes = map (mkVolumeDef container) volumes; + }; + + cfg = config.nixfiles.oci-containers; + + allContainers = + let + mkPodContainer = podName: pod: containerName: container: nameValuePair "${podName}-${containerName}" ( + container // + { + network = if cfg.backend == "docker" then podName else null; + pod = if cfg.backend == "docker" then null else podName; + volumeSubDir = pod.volumeSubDir; + } + ); + in + concatMapAttrs (podName: pod: mapAttrs' (mkPodContainer podName pod) pod.containers) cfg.pods; +in +{ + imports = [ + ./options.nix + ]; + + config = { + virtualisation.${cfg.backend} = { + enable = true; + autoPrune.enable = true; + }; + + virtualisation.oci-containers = { + backend = cfg.backend; + containers = mapAttrs mkContainer allContainers; + }; + + systemd.services = mkMerge [ + (mapAttrs' mkPreStart (filterAttrs shouldPreStart allContainers)) + (mapAttrs' mkNetworkService (filterAttrs shouldNetworkService allContainers)) + (if cfg.backend == "podman" then mapAttrs' mkPodService cfg.pods else { }) + ]; + }; +} diff --git a/modules/virtualisation/oci-containers/options.nix b/modules/virtualisation/oci-containers/options.nix new file mode 100644 index 0000000..38ef737 --- /dev/null +++ b/modules/virtualisation/oci-containers/options.nix @@ -0,0 +1,207 @@ +{ lib, ... }: + +with lib; +let + portOptions = { + host = mkOption { + type = types.int; + description = mdDoc '' + Host port (on 127.0.0.1) to expose the container port on. + ''; + }; + + inner = mkOption { + type = types.int; + description = mdDoc '' + The container port to expose to the hosti. + ''; + }; + }; + + volumeOptions = { + name = mkOption { + type = types.nullOr types.str; + default = null; + description = mdDoc '' + Name of the volume. This is equivalent to: + + ```nix + host = "''${volumeBaseDir}/''${volumeSubDir}/''${name}"; + ``` + + This option c.logonflicts with `''${host}`. + ''; + }; + + host = mkOption { + type = types.nullOr types.str; + default = null; + description = mdDoc '' + Directory on the host to bind-mount into the container. + + This option conflicts with `''${name}`. + ''; + }; + + inner = mkOption { + type = types.str; + description = mdDoc '' + Directory in the container to mount the volume to. + ''; + }; + }; + + containerOptions = { + /* regular oci-containers */ + autoStart = mkOption { + type = types.bool; + default = true; + description = mdDoc '' + Start the container automatically on boot. + ''; + }; + + cmd = mkOption { + type = types.listOf types.str; + default = [ ]; + description = mdDoc '' + Command-line arguments to pass to the container image's entrypoint. + ''; + }; + + dependsOn = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "concourse-db" ]; + description = mdDoc '' + Other containers that this one depends on, in `''${pod}-''${name}` + format. + ''; + }; + + environment = mkOption { + type = types.attrsOf types.str; + default = { }; + description = mdDoc '' + Environment variables to set for this container. + ''; + }; + + environmentFiles = mkOption { + type = types.listOf types.path; + default = [ ]; + description = mdDoc '' + List of environment files for this container. + ''; + }; + + extraOptions = mkOption { + type = types.listOf types.str; + default = [ ]; + description = mdDoc '' + Extra options to pass to `docker run` / `podman run`. + ''; + }; + + image = mkOption { + type = types.str; + description = mdDoc '' + Container image to run. + ''; + }; + + login = { + username = mkOption { + type = types.nullOr types.str; + default = null; + description = mdDoc '' + Username for the container registry. + ''; + }; + passwordFile = mkOption { + type = types.nullOr types.str; + default = null; + description = mdDoc '' + File containing the password for the container registry. + ''; + }; + registry = mkOption { + type = types.nullOr types.str; + default = null; + description = mdDoc '' + Container registry to authenticate with. + ''; + }; + }; + + /* changed */ + ports = mkOption { + type = types.listOf (types.submodule { options = portOptions; }); + default = [ ]; + description = mdDoc '' + List of ports to expose. + ''; + }; + + volumes = mkOption { + type = types.listOf (types.submodule { options = volumeOptions; }); + default = [ ]; + description = mdDoc '' + List of volume definitions. + ''; + }; + + /* new options */ + pullOnStart = mkOption { + type = types.bool; + default = true; + description = mdDoc '' + Pull the container image when starting (useful for `:latest` images). + ''; + }; + }; +in +{ + options.nixfiles.oci-containers = { + backend = mkOption { + type = types.enum [ "docker" "podman" ]; + default = "docker"; + description = mdDoc '' + The container runtime. + ''; + }; + + pods = mkOption { + type = types.attrsOf (types.submodule ({ name, ... }: { + options = { + containers = mkOption { + type = types.attrsOf (types.submodule { options = containerOptions; }); + default = { }; + description = mdDoc '' + Attrset of container definitions. + ''; + }; + volumeSubDir = mkOption { + type = types.str; + default = name; + description = mdDoc '' + Subdirectory of the `''${volumeBaseDir}` to store bind-mounts + under. + ''; + }; + }; + })); + default = { }; + description = mdDoc '' + Attrset of pod definitions. + ''; + }; + + volumeBaseDir = mkOption { + type = types.str; + description = mdDoc '' + Directory to store volume bind-mounts under. + ''; + }; + }; +} diff --git a/modules/virtualisation/podman.nix b/modules/virtualisation/podman.nix new file mode 100644 index 0000000..ac466dc --- /dev/null +++ b/modules/virtualisation/podman.nix @@ -0,0 +1,29 @@ +{ lib, config, pkgs, ... }: +let + cfg = config.podman; +in +{ + options.podman = { + enable = lib.mkEnableOption "Enable Podman module"; + }; + + config = lib.mkIf cfg.enable { + virtualisation.podman = { + enable = true; + + # Depending on the host filesystem + # extraPackages = [ pkgs.zfs ]; + }; + + # Depending on the host filesystem + # virtualisation.containers.storage.settings = { + # storage = { + # driver = "zfs"; + # graphroot = "/var/lib/containers/storage"; + # runroot = "/run/containers/storage"; + # }; + # }; + + }; + +}