box zfs
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
# Helm Environment Overview
|
||||
|
||||
## Box NAS Server (`box` / `mossnet.lan`)
|
||||
|
||||
### Hardware
|
||||
- NVMe boot drive (LUKS encrypted)
|
||||
- 3x 4TB drives in ZFS RAIDZ1 pool (`tank`) - ~7.14TB usable
|
||||
|
||||
### ZFS Datasets
|
||||
| Dataset | Mountpoint | Purpose |
|
||||
|---------|------------|---------|
|
||||
| `tank/data/media` | `/tank/media` | Media library (music, photos, tv, movies) |
|
||||
| `tank/data/books` | `/tank/books` | Calibre library |
|
||||
| `tank/data/podcasts` | `/tank/podcasts` | Podcast storage |
|
||||
| `tank/data/new-music` | `/tank/new-music` | Incoming music from seedbox |
|
||||
| `tank/data/backup` | `/tank/backup` | PostgreSQL backups |
|
||||
| `tank/data/archive` | `/tank/archive` | Old data (memories, old-home-dirs, etc.) |
|
||||
|
||||
### Services
|
||||
- **Immich** - Photo management (`/tank/media/photos`)
|
||||
- **Gonic** - Music streaming (`/tank/media/music`)
|
||||
- **Calibre-server/Calibre-web** - Ebook management (`/tank/books`)
|
||||
- **Jellyfin** - Media streaming
|
||||
- **Lidarr** - Music management (runs as `headphones:audio`)
|
||||
- **Radicale** - CalDAV/CardDAV
|
||||
- **Syncthing** - File sync
|
||||
- **PostgreSQL** - Database
|
||||
- **Taskserver** - Taskwarrior sync
|
||||
|
||||
---
|
||||
|
||||
## Repository Structure
|
||||
|
||||
```
|
||||
helm/
|
||||
├── flake.nix # Main flake - defines all hosts
|
||||
├── hosts/
|
||||
│ ├── box/
|
||||
│ │ ├── default.nix # Box host config, imports profiles
|
||||
│ │ ├── configuration.nix # Hardware/boot config
|
||||
│ │ └── disko.nix # Disk/ZFS layout
|
||||
│ ├── profiles/ # NixOS service profiles
|
||||
│ │ ├── sync/music/ # get-music-sync service
|
||||
│ │ ├── headphones/ # Lidarr config
|
||||
│ │ ├── jellyfin/
|
||||
│ │ ├── monitoring/
|
||||
│ │ └── ...
|
||||
├── home/
|
||||
│ ├── dev/
|
||||
│ │ └── default.nix # Home-manager config for box
|
||||
│ └── profiles/
|
||||
│ ├── beets/ # Beets music library config
|
||||
│ ├── cli/
|
||||
│ ├── nvim/
|
||||
│ ├── git/
|
||||
│ └── opencode/
|
||||
├── secrets/ # Agenix encrypted secrets
|
||||
└── modules/ # Custom NixOS modules
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Deployment
|
||||
|
||||
```bash
|
||||
# Deploy to a host
|
||||
deploy .#box
|
||||
deploy .#curve
|
||||
deploy .#helix
|
||||
deploy .#lituus
|
||||
|
||||
# SSH access to box
|
||||
ssh anish@mossnet.lan
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Users/Groups
|
||||
|
||||
| User | Group | Purpose |
|
||||
|------|-------|---------|
|
||||
| `anish` | `users`, `wheel`, `audio`, `video`, `docker` | Primary user |
|
||||
| `headphones` | `audio` | Lidarr service |
|
||||
| `gonic` | `audio` | Gonic music streaming |
|
||||
| `immich` | `immich` | Photo management |
|
||||
| `calibre-server` | `calibre-server` | Ebook server |
|
||||
|
||||
---
|
||||
|
||||
## Hosts
|
||||
|
||||
| Host | Description |
|
||||
|------|-------------|
|
||||
| `box` | NAS server (mossnet.lan) |
|
||||
| `curve` | Workstation |
|
||||
| `helix` | Workstation |
|
||||
| `lituus` | VPS/Server |
|
||||
Generated
+5
-5
@@ -737,15 +737,15 @@
|
||||
"treefmt-nix": "treefmt-nix_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1768359433,
|
||||
"narHash": "sha256-e/6qI81VBJo0lAQsyUG+2jMsL0q3YLz88NZoZOCVFu8=",
|
||||
"owner": "Chickensoupwithrice",
|
||||
"lastModified": 1768434130,
|
||||
"narHash": "sha256-4rBBs7spDuimvUcL3egp2Zh94Lk8pf00VsjkOs59h7E=",
|
||||
"owner": "numtide",
|
||||
"repo": "llm-agents.nix",
|
||||
"rev": "596bf03f14e9a54654473a1666b3b274bbc5939e",
|
||||
"rev": "d0ed3ef68a04b5bd127fecd405baf803eea29c29",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "Chickensoupwithrice",
|
||||
"owner": "numtide",
|
||||
"repo": "llm-agents.nix",
|
||||
"type": "github"
|
||||
}
|
||||
|
||||
@@ -75,8 +75,7 @@
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
# LLM Agents (using fork until chainlink PR is merged)
|
||||
llm-agents.url = "github:Chickensoupwithrice/llm-agents.nix";
|
||||
llm-agents.url = "github:numtide/llm-agents.nix";
|
||||
|
||||
# Others
|
||||
nur.url = "github:nix-community/NUR";
|
||||
@@ -361,6 +360,7 @@
|
||||
pkgs = nixpkgsFor.${system};
|
||||
modules = [
|
||||
./hosts/box
|
||||
disko.nixosModules.disko
|
||||
agenix.nixosModules.age
|
||||
self.nixosModules.backup
|
||||
self.nixosModules.wireguard
|
||||
@@ -368,7 +368,7 @@
|
||||
self.nixosModules.gpodder2go
|
||||
self.nixosModules.wallabag
|
||||
self.nixosModules.ulogger-server
|
||||
grasp.nixosModule
|
||||
# grasp.nixosModule # Disabled for initial install - private repo
|
||||
home-manager.nixosModules.home-manager
|
||||
{
|
||||
nix.registry.nixpkgs.flake = nixpkgs;
|
||||
|
||||
+15
-3
@@ -1,5 +1,17 @@
|
||||
{ self, pkgs, inputs, ... }: {
|
||||
imports =
|
||||
[ ../profiles/cli ../profiles/nvim ../profiles/direnv ../profiles/git ../profiles/opencode ];
|
||||
{
|
||||
self,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
imports = [
|
||||
../profiles/cli
|
||||
../profiles/nvim
|
||||
../profiles/direnv
|
||||
../profiles/git
|
||||
../profiles/opencode
|
||||
../profiles/beets
|
||||
];
|
||||
home.stateVersion = "22.05";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
programs.beets = {
|
||||
enable = true;
|
||||
package = pkgs.beets.override {
|
||||
pluginOverrides = {
|
||||
fetchart.enable = true;
|
||||
embedart.enable = true;
|
||||
lastgenre.enable = true;
|
||||
duplicates.enable = true;
|
||||
missing.enable = true;
|
||||
};
|
||||
};
|
||||
settings = {
|
||||
directory = "/tank/media/music";
|
||||
library = "/home/anish/.local/share/beets/library.db";
|
||||
|
||||
import = {
|
||||
move = true; # Move files from new-music to library
|
||||
write = true; # Write tags to files
|
||||
log = "/tank/new-music/beets-import.log";
|
||||
incremental = true; # Skip already-imported directories
|
||||
};
|
||||
|
||||
# Path format for organizing music
|
||||
paths = {
|
||||
default = "$albumartist/$album%aunique{}/$track $title";
|
||||
singleton = "Non-Album/$artist/$title";
|
||||
comp = "Compilations/$album%aunique{}/$track $title";
|
||||
};
|
||||
|
||||
plugins = [
|
||||
"fetchart"
|
||||
"embedart"
|
||||
"lastgenre"
|
||||
"duplicates"
|
||||
"missing"
|
||||
];
|
||||
|
||||
fetchart = {
|
||||
auto = true;
|
||||
};
|
||||
|
||||
embedart = {
|
||||
auto = true;
|
||||
};
|
||||
|
||||
lastgenre = {
|
||||
auto = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
+44
-54
@@ -1,79 +1,73 @@
|
||||
# Edit this configuration file to define what should be installed on
|
||||
# your system. Help is available in the configuration.nix(5) man page
|
||||
# and in the NixOS manual (accessible by running ‘nixos-help’).
|
||||
# and in the NixOS manual (accessible by running 'nixos-help').
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
{
|
||||
imports =
|
||||
[
|
||||
imports = [
|
||||
# Include the results of the hardware scan.
|
||||
./hardware-configuration.nix
|
||||
./disko.nix
|
||||
];
|
||||
|
||||
# No systemd emergency mode (can't reliably be accessed over SSH)
|
||||
systemd.enableEmergencyMode = false;
|
||||
|
||||
# Use the GRUB 2 boot loader.
|
||||
boot.loader.efi.canTouchEfiVariables = false;
|
||||
boot.loader.efi.efiSysMountPoint = "/boot/efi";
|
||||
boot.loader.grub = {
|
||||
# ZFS requires a hostId
|
||||
networking.hostId = "bb7d707a";
|
||||
|
||||
# Boot configuration for LUKS + ZFS
|
||||
boot.loader.efi.canTouchEfiVariables = true;
|
||||
boot.loader.efi.efiSysMountPoint = "/boot";
|
||||
boot.loader.systemd-boot.enable = true;
|
||||
|
||||
# ZFS support
|
||||
boot.supportedFilesystems = [ "zfs" ];
|
||||
boot.zfs = {
|
||||
requestEncryptionCredentials = [ "tank" ]; # Load key for tank pool
|
||||
forceImportRoot = false;
|
||||
};
|
||||
|
||||
# ZFS services
|
||||
services.zfs = {
|
||||
autoScrub = {
|
||||
enable = true;
|
||||
device = "nodev";
|
||||
efiSupport = true;
|
||||
enableCryptodisk = true;
|
||||
efiInstallAsRemovable = true;
|
||||
interval = "weekly";
|
||||
};
|
||||
|
||||
boot.initrd.secrets = {
|
||||
"/keyfile0.bin" = /etc/secrets/initrd/keyfile0.bin;
|
||||
"/keyfile1.bin" = /etc/secrets/initrd/keyfile1.bin;
|
||||
};
|
||||
|
||||
# Remote SSH unlock
|
||||
boot.initrd.network.enable = true;
|
||||
boot.initrd.network.ssh = {
|
||||
autoSnapshot = {
|
||||
enable = true;
|
||||
port = 22;
|
||||
authorizedKeys = [ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDM0Zvei46x/yZl/IeBCq6+IYQQ0avulzVyBysF9cPigZMCybWRV7IEU+E3k9t6JrbdbdGfJkcZIWmsWDdKS8W8mBnZpVoT0ffLynu8JQ/TKdGm4Qv6bgUeKNrGsNv0ZPs2CDaGSLj0oJfRF7Ko10tcLP0vW+yujrh+y6TH/vVzJioaV4TGvtCUpn+wEQah9ROwPQLUUofsSWdnRsDJ/gp37zXWs4l5wyjSKtP3O9RZUP7kBekbSqEgSXiTk0oUQSVqIWl9NDiP6onk/gSOjXsR/JPqsSN/XI/c/yj6gyY0f51Ru2D7iBxuMJIJcWV+rU6coIj+ULcQWLzt/7TI8jq5AOOzI/ll4zbL24Eo84Rz+TP9tvMMhDZ0VaMN22AJ8qQEjc5P09tWKsX7Jg39XelyV1jHXncE4yvIE9F4RSCHzWCeKeXakizQNuzSaxTxIExRFYHjNW5bR6+3MTGwVrEIXU+qML+0yFTR86MT+tdY5AreAJQLwbog79O1NupeXJE= anish@curve " ];
|
||||
hostKeys = [ "/etc/secrets/initrd/ssh_host_ed25519_key" ]; # create this file with `ssh-keygen -t ed25519 -N "" -f /etc/secrets/initrd/ssh_host_ed25519_key`
|
||||
frequent = 4; # 15-minute snapshots
|
||||
hourly = 24;
|
||||
daily = 7;
|
||||
weekly = 4;
|
||||
monthly = 12;
|
||||
};
|
||||
boot.initrd.availableKernelModules = [ "igc" "iwlwifi" ];
|
||||
|
||||
boot.initrd.luks.devices = {
|
||||
"root" = {
|
||||
#name = "root";
|
||||
device = "/dev/disk/by-uuid/f37f3222-47d7-42d8-b400-363320a31853"; # UUID for /dev/nvme01np2
|
||||
preLVM = true;
|
||||
allowDiscards = true;
|
||||
keyFile = "/keyfile0.bin";
|
||||
trim.enable = true;
|
||||
};
|
||||
};
|
||||
|
||||
# Data mount
|
||||
# fileSystems."/data" = {
|
||||
# device = "/dev/disk/by-uuid/3276a297-9ee4-4998-b262-1ed100366c06"; # UUID for /dev/mapper/crypted-data
|
||||
# encrypted = {
|
||||
# enable = true;
|
||||
# label = "crypted-data";
|
||||
# blkDev = "/dev/disk/by-uuid/8a317bf4-fe13-4334-a6df-5fe5a5048b5e"; # UUID for /dev/sda1
|
||||
# keyFile = "/keyfile1.bin";
|
||||
# };
|
||||
# };
|
||||
|
||||
networking.interfaces.enp2s0 = {
|
||||
ipv4.addresses = [{
|
||||
ipv4.addresses = [
|
||||
{
|
||||
address = "192.168.1.240";
|
||||
prefixLength = 24;
|
||||
}];
|
||||
ipv6.addresses = [{
|
||||
}
|
||||
];
|
||||
ipv6.addresses = [
|
||||
{
|
||||
address = "fd7d:587a:4300:1::240";
|
||||
prefixLength = 64;
|
||||
}];
|
||||
ipv4.routes = [{ address = "192.168.1.0"; prefixLength = 24; via = "192.168.1.1"; }];
|
||||
}
|
||||
];
|
||||
ipv4.routes = [
|
||||
{
|
||||
address = "192.168.1.0";
|
||||
prefixLength = 24;
|
||||
via = "192.168.1.1";
|
||||
}
|
||||
];
|
||||
useDHCP = false;
|
||||
};
|
||||
#networking.nameservers = [ "172.16.11.240" ];
|
||||
networking.nameservers = [ "192.168.1.1" ];
|
||||
networking.defaultGateway = {
|
||||
address = "192.168.1.1";
|
||||
@@ -81,11 +75,8 @@
|
||||
};
|
||||
|
||||
networking.hostName = "box"; # Define your hostname.
|
||||
# networking.wireless.enable = true; # Enables wireless support via wpa_supplicant.
|
||||
|
||||
# The global useDHCP flag is deprecated, therefore explicitly set to false here.
|
||||
# Per-interface useDHCP will be mandatory in the future, so this generated config
|
||||
# replicates the default behaviour.
|
||||
networking.useDHCP = false;
|
||||
networking.interfaces.wlp3s0.useDHCP = true;
|
||||
|
||||
@@ -103,4 +94,3 @@
|
||||
system.stateVersion = "19.09"; # Did you read the comment?
|
||||
|
||||
}
|
||||
|
||||
|
||||
+30
-24
@@ -1,4 +1,5 @@
|
||||
{ self, pkgs, ... }: {
|
||||
{ self, pkgs, ... }:
|
||||
{
|
||||
imports = [
|
||||
./configuration.nix
|
||||
../profiles/core
|
||||
@@ -6,7 +7,7 @@
|
||||
../profiles/taskd
|
||||
../profiles/shaarli
|
||||
../profiles/dns
|
||||
# ../profiles/monitoring
|
||||
../profiles/monitoring
|
||||
../profiles/nfs
|
||||
../profiles/gonic
|
||||
../profiles/headphones
|
||||
@@ -19,43 +20,45 @@
|
||||
../profiles/finance
|
||||
../profiles/sync/website
|
||||
../profiles/sync/music
|
||||
../profiles/grasp
|
||||
# ../profiles/archivebox
|
||||
# ../profiles/woodpecker-agent
|
||||
# ../profiles/jellyfin
|
||||
# ../profiles/grasp # private repo - disabled
|
||||
# ../profiles/archivebox # requires insecure django - fix in flake.nix permittedInsecurePackages
|
||||
../profiles/woodpecker-agent
|
||||
../profiles/jellyfin
|
||||
../profiles/ulogger-server
|
||||
../profiles/immich
|
||||
../profiles/jacket
|
||||
../profiles/gpodder
|
||||
../profiles/transmission
|
||||
../profiles/raven
|
||||
#../profiles/postgres_upgrade_script
|
||||
# ../profiles/postgres_upgrade_script # one-time use
|
||||
];
|
||||
|
||||
# Backups
|
||||
age.secrets.borg-password.file = "${self}/secrets/borg-password.age";
|
||||
services.postgresqlBackup = {
|
||||
enable = true;
|
||||
databases = [ "wallabag" "immich" "ulogger" ];
|
||||
location = "/var/backup/postgresql";
|
||||
databases = [
|
||||
"wallabag"
|
||||
"immich"
|
||||
"ulogger"
|
||||
];
|
||||
location = "/tank/backup/postgresql";
|
||||
};
|
||||
mossnet.backup = {
|
||||
enable = true;
|
||||
name = "mossnet";
|
||||
paths = [
|
||||
"/var/lib/taskserver" # taskwarrior
|
||||
"/var/www/shaarli-config" # sharli
|
||||
"/var/backup/postgresql" # wallabag
|
||||
"/var/www/shaarli-config" # shaarli
|
||||
"/tank/backup/postgresql" # postgresql backups
|
||||
"/var/lib/radicale" # radicale
|
||||
"/home/anish/usr/drawing" # syncthing
|
||||
"/data/books" # calibre-web
|
||||
# "/home/anish/usr/nonfiction" # syncthing
|
||||
"/tank/syncthing/drawing" # syncthing
|
||||
"/tank/books" # calibre-web
|
||||
"/home/anish/usr/finance" # beancount
|
||||
"/mnt/two/postgres" # sealight postgres backups TODO remove once moved to capsul
|
||||
"/mnt/two/photos"
|
||||
"/mnt/two/music"
|
||||
"/tank/postgres" # postgres data
|
||||
"/tank/media/photos"
|
||||
"/tank/media/music"
|
||||
];
|
||||
# seafile
|
||||
};
|
||||
|
||||
# opencode-manager ports
|
||||
@@ -65,18 +68,21 @@
|
||||
5173 # opencode-manager frontend
|
||||
5551 # opencode server
|
||||
];
|
||||
allowedTCPPortRanges = [{
|
||||
allowedTCPPortRanges = [
|
||||
{
|
||||
from = 7000;
|
||||
to = 9000;
|
||||
}]; # ports for testing user changes
|
||||
}
|
||||
]; # ports for testing user changes
|
||||
};
|
||||
|
||||
environment.systemPackages = with pkgs; [ lm_sensors ];
|
||||
hardware.fancontrol = {
|
||||
enable = false;
|
||||
config = '''';
|
||||
};
|
||||
# hardware.fancontrol = {
|
||||
# enable = false;
|
||||
# config = '''';
|
||||
# };
|
||||
|
||||
# Secrets
|
||||
age.secrets.box-wg.file = "${self}/secrets/box-wg.age";
|
||||
age.secrets.box-wg.owner = "anish";
|
||||
age.secrets.borg-key.file = "${self}/secrets/borg-key.age";
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
# Disko configuration for box NAS
|
||||
# NVMe boot drive with LUKS + 3x 4TB ZFS RAIDZ1 pool (~8TB usable)
|
||||
# Unified encryption: LUKS passphrase unlocks root, ZFS uses keyfile inside encrypted root
|
||||
#
|
||||
# NOTE: Using RAIDZ1 (3 drives) temporarily due to DOA drive. Can migrate to RAIDZ2
|
||||
# with 4 drives later by creating a new pool and copying data.
|
||||
#
|
||||
# Installation steps:
|
||||
#
|
||||
# 1. Generate the ZFS keyfile and LUKS password:
|
||||
# dd if=/dev/urandom of=/tmp/tank.key bs=32 count=1
|
||||
# echo -n "your-luks-password" > /tmp/luks-password
|
||||
#
|
||||
# 2. Run disko-install:
|
||||
# sudo nix run 'github:nix-community/disko/latest#disko-install' -- \
|
||||
# --flake ~/helm#box \
|
||||
# --disk nvme /dev/disk/by-id/nvme-CT500P310SSD8_2544543B87C2 \
|
||||
# --disk zfs1 /dev/disk/by-id/ata-WDC_WD40EFPX-68C6CN0_WD-WX32D954A2J7 \
|
||||
# --disk zfs2 /dev/disk/by-id/ata-WDC_WD40EFPX-68C6CN0_WD-WX32D95FVZVL \
|
||||
# --disk zfs3 /dev/disk/by-id/ata-WDC_WD40EFPX-68C6CN0_WD-WX42D95M807R
|
||||
#
|
||||
# 3. Copy the keyfile and update keylocation:
|
||||
# sudo mkdir -p /mnt/etc/zfs
|
||||
# sudo cp /tmp/tank.key /mnt/etc/zfs/tank.key
|
||||
# sudo chmod 000 /mnt/etc/zfs/tank.key
|
||||
# sudo zfs set keylocation=file:///etc/zfs/tank.key tank
|
||||
{
|
||||
disko.devices = {
|
||||
disk = {
|
||||
# Boot drive - 500GB NVMe with LUKS encryption
|
||||
nvme = {
|
||||
type = "disk";
|
||||
device = "/dev/disk/by-id/nvme-placeholder"; # Override with --disk nvme /dev/disk/by-id/...
|
||||
content = {
|
||||
type = "gpt";
|
||||
partitions = {
|
||||
ESP = {
|
||||
size = "512M";
|
||||
type = "EF00";
|
||||
content = {
|
||||
type = "filesystem";
|
||||
format = "vfat";
|
||||
mountpoint = "/boot";
|
||||
mountOptions = [ "umask=0077" ];
|
||||
};
|
||||
};
|
||||
luks = {
|
||||
size = "100%";
|
||||
content = {
|
||||
type = "luks";
|
||||
name = "cryptroot";
|
||||
settings = {
|
||||
allowDiscards = true;
|
||||
};
|
||||
# Passphrase will be prompted during boot
|
||||
passwordFile = "/tmp/luks-password"; # Only used during install, set this before running disko
|
||||
content = {
|
||||
type = "filesystem";
|
||||
format = "ext4";
|
||||
mountpoint = "/";
|
||||
mountOptions = [ "noatime" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# ZFS pool drives - 3x 4TB in RAIDZ1
|
||||
zfs1 = {
|
||||
type = "disk";
|
||||
device = "/dev/disk/by-id/placeholder-zfs1"; # Override with --disk zfs1 /dev/disk/by-id/...
|
||||
content = {
|
||||
type = "gpt";
|
||||
partitions = {
|
||||
zfs = {
|
||||
size = "100%";
|
||||
content = {
|
||||
type = "zfs";
|
||||
pool = "tank";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
zfs2 = {
|
||||
type = "disk";
|
||||
device = "/dev/disk/by-id/placeholder-zfs2";
|
||||
content = {
|
||||
type = "gpt";
|
||||
partitions = {
|
||||
zfs = {
|
||||
size = "100%";
|
||||
content = {
|
||||
type = "zfs";
|
||||
pool = "tank";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
zfs3 = {
|
||||
type = "disk";
|
||||
device = "/dev/disk/by-id/placeholder-zfs3";
|
||||
content = {
|
||||
type = "gpt";
|
||||
partitions = {
|
||||
zfs = {
|
||||
size = "100%";
|
||||
content = {
|
||||
type = "zfs";
|
||||
pool = "tank";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
zpool = {
|
||||
tank = {
|
||||
type = "zpool";
|
||||
mode = "raidz1";
|
||||
options = {
|
||||
ashift = "12";
|
||||
cachefile = "none"; # Needed for disko
|
||||
};
|
||||
rootFsOptions = {
|
||||
compression = "lz4";
|
||||
atime = "off";
|
||||
xattr = "sa";
|
||||
acltype = "posixacl";
|
||||
# ZFS native encryption
|
||||
# During install: keyfile at /tmp/tank.key
|
||||
# After install: install-box.sh copies to /etc/zfs/tank.key and updates keylocation
|
||||
encryption = "aes-256-gcm";
|
||||
keyformat = "raw";
|
||||
keylocation = "file:///tmp/tank.key";
|
||||
"com.sun:auto-snapshot" = "false";
|
||||
};
|
||||
# Don't mount the pool root directly
|
||||
mountpoint = null;
|
||||
|
||||
datasets = {
|
||||
# /nix is on the NVMe ext4 root, not on ZFS
|
||||
# This simplifies boot dependencies - ZFS is purely for data storage
|
||||
|
||||
# Parent dataset for all data - inherits encryption
|
||||
data = {
|
||||
type = "zfs_fs";
|
||||
options.mountpoint = "none";
|
||||
};
|
||||
|
||||
# Media datasets
|
||||
"data/media" = {
|
||||
type = "zfs_fs";
|
||||
options.mountpoint = "none";
|
||||
};
|
||||
"data/media/music" = {
|
||||
type = "zfs_fs";
|
||||
mountpoint = "/tank/media/music";
|
||||
options.recordsize = "1M"; # Large files benefit from larger recordsize
|
||||
};
|
||||
"data/media/photos" = {
|
||||
type = "zfs_fs";
|
||||
mountpoint = "/tank/media/photos";
|
||||
options.recordsize = "1M";
|
||||
};
|
||||
"data/media/movies" = {
|
||||
type = "zfs_fs";
|
||||
mountpoint = "/tank/media/movies";
|
||||
options.recordsize = "1M";
|
||||
};
|
||||
"data/media/tv" = {
|
||||
type = "zfs_fs";
|
||||
mountpoint = "/tank/media/tv";
|
||||
options.recordsize = "1M";
|
||||
};
|
||||
|
||||
# Other data
|
||||
"data/books" = {
|
||||
type = "zfs_fs";
|
||||
mountpoint = "/tank/books";
|
||||
options."com.sun:auto-snapshot" = "true";
|
||||
};
|
||||
"data/podcasts" = {
|
||||
type = "zfs_fs";
|
||||
mountpoint = "/tank/podcasts";
|
||||
};
|
||||
"data/postgres" = {
|
||||
type = "zfs_fs";
|
||||
mountpoint = "/tank/postgres";
|
||||
options = {
|
||||
recordsize = "16K"; # Better for databases
|
||||
"com.sun:auto-snapshot" = "true";
|
||||
};
|
||||
};
|
||||
"data/syncthing" = {
|
||||
type = "zfs_fs";
|
||||
options.mountpoint = "none";
|
||||
options."com.sun:auto-snapshot" = "true";
|
||||
};
|
||||
"data/syncthing/drawing" = {
|
||||
type = "zfs_fs";
|
||||
mountpoint = "/tank/syncthing/drawing";
|
||||
};
|
||||
"data/backup" = {
|
||||
type = "zfs_fs";
|
||||
mountpoint = "/tank/backup";
|
||||
options."com.sun:auto-snapshot" = "true";
|
||||
};
|
||||
"data/ftp" = {
|
||||
type = "zfs_fs";
|
||||
mountpoint = "/tank/ftp";
|
||||
};
|
||||
"data/cache" = {
|
||||
type = "zfs_fs";
|
||||
mountpoint = "/tank/cache";
|
||||
options."com.sun:auto-snapshot" = "false";
|
||||
};
|
||||
"data/playlists" = {
|
||||
type = "zfs_fs";
|
||||
mountpoint = "/tank/playlists";
|
||||
};
|
||||
"data/new-music" = {
|
||||
type = "zfs_fs";
|
||||
mountpoint = "/tank/new-music";
|
||||
};
|
||||
"data/archive" = {
|
||||
type = "zfs_fs";
|
||||
mountpoint = "/tank/archive";
|
||||
options."com.sun:auto-snapshot" = "true";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,66 +1,34 @@
|
||||
# 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, pkgs, modulesPath, ... }:
|
||||
# Hardware configuration for box NAS
|
||||
# Filesystem mounts are handled by disko.nix
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
modulesPath,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
imports =
|
||||
[
|
||||
imports = [
|
||||
(modulesPath + "/installer/scan/not-detected.nix")
|
||||
];
|
||||
|
||||
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "thunderbolt" "uas" "usb_storage" "sd_mod" ];
|
||||
boot.initrd.kernelModules = [ "dm-snapshot" ];
|
||||
boot.initrd.availableKernelModules = [
|
||||
"xhci_pci"
|
||||
"ahci"
|
||||
"nvme"
|
||||
"thunderbolt"
|
||||
"uas"
|
||||
"usb_storage"
|
||||
"sd_mod"
|
||||
];
|
||||
boot.initrd.kernelModules = [ ];
|
||||
boot.kernelModules = [ "kvm-intel" ];
|
||||
boot.extraModulePackages = [ ];
|
||||
|
||||
fileSystems."/" =
|
||||
{
|
||||
device = "/dev/disk/by-uuid/ade0752d-84d3-4e39-865b-9027ba2d5c67";
|
||||
fsType = "ext4";
|
||||
};
|
||||
# Filesystems are managed by disko - do not define them here
|
||||
|
||||
fileSystems."/boot/efi" =
|
||||
{
|
||||
device = "/dev/disk/by-uuid/1715-278E";
|
||||
fsType = "vfat";
|
||||
};
|
||||
|
||||
fileSystems."/mnt/one" =
|
||||
{
|
||||
device = "/dev/disk/by-uuid/0f857c6e-509d-436f-9e78-bc25f1b0d23b";
|
||||
fsType = "ext4";
|
||||
options = [
|
||||
"noatime"
|
||||
"nodiratime"
|
||||
"nofail"
|
||||
];
|
||||
};
|
||||
|
||||
fileSystems."/mnt/two" =
|
||||
{
|
||||
device = "/dev/disk/by-uuid/5bc894bf-ed87-4c30-aab4-87e154e0cd08";
|
||||
fsType = "ext4";
|
||||
options = [
|
||||
"noatime"
|
||||
"nodiratime"
|
||||
"nofail"
|
||||
];
|
||||
};
|
||||
|
||||
fileSystems."/mnt/three" =
|
||||
{
|
||||
device = "/dev/disk/by-uuid/0be3ded1-9c8b-40aa-94ca-dc2297d5988e";
|
||||
fsType = "ext4";
|
||||
options = [
|
||||
"noatime"
|
||||
"nodiratime"
|
||||
"nofail"
|
||||
];
|
||||
};
|
||||
|
||||
swapDevices =
|
||||
[{ device = "/dev/disk/by-uuid/b790abb4-ba5f-4476-8f09-b0fc575414aa"; }];
|
||||
swapDevices = [ ];
|
||||
|
||||
powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
|
||||
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
|
||||
|
||||
+51
-22
@@ -1,4 +1,5 @@
|
||||
{ self, pkgs, ... }: {
|
||||
{ self, pkgs, ... }:
|
||||
{
|
||||
imports = [
|
||||
./configuration.nix
|
||||
../users/anish
|
||||
@@ -36,34 +37,45 @@
|
||||
|
||||
virtualisation.docker.enable = true;
|
||||
virtualisation.docker.storageDriver = "btrfs";
|
||||
environment.systemPackages = with pkgs; [ docker-compose via ];
|
||||
environment.systemPackages = with pkgs; [
|
||||
docker-compose
|
||||
via
|
||||
];
|
||||
|
||||
# Speed up boot by removing dependency on network
|
||||
systemd = {
|
||||
targets.network-online.wantedBy =
|
||||
pkgs.lib.mkForce [ ]; # Normally ["multi-user.target"]
|
||||
services.NetworkManager-wait-online.wantedBy =
|
||||
pkgs.lib.mkForce [ ]; # Normally ["network-online.target"]
|
||||
targets.network-online.wantedBy = pkgs.lib.mkForce [ ]; # Normally ["multi-user.target"]
|
||||
services.NetworkManager-wait-online.wantedBy = pkgs.lib.mkForce [ ]; # Normally ["network-online.target"]
|
||||
};
|
||||
|
||||
|
||||
|
||||
fileSystems."/mnt/ftp" = {
|
||||
device = "192.168.1.240:/home/ftp";
|
||||
device = "192.168.1.240:/tank/ftp";
|
||||
fsType = "nfs";
|
||||
options = [ "x-systemd.automount" "noauto" "x-systemd.idle-timeout=600" ];
|
||||
options = [
|
||||
"x-systemd.automount"
|
||||
"noauto"
|
||||
"x-systemd.idle-timeout=600"
|
||||
];
|
||||
};
|
||||
|
||||
fileSystems."/mnt/tv" = {
|
||||
device = "192.168.1.240:/mnt/three/tv";
|
||||
device = "192.168.1.240:/tank/media/tv";
|
||||
fsType = "nfs";
|
||||
options = [ "x-systemd.automount" "noauto" "x-systemd.idle-timeout=600" ];
|
||||
options = [
|
||||
"x-systemd.automount"
|
||||
"noauto"
|
||||
"x-systemd.idle-timeout=600"
|
||||
];
|
||||
};
|
||||
|
||||
fileSystems."/mnt/movies" = {
|
||||
device = "192.168.1.240:/mnt/three/movies";
|
||||
device = "192.168.1.240:/tank/media/movies";
|
||||
fsType = "nfs";
|
||||
options = [ "x-systemd.automount" "noauto" "x-systemd.idle-timeout=600" ];
|
||||
options = [
|
||||
"x-systemd.automount"
|
||||
"noauto"
|
||||
"x-systemd.idle-timeout=600"
|
||||
];
|
||||
};
|
||||
|
||||
boot.supportedFilesystems = [ "ntfs" ];
|
||||
@@ -77,15 +89,23 @@
|
||||
|
||||
# lazy enable of ports necessary for KDE connect which is installed via cli home profile (for some reason?)
|
||||
networking.firewall = {
|
||||
allowedTCPPorts = [ 22 4173 3000 ]; # allow ssh and vibekanban
|
||||
allowedTCPPortRanges = [{
|
||||
allowedTCPPorts = [
|
||||
22
|
||||
4173
|
||||
3000
|
||||
]; # allow ssh and vibekanban
|
||||
allowedTCPPortRanges = [
|
||||
{
|
||||
from = 1714;
|
||||
to = 1764;
|
||||
}];
|
||||
allowedUDPPortRanges = [{
|
||||
}
|
||||
];
|
||||
allowedUDPPortRanges = [
|
||||
{
|
||||
from = 1714;
|
||||
to = 1764;
|
||||
}];
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
age.secrets.curve-wg.file = "${self}/secrets/curve-wg.age";
|
||||
@@ -105,7 +125,11 @@
|
||||
mossnet.backup = {
|
||||
enable = true;
|
||||
name = "curve";
|
||||
paths = [ "/home/anish/usr" "/home/anish/.ssh" "/home/anish/.password-store/" ];
|
||||
paths = [
|
||||
"/home/anish/usr"
|
||||
"/home/anish/.ssh"
|
||||
"/home/anish/.password-store/"
|
||||
];
|
||||
};
|
||||
|
||||
# enable adb
|
||||
@@ -114,8 +138,13 @@
|
||||
#virtualisation.docker.enable = true;
|
||||
boot.blacklistedKernelModules = [ "qcserial" ];
|
||||
# Used for packer Capsul
|
||||
users.users.anish.extraGroups =
|
||||
[ "adbusers" "wheel" "plugdev" "libvertd" "docker" ];
|
||||
users.users.anish.extraGroups = [
|
||||
"adbusers"
|
||||
"wheel"
|
||||
"plugdev"
|
||||
"libvertd"
|
||||
"docker"
|
||||
];
|
||||
virtualisation.libvirtd.enable = true;
|
||||
hardware.keyboard.zsa.enable = true;
|
||||
services.udev.extraRules = ''
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
{ config, options, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
options,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
with lib;
|
||||
let
|
||||
# cfg = config.services.archivebox;
|
||||
@@ -7,9 +13,8 @@ let
|
||||
port = "8123";
|
||||
in
|
||||
{
|
||||
nixpkgs.config.permittedInsecurePackages = [
|
||||
"python3.10-django-3.1.14"
|
||||
];
|
||||
# Note: permittedInsecurePackages must be set in flake.nix nixpkgsFor config
|
||||
# if archivebox still requires python3.10-django-3.1.14
|
||||
|
||||
services.nginx.virtualHosts."archive.mossnet.lan" = {
|
||||
enableACME = false;
|
||||
@@ -26,7 +31,10 @@ in
|
||||
systemd.services.archivebox-install = {
|
||||
description = "archivebox install service";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
path = with pkgs; [ coreutils archivebox ];
|
||||
path = with pkgs; [
|
||||
coreutils
|
||||
archivebox
|
||||
];
|
||||
|
||||
serviceConfig = {
|
||||
User = user;
|
||||
@@ -51,7 +59,10 @@ in
|
||||
systemd.services.archivebox-server = {
|
||||
description = "archivebox server service";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
path = with pkgs; [ coreutils archivebox ];
|
||||
path = with pkgs; [
|
||||
coreutils
|
||||
archivebox
|
||||
];
|
||||
|
||||
serviceConfig = {
|
||||
User = user;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
user = "calibre-server";
|
||||
group = "calibre-server";
|
||||
options = {
|
||||
calibreLibrary = "/data/books";
|
||||
calibreLibrary = "/tank/books";
|
||||
enableBookUploading = true;
|
||||
};
|
||||
};
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
services.calibre-server = {
|
||||
enable = true;
|
||||
libraries = [ "/data/books" ];
|
||||
libraries = [ "/tank/books" ];
|
||||
# Bug in the module puts this in quotes in the systemd file
|
||||
# user = calibre;
|
||||
# group = calibre;
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
environment.systemPackages = [ pkgs.ffmpeg ];
|
||||
mossnet.gonic.enable = true;
|
||||
mossnet.gonic.settings = ''
|
||||
music-path /mnt/two/music/
|
||||
podcast-path /data/podcasts
|
||||
cache-path /data/cache
|
||||
playlists-path /data/playlists
|
||||
music-path /tank/media/music/
|
||||
podcast-path /tank/podcasts
|
||||
cache-path /tank/cache
|
||||
playlists-path /tank/playlists
|
||||
'';
|
||||
mossnet.gonic.user = "gonic";
|
||||
mossnet.gonic.group = "audio";
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
services.headphones = {
|
||||
enable = true;
|
||||
@@ -6,7 +11,7 @@
|
||||
port = 8181;
|
||||
user = "headphones";
|
||||
group = "audio";
|
||||
dataDir = "/data/music";
|
||||
dataDir = "/tank/media/music";
|
||||
};
|
||||
services.nginx.virtualHosts."headphones.mossnet.lan" = {
|
||||
enableACME = false;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
};
|
||||
host = "0.0.0.0";
|
||||
port = 8567;
|
||||
mediaLocation = "/mnt/two/photos";
|
||||
mediaLocation = "/tank/media/photos";
|
||||
openFirewall = true;
|
||||
settings.server.externalDomain = "https://photos.sealight.xyz";
|
||||
};
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
# Enable Hardware Acceleration for transcoding
|
||||
nixpkgs.config.packageOverrides = pkgs: {
|
||||
vaapiIntel = pkgs.vaapiIntel.override { enableHybridCodec = true; };
|
||||
};
|
||||
hardware.opengl = {
|
||||
# Note: vaapiIntel override with enableHybridCodec should be in flake.nix overlay if needed
|
||||
hardware.graphics = {
|
||||
enable = true;
|
||||
extraPackages = with pkgs; [
|
||||
intel-media-driver
|
||||
@@ -18,10 +21,22 @@
|
||||
enable = true;
|
||||
user = "jellyfin";
|
||||
group = "video";
|
||||
openFirewall = true; # only for defaults
|
||||
openFirewall = true; # only for defaults (8096)
|
||||
};
|
||||
networking.firewall.allowedTCPPorts = [ 8181 ];
|
||||
users.users.jellyfin = {
|
||||
extraGroups = [ "video" "audio" ];
|
||||
extraGroups = [
|
||||
"video"
|
||||
"audio"
|
||||
];
|
||||
};
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
virtualHosts = {
|
||||
"jellyfin.mossnet.lan" = {
|
||||
forceSSL = false;
|
||||
enableACME = false;
|
||||
locations."/".proxyPass = "http://localhost:8096/";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
{ self, config, pkgs, ... }: {
|
||||
{
|
||||
self,
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
age.secrets.nullhex-smtp.file = "${self}/secrets/nullhex-smtp.age";
|
||||
age.secrets.nullhex-smtp.owner = "grafana";
|
||||
|
||||
@@ -34,21 +40,23 @@
|
||||
};
|
||||
|
||||
services.postgresql = {
|
||||
ensureUsers = [{
|
||||
ensureUsers = [
|
||||
{
|
||||
name = "grafana";
|
||||
# TODO this is deprecated
|
||||
# Need to translate this to
|
||||
# systemd.services.postgresql.postStart
|
||||
# or initialScript
|
||||
ensurePermissions = {
|
||||
"ALL TABLES IN SCHEMA public" = "SELECT";
|
||||
"DATABASE wallabag" = "CONNECT";
|
||||
"DATABASE ulogger" = "CONNECT";
|
||||
"DATABASE photoprism" = "CONNECT";
|
||||
};
|
||||
}];
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
# Grant grafana user read access to databases for monitoring
|
||||
systemd.services.postgresql.postStart = pkgs.lib.mkAfter ''
|
||||
$PSQL -tAc "GRANT CONNECT ON DATABASE wallabag TO grafana" 2>/dev/null || true
|
||||
$PSQL -tAc "GRANT CONNECT ON DATABASE ulogger TO grafana" 2>/dev/null || true
|
||||
$PSQL -tAc "GRANT CONNECT ON DATABASE photoprism TO grafana" 2>/dev/null || true
|
||||
$PSQL -d wallabag -tAc "GRANT SELECT ON ALL TABLES IN SCHEMA public TO grafana" 2>/dev/null || true
|
||||
$PSQL -d ulogger -tAc "GRANT SELECT ON ALL TABLES IN SCHEMA public TO grafana" 2>/dev/null || true
|
||||
$PSQL -d photoprism -tAc "GRANT SELECT ON ALL TABLES IN SCHEMA public TO grafana" 2>/dev/null || true
|
||||
'';
|
||||
|
||||
services.prometheus = {
|
||||
enable = true;
|
||||
port = 9001;
|
||||
@@ -66,11 +74,15 @@
|
||||
scrapeConfigs = [
|
||||
{
|
||||
job_name = "box";
|
||||
static_configs = [{ targets = [ "127.0.0.1:${toString config.services.prometheus.exporters.node.port}" ]; }];
|
||||
static_configs = [
|
||||
{ targets = [ "127.0.0.1:${toString config.services.prometheus.exporters.node.port}" ]; }
|
||||
];
|
||||
}
|
||||
{
|
||||
job_name = "dns";
|
||||
static_configs = [{ targets = [ "127.0.0.1:${toString config.services.prometheus.exporters.dnsmasq.port}" ]; }];
|
||||
static_configs = [
|
||||
{ targets = [ "127.0.0.1:${toString config.services.prometheus.exporters.dnsmasq.port}" ]; }
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
@@ -12,16 +12,31 @@
|
||||
statdPort = 4000;
|
||||
extraNfsdConfig = '''';
|
||||
exports = ''
|
||||
/home/ftp 192.168.1.0/24(rw)
|
||||
/mnt/one 192.168.1.0/24(rw)
|
||||
/mnt/two 192.168.1.0/24(rw,async,no_subtree_check) 10.0.69.0/24(rw,async,no_subtree_check)
|
||||
/mnt/three 192.168.1.0/24(rw)
|
||||
/tank/ftp 192.168.1.0/24(rw)
|
||||
/tank/media/music 192.168.1.0/24(rw,async,no_subtree_check) 10.0.69.0/24(rw,async,no_subtree_check)
|
||||
/tank/media/photos 192.168.1.0/24(rw,async,no_subtree_check) 10.0.69.0/24(rw,async,no_subtree_check)
|
||||
/tank/media/movies 192.168.1.0/24(rw)
|
||||
/tank/media/tv 192.168.1.0/24(rw)
|
||||
'';
|
||||
};
|
||||
|
||||
networking.firewall = {
|
||||
allowedTCPPorts = [ 111 2049 4000 4001 4002 20048 ];
|
||||
allowedUDPPorts = [ 111 2049 4000 4001 4002 20048 ];
|
||||
allowedTCPPorts = [
|
||||
111
|
||||
2049
|
||||
4000
|
||||
4001
|
||||
4002
|
||||
20048
|
||||
];
|
||||
allowedUDPPorts = [
|
||||
111
|
||||
2049
|
||||
4000
|
||||
4001
|
||||
4002
|
||||
20048
|
||||
];
|
||||
};
|
||||
|
||||
#systemd.services.create-mount-dir = {
|
||||
|
||||
@@ -4,9 +4,9 @@ set -euo pipefail
|
||||
|
||||
REMOTE_HOST="aynish@talos.feralhosting.com"
|
||||
REMOTE_PATH="private/transmission/data/"
|
||||
LOCAL_PATH="/mnt/two/incoming"
|
||||
TRACKING_FILE="/mnt/two/incoming/.downloaded_albums"
|
||||
LOG_FILE="/mnt/two/incoming/download-log"
|
||||
LOCAL_PATH="/tank/new-music"
|
||||
TRACKING_FILE="/tank/new-music/.downloaded_albums"
|
||||
LOG_FILE="/tank/new-music/download-log"
|
||||
|
||||
# Create tracking file if it doesn't exist
|
||||
touch "$TRACKING_FILE"
|
||||
@@ -47,7 +47,7 @@ while IFS= read -r album; do
|
||||
echo "$(date): Importing $album to beets..." >>"$LOG_FILE"
|
||||
# Set umask to allow group read/write access
|
||||
umask 002
|
||||
if beet -p fetchart import -m -l /home/anish/music.log -q -g "$LOCAL_PATH/$album"; then
|
||||
if beet import -q "$LOCAL_PATH/$album"; then
|
||||
echo "$(date): Successfully imported $album to beets" >>"$LOG_FILE"
|
||||
else
|
||||
echo "$(date): Failed to import $album to beets" >>"$LOG_FILE"
|
||||
|
||||
@@ -11,12 +11,12 @@
|
||||
# Normally, I would write this into the homedir with home-manager
|
||||
# And explictly set the dir to be the output of the home-manager location
|
||||
script-torrent-done-filename = pkgs.writeShellScript "beet-import.sh" ''
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
beet -p fetchart import -l /home/anish/music.log -q -g "$TR_TORRENT_DIR"
|
||||
beet -p fetchart import -l /home/anish/music.log -q -g "$TR_TORRENT_DIR"
|
||||
'';
|
||||
rpc-url = "/transmission/rpc/";
|
||||
download-dir = "/mnt/two/new-music";
|
||||
download-dir = "/tank/new-music";
|
||||
};
|
||||
};
|
||||
services.nginx.virtualHosts."transmission.mossnet.lan" = {
|
||||
|
||||
Executable
+119
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Install script for box NAS
|
||||
# Run this from the NixOS installer after rsync'ing the helm repo
|
||||
#
|
||||
# Prerequisites:
|
||||
# - Boot NixOS installer
|
||||
# - Enable SSH: passwd && sudo systemctl start sshd
|
||||
# - rsync helm repo: rsync -avz --exclude='.git' /path/to/helm nixos@<IP>:~/
|
||||
#
|
||||
# Usage:
|
||||
# cd ~/helm
|
||||
# ./install-box.sh
|
||||
|
||||
# Configuration
|
||||
FLAKE="$HOME/helm#box"
|
||||
NVME="/dev/disk/by-id/nvme-CT500P310SSD8_2544543B87C2"
|
||||
|
||||
# ZFS drives - update these if drives change
|
||||
ZFS1="/dev/disk/by-id/ata-WDC_WD40EFPX-68C6CN0_WD-WX32D954A2J7"
|
||||
ZFS2="/dev/disk/by-id/ata-WDC_WD40EFPX-68C6CN0_WD-WX32D95FVZVL"
|
||||
ZFS3="/dev/disk/by-id/ata-WDC_WD40EFPX-68C6CN0_WD-WX42D95M807R"
|
||||
|
||||
echo "=== Box NAS Installation ==="
|
||||
echo ""
|
||||
echo "This will install NixOS with:"
|
||||
echo " - NVMe boot drive: $NVME"
|
||||
echo " - ZFS RAIDZ1 pool with 3x 4TB drives (~8TB usable)"
|
||||
echo ""
|
||||
|
||||
# Verify drives exist
|
||||
echo "Verifying drives..."
|
||||
for disk in "$NVME" "$ZFS1" "$ZFS2" "$ZFS3"; do
|
||||
if [[ ! -e "$disk" ]]; then
|
||||
echo "ERROR: Disk not found: $disk"
|
||||
echo "Available disks:"
|
||||
ls -la /dev/disk/by-id/ | grep -E '(nvme|ata)' | grep -v part
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
echo "All drives found."
|
||||
echo ""
|
||||
|
||||
# Generate ZFS keyfile
|
||||
echo "Generating ZFS keyfile..."
|
||||
dd if=/dev/urandom of=/tmp/tank.key bs=32 count=1 2>/dev/null
|
||||
echo "ZFS keyfile created at /tmp/tank.key"
|
||||
echo ""
|
||||
|
||||
# Get LUKS password
|
||||
echo "Enter LUKS password for boot drive encryption:"
|
||||
read -s LUKS_PASSWORD
|
||||
echo ""
|
||||
echo "Confirm LUKS password:"
|
||||
read -s LUKS_PASSWORD_CONFIRM
|
||||
echo ""
|
||||
|
||||
if [[ "$LUKS_PASSWORD" != "$LUKS_PASSWORD_CONFIRM" ]]; then
|
||||
echo "ERROR: Passwords do not match"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -n "$LUKS_PASSWORD" > /tmp/luks-password
|
||||
echo "LUKS password saved."
|
||||
echo ""
|
||||
|
||||
# Confirm before proceeding
|
||||
echo "WARNING: This will DESTROY all data on the following drives:"
|
||||
echo " - $NVME"
|
||||
echo " - $ZFS1"
|
||||
echo " - $ZFS2"
|
||||
echo " - $ZFS3"
|
||||
echo ""
|
||||
read -p "Type 'yes' to continue: " CONFIRM
|
||||
if [[ "$CONFIRM" != "yes" ]]; then
|
||||
echo "Aborted."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Running disko-install..."
|
||||
sudo nix \
|
||||
--extra-experimental-features nix-command \
|
||||
--extra-experimental-features flakes \
|
||||
run 'github:nix-community/disko/latest#disko-install' -- \
|
||||
--flake "$FLAKE" \
|
||||
--disk nvme "$NVME" \
|
||||
--disk zfs1 "$ZFS1" \
|
||||
--disk zfs2 "$ZFS2" \
|
||||
--disk zfs3 "$ZFS3"
|
||||
|
||||
echo ""
|
||||
echo "Copying ZFS keyfile to installed system..."
|
||||
# disko-install mounts the root filesystem at /mnt
|
||||
if [[ ! -d /mnt/etc ]]; then
|
||||
echo "ERROR: /mnt/etc does not exist. Is the root filesystem mounted?"
|
||||
exit 1
|
||||
fi
|
||||
sudo mkdir -p /mnt/etc/zfs
|
||||
sudo cp /tmp/tank.key /mnt/etc/zfs/tank.key
|
||||
sudo chmod 000 /mnt/etc/zfs/tank.key
|
||||
|
||||
echo "Updating ZFS keylocation to permanent path..."
|
||||
# Update keylocation so ZFS looks for the key in the installed system
|
||||
sudo zfs set keylocation=file:///etc/zfs/tank.key tank
|
||||
|
||||
echo ""
|
||||
echo "Cleaning up..."
|
||||
rm -f /tmp/luks-password /tmp/tank.key
|
||||
|
||||
echo ""
|
||||
echo "=== Installation complete! ==="
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Reboot: sudo reboot"
|
||||
echo " 2. Enter LUKS password at boot prompt"
|
||||
echo " 3. SSH to box at 192.168.1.240"
|
||||
echo ""
|
||||
Reference in New Issue
Block a user