Compare commits

..

1 commit

Author SHA1 Message Date
99ad3058a3 integrate sops-nix for secret management 2026-03-11 19:24:17 +07:00
59 changed files with 633 additions and 948 deletions

View file

@ -1,41 +0,0 @@
name: Activate Homelab Configuration
on:
workflow_dispatch:
push:
branches: [ main ]
env:
PATH: /run/current-system/sw/bin:/run/wrappers/bin
jobs:
rebuild:
runs-on: self-hosted
steps:
- name: Setup SSH key
run: |
mkdir -p ./ssh
echo "${{ secrets.DEPLOY_SSH_KEY }}" > ./ssh/deploy_key
chmod 600 ./ssh/deploy_key
- name: Rebuild and switch
run: |
ssh -i ./ssh/deploy_key \
-o PasswordAuthentication=no \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
root@localhost \
"bash -lc 'nixos-rebuild switch --refresh --flake git+http://localhost:5080/satr14/nix-flake#homelab -L'"
- name: Show generation
if: always()
run: |
ssh -i ./ssh/deploy_key \
-o PasswordAuthentication=no \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
root@localhost "bash -lc 'nixos-version'"
- name: Clean Up
if: always()
run: rm -f ./ssh/deploy_key

25
.sops.yaml Normal file
View file

@ -0,0 +1,25 @@
# To set up sops-nix:
# 1. Generate an age key on each host:
# mkdir -p ~/.config/sops/age
# age-keygen -o ~/.config/sops/age/keys.txt
# Or derive from the host SSH key:
# nix-shell -p ssh-to-age --run 'cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age'
#
# 2. Replace the placeholder age keys below with the actual public keys.
#
# 3. Encrypt secret files:
# sops secrets/homelab.yaml
#
# 4. To re-key after changing keys:
# sops updatekeys secrets/homelab.yaml
keys:
- &homelab age1XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX # replace with: ssh-to-age < /etc/ssh/ssh_host_ed25519_key.pub
- &admin age1XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX # replace with: age-keygen output from your admin machine
creation_rules:
- path_regex: secrets/homelab\.yaml$
key_groups:
- age:
- *homelab
- *admin

View file

@ -1,3 +0,0 @@
{
"nixEnvSelector.suggestion": false
}

View file

@ -1,18 +1,5 @@
![nix-flake](ss.png)
rewrite of my nixos flake with hopefully better structuring and modularity
> [!WARNING]
> This flake is ment for personal use. The code is not well documented nor structured and is not ment to be used by others. **Use at your own risk.**
## Hosts
- `thinkpad` - Thinkpad T480, i5 8350U, 16GB RAM, 256GB NVME
- `homelab` - i7 8700T, 32GB RAM, 512GB NVME, 1TB 2.5" SATA
## Todo
- Automatic backups to external drives.
- Better documentation and code structure.
- Use NixOS modules system.
## Credits
- [orangc's flake](https://git.orangc.net/c/dots)
- [vimjoyer's tutorials](https://www.youtube.com/@vimjoyer)
- [wallpaper source](https://github.com/er2de2/catppuccin_walls/blob/master/wallpapers_png/autumn_2.0.png)
> this flake is ment for personal use. code is not well documented and is not ment to be used by others. use at your own risk.

97
flake.lock generated
View file

@ -5,11 +5,11 @@
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1777734189,
"narHash": "sha256-kbIhdhDPaTP6gxAPkcRYeB+cqPFDpTM/bnw+m+26vkI=",
"lastModified": 1773146250,
"narHash": "sha256-azzOjRqTxAqByzRP87jUUsmfOQ85i7h/YkrgTX0jZgg=",
"owner": "catppuccin",
"repo": "nix",
"rev": "e68cf5deaf1a7afed2e548835dba2ae99f5a3ccb",
"rev": "0fa0d06dd3cd09f37f76d19b389d7ff947dfd7e8",
"type": "github"
},
"original": {
@ -18,22 +18,6 @@
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1747046372,
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
@ -78,11 +62,11 @@
]
},
"locked": {
"lastModified": 1777977606,
"narHash": "sha256-8ceIdvijN2tm9fIAUgnIZ8BM8TlsFx7pRYKRoxNsi1k=",
"lastModified": 1773179137,
"narHash": "sha256-EdW2bwzlfme0vbMOcStnNmKlOAA05Bp6su2O8VLGT0k=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "7ef1c04d11f7ef69fd946b118c768c32de0b89a5",
"rev": "3f98e2bbc661ec0aaf558d8a283d6955f05f1d09",
"type": "github"
},
"original": {
@ -92,33 +76,13 @@
"type": "github"
}
},
"mc": {
"inputs": {
"flake-compat": "flake-compat",
"nixpkgs": "nixpkgs_3",
"systems": "systems_2"
},
"locked": {
"lastModified": 1777952170,
"narHash": "sha256-8dQ/DOUvQI8x5i6MZ309/xZLLVfV1CgWbD2+JiQ7Hd4=",
"owner": "Infinidoge",
"repo": "nix-minecraft",
"rev": "34a46e4de360c5004ee1866f5e3de78bf5e8b289",
"type": "github"
},
"original": {
"owner": "Infinidoge",
"repo": "nix-minecraft",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1777268161,
"narHash": "sha256-bxrdOn8SCOv8tN4JbTF/TXq7kjo9ag4M+C8yzzIRYbE=",
"lastModified": 1772773019,
"narHash": "sha256-E1bxHxNKfDoQUuvriG71+f+s/NT0qWkImXsYZNFFfCs=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1c3fe55ad329cbcb28471bb30f05c9827f724c76",
"rev": "aca4d95fce4914b3892661bcb80b8087293536c6",
"type": "github"
},
"original": {
@ -145,11 +109,11 @@
},
"nixpkgs_3": {
"locked": {
"lastModified": 1769461804,
"narHash": "sha256-msG8SU5WsBUfVVa/9RPLaymvi5bI8edTavbIq3vRlhI=",
"lastModified": 1772963539,
"narHash": "sha256-9jVDGZnvCckTGdYT53d/EfznygLskyLQXYwJLKMPsZs=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "bfc1b8a4574108ceef22f02bafcf6611380c100d",
"rev": "9dcb002ca1690658be4a04645215baea8b95f31d",
"type": "github"
},
"original": {
@ -161,16 +125,16 @@
},
"nixpkgs_4": {
"locked": {
"lastModified": 1777578337,
"narHash": "sha256-Ad49moKWeXtKBJNy2ebiTQUEgdLyvGmTeykAQ9xM+Z4=",
"owner": "nixos",
"lastModified": 1772736753,
"narHash": "sha256-au/m3+EuBLoSzWUCb64a/MZq6QUtOV8oC0D9tY2scPQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "15f4ee454b1dce334612fa6843b3e05cf546efab",
"rev": "917fec990948658ef1ccd07cef2a1ef060786846",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
@ -180,26 +144,29 @@
"ctp": "ctp",
"gl": "gl",
"hm": "hm",
"mc": "mc",
"nixpkgs": "nixpkgs_4"
"nixpkgs": "nixpkgs_3",
"sops": "sops"
}
},
"systems": {
"sops": {
"inputs": {
"nixpkgs": "nixpkgs_4"
},
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"lastModified": 1773096132,
"narHash": "sha256-M3zEnq9OElB7zqc+mjgPlByPm1O5t2fbUrH3t/Hm5Ag=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "d1ff3b1034d5bab5d7d8086a7803c5a5968cd784",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"owner": "Mic92",
"repo": "sops-nix",
"type": "github"
}
},
"systems_2": {
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",

View file

@ -7,10 +7,9 @@
inputs.nixpkgs.follows = "nixpkgs";
};
sops.url = "github:Mic92/sops-nix";
gl.url = "github:nix-community/nixGL";
ctp.url = "github:catppuccin/nix";
mc.url = "github:Infinidoge/nix-minecraft";
};
outputs = inputs: let
@ -19,7 +18,7 @@
overlays = [ inputs.gl.overlay ];
config = {
allowUnfree = true;
permittedInsecurePackages = [ "ventoy-qt5-1.1.12" ];
permittedInsecurePackages = [ "ventoy-qt5-1.1.10" ];
};
};
args = {
@ -32,6 +31,7 @@
modules = [
./hosts/${host}/config.nix
inputs.ctp.nixosModules.catppuccin
inputs.sops.nixosModules.sops
];
};
@ -41,6 +41,7 @@
modules = [
./hosts/${host}/config.nix
inputs.ctp.nixosModules.catppuccin
inputs.sops.nixosModules.sops
inputs.hm.nixosModules.home-manager
{
home-manager = {

View file

@ -21,7 +21,7 @@
tailscale.enable = true;
openssh = {
enable = true;
settings.PermitRootLogin = "prohibit-password";
settings.PermitRootLogin = "yes";
};
};
users.users."${username}" = {

View file

@ -1,12 +1,4 @@
let
d = dest: { inherit dest; auth = false; };
da = dest: { inherit dest; auth = true; };
ext4 = path: { inherit path; type = "ext4"; };
btrfs = path: { inherit path; type = "btrfs"; };
selfSigned = service: { inherit service; originRequest.noTLSVerify = true; };
in {
{
flake-path = "~/Projects/nix-flake"; # set this to the cloned repo path
username = "satr14";
@ -23,78 +15,20 @@ in {
homelab = rec {
domain = "satr14.my.id"; # root domain for dns, ssl certs, reverse proxy, etc.
ssh-keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIESvQFXoUBafatqnxTd6qk3WEOcfwb3AIWVTstR3lHzX forgejo"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJtdH1YqRH9xhuHMivezLvj/hpH77yfH3HUCaRboB/hb forgejo-deploy-runner"
];
cf-tunnel-id = "26318288-cdd7-4e58-904b-c45f10d3e40a";
disks = {
# gallery = ext4 "/dev/disk/by-uuid/834f51c1-90ee-4601-ba76-ef0419198d67"; # disk for photo gallery
# data = ext4 "/dev/disk/by-uuid/a5752dd6-092d-484c-969c-2fdc7cb4a5f0"; # disk for app data
# host = ext4 "/dev/disk/by-uuid/968f14a4-631e-4325-8cd1-f9aec0da9e4d"; # disk for media collection (named host for backwards compatibility)
# ^^ virtual disks
# achive = ext4 "/dev/disk/by-uuid/"; # long term archival
data = ext4 "/dev/disk/by-uuid/aa453135-4b7a-4b12-8efc-f3dda093d2b7"; # app data
share = btrfs "/dev/disk/by-uuid/f1ee1d17-e852-4e02-ae86-eaf6116a2aeb"; # file server
};
dash = [
[ "PocketID" "authentik" "https://auth.${domain}" "http://localhost:1411/" ]
[ "Forgejo" "forgejo" "https://git.${domain}" "http://localhost:5080/" ]
[ "Copyparty" "files" "https://cdn.${domain}" "http://localhost:3923/" ]
[ "CryptPad" "cryptpad" "https://docs.${domain}" "http://localhost:7090/" ]
[ "CodeServer" "coder" "https://code.proxy.${domain}" "http://localhost:8443/" ]
[ "AdGuardHome" "adguard" "https://dns.proxy.${domain}" "http://localhost:8088/" ]
[ "Traefik" "traefikproxy" "https://dynamic.proxy.${domain}/dashboard/" "" ]
[ "Immich" "immich" "https://gallery.proxy.${domain}" "http://localhost:2283/" ]
[ "Jellyfin" "jellyfin" "https://media.proxy.${domain}" "http://localhost:8096/" ]
[ "VaultWarden" "vaultwarden" "https://pass.proxy.${domain}" "http://localhost:8060/" ]
[ "Ollama" "ollama" "https://ai.proxy.${domain}" "http://localhost:8080/" ]
[ "Ntfy" "ntfy" "https://notify.proxy.${domain}" "http://localhost:8067/" ]
[ "SearXNG" "searxng" "https://search.proxy.${domain}" "http://localhost:8091/" ]
[ "Dockge" "docker" "https://containers.proxy.${domain}" "http://localhost:5001/" ]
];
routes = {
"mc0.${domain}" = "tcp://localhost:25565";
"docs-sandbox.${domain}" = "http://localhost:7090";
"docs.${domain}" = "http://localhost:7090";
"cdn.${domain}" = selfSigned "https://localhost:3923";
"git.${domain}" = "http://localhost:5080";
"auth.${domain}" = "http://localhost:1411";
"dash.${domain}" = "http://localhost:5070";
"media.${domain}" = "http://localhost:8096";
"gallery.${domain}" = "http://localhost:2284";
};
proxy = {
base = "proxy.${domain}";
hosts = {
"containers" = da "http://localhost:5001";
"code" = da "http://localhost:8443";
"dns" = da "http://localhost:8088";
"gallery" = d "http://localhost:2283";
"dynamic" = d "http://localhost:8082";
"search" = d "http://localhost:8091";
"notify" = d "http://localhost:8067";
"media" = d "http://localhost:8096";
"pass" = d "http://localhost:8060";
"auth" = d "http://localhost:1411";
"git" = d "http://localhost:5080";
"cdn" = d "http://localhost:3923";
"ai" = d "http://localhost:8080";
"@" = d "http://localhost:5070";
};
redirects = {
"www" = "https://${proxy.base}";
"dash" = "https://${proxy.base}";
"immich" = "https://gallery.${proxy.base}";
"2fa" = "https://2fa.${domain}";
};
gallery = "/dev/disk/by-uuid/834f51c1-90ee-4601-ba76-ef0419198d67"; # disk for photo gallery
data = "/dev/disk/by-uuid/a5752dd6-092d-484c-969c-2fdc7cb4a5f0"; # disk for app data
host = "/dev/disk/by-uuid/968f14a4-631e-4325-8cd1-f9aec0da9e4d"; # disk for media collection (named host for backwards compatibility)
};
records = [
[ "main.dns.${domain}" "100.113.147.93" ] # this machine
[ "server.dns.${domain}" "10.3.14.69" ]
[ "router.dns.${domain}" "10.3.14.1" ]
[ "home.dns.${domain}" "10.3.14.235" ]
[ "workspace.dns.${domain}" "10.3.14.57" ]
[ "old-main.dns.${domain}" "10.3.14.42" ] # old main machine for connecting while migrating
[ "main.dns.${domain}" "10.3.14.215" ] # this machine
[ "proxy.${domain}" "main.dns.${domain}" ]
[ "*.proxy.${domain}" "proxy.${domain}" ]
@ -128,9 +62,7 @@ in {
};
git = { # setup your git author
username = "satr14"; # forgejo username
server = "https://git.satr14.my.id"; # forgejo server url
user = "satr14";
user = "Satria";
email = "admin@satr14.my.id";
};
}

View file

@ -1,13 +1,14 @@
{ ... }: {
imports = [
# ./misc/cpu-hotplug.nix
# ./misc/serial.nix
# ./misc/qemu-virtio.nix
# ^^ only used if vm
./core/firmware.nix
./core/igpu.nix
./misc/disks.nix
./misc/serial.nix
];
boot.initrd.availableKernelModules = [ "virtio_net" "virtio_pci" "virtio_mmio" "virtio_blk" "virtio_scsi" "virtio_console" ];
services = {
qemuGuest.enable = true;
spice-vdagentd.enable = true;
};
}

View file

@ -1,4 +1,4 @@
{ pkgs, username, resume-dev, ... }: {
{ pkgs, resume-dev, ... }: {
powerManagement.powertop.enable = true;
services = {
@ -10,20 +10,6 @@
echo 85 > /sys/class/power_supply/BAT*/charge_control_end_threshold || true
''}"
'';
cron = {
enable = true;
systemCronJobs = [
"* * * * * ${username} bash -x ${pkgs.writeShellScript "low-battery-notifier" ''
BAT_PCT=`${pkgs.acpi}/bin/acpi -b | ${pkgs.gnugrep}/bin/grep -P -o '[0-9]+(?=%)'`
BAT_STA=`${pkgs.acpi}/bin/acpi -b | ${pkgs.gnugrep}/bin/grep -P -o '\w+(?=,)'`
echo "`date` battery status:$BAT_STA percentage:$BAT_PCT"
export DISPLAY=:0
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus
test $BAT_PCT -le 30 && test $BAT_PCT -gt 15 && test $BAT_STA = "Discharging" && ${pkgs.libnotify}/bin/notify-send "Low Battery" "Battery remaining: $BAT_PCT%."
test $BAT_PCT -le 15 && test $BAT_STA = "Discharging" && ${pkgs.libnotify}/bin/notify-send -u critical "Low Battery" "Shutdown at 10%."
''} > /tmp/cron.batt.log 2>&1"
];
};
upower = {
enable = true;
percentageCritical = 15;

View file

@ -51,7 +51,7 @@
enable = true; # wait for fix: https://github.com/AdnanHodzic/auto-cpufreq/issues/906
settings = {
charger = {
governor = "powersave"; # "performance";
governor = "performance";
energy_performance_preference = "performance";
turbo = "always";
platform_profile = "performance";

View file

@ -1,5 +0,0 @@
{ ... }: {
services.udev.extraRules = ''
SUBSYSTEM=="cpu", ACTION=="add", TEST=="online", ATTR{online}=="0", ATTR{online}="1"
'';
}

View file

@ -1,15 +1,13 @@
{ lib, homelab, ... }: let
globalOpts = {
fsType = "ext4";
autoFormat = true;
autoResize = true;
};
in {
fileSystems = {
"/".autoResize = true;
} // lib.mapAttrs' (name: dev:
lib.nameValuePair "/mnt/${name}" (globalOpts // {
device = dev.path;
fsType = dev.type;
})
} // lib.mapAttrs' (name: device:
lib.nameValuePair "/mnt/${name}" (globalOpts // { inherit device; })
) homelab.disks;
}

View file

@ -1,14 +0,0 @@
{ ... }: {
boot.initrd.availableKernelModules = [
"virtio_net"
"virtio_pci"
"virtio_mmio"
"virtio_blk"
"virtio_scsi"
"virtio_console"
];
services = {
qemuGuest.enable = true;
spice-vdagentd.enable = true;
};
}

View file

@ -12,7 +12,7 @@
];
boot = {
kernelPackages = pkgs.linuxPackages_zen;
kernelPackages = pkgs.linuxPackages;
kernel.sysctl."vm.laptop_mode" = 5;
initrd.availableKernelModules = [ "thinkpad_acpi" ];
kernelParams = [

View file

@ -1,30 +1,6 @@
{ pkgs, ... }: {
nixpkgs.config.allowUnfree = true;
xdg = {
autostart.enable = true;
mimeApps = {
enable = true;
defaultApplications = {
"text/plain" = "nvim.desktop";
"text/html" = "brave-browser.desktop";
"application/pdf" = "brave-browser.desktop";
"x-scheme-handler/http" = "brave-browser.desktop";
"x-scheme-handler/https" = "brave-browser.desktop";
"x-terminal-emulator" = "kitty.desktop";
"inode/directory" = "pcmanfm-qt.desktop";
"audio/mpeg" = "vlc.desktop";
"audio/mp3" = "vlc.desktop";
"audio/wav" = "vlc.desktop";
"audio/flac" = "vlc.desktop";
"video/mp4" = "vlc.desktop";
"video/x-matroska" = "vlc.desktop";
"video/webm" = "vlc.desktop";
"video/x-msvideo" = "vlc.desktop";
};
};
};
home.packages = with pkgs; [
zed-editor
# kicad-small
@ -32,11 +8,10 @@
slack
discord
# protonmail-desktop # https://www.reddit.com/r/NixOS/comments/1rm9alf/protonmail_in_nixos/
protonmail-desktop
vlc
brave
flameshot
libreoffice
appimage-run
# keepassxc
@ -61,8 +36,8 @@
})
ferium
packwiz
portablemc
prismlauncher
steamguard-cli
# modrinth-app
];

View file

@ -44,26 +44,13 @@
enable = true;
defaultEditor = true;
vimAlias = true;
withRuby = false;
withPython3 = false;
initLua = ''
vim.opt.clipboard = "unnamedplus"
vim.opt.termguicolors = true
vim.g.clipboard = {
name = "OSC 52",
copy = {
["+"] = require("vim.ui.clipboard.osc52").copy("+"),
["*"] = require("vim.ui.clipboard.osc52").copy("*"),
},
paste = {
["+"] = require("vim.ui.clipboard.osc52").paste("+"),
["*"] = require("vim.ui.clipboard.osc52").paste("*"),
},
}
require("nvim-tree").setup()
vim.api.nvim_create_autocmd("VimEnter", {
callback = function()
vim.cmd("set nu")
-- vim.cmd("NvimTreeOpen")
vim.cmd.wincmd 'p'
end,
})
@ -76,6 +63,7 @@
telescope-file-browser-nvim
nvim-tree-lua
nvim-cmp
barbar-nvim
indent-blankline-nvim
markdown-preview-nvim
];
@ -92,7 +80,6 @@
};
git = {
enable = true;
signing.format = null;
settings = {
pull.rebase = "true";
credential.helper = "cache --timeout=3600";

26
modules/home/core/xdg.nix Normal file
View file

@ -0,0 +1,26 @@
{ ... }: {
xdg = {
autostart.enable = true;
mimeApps = {
enable = true;
defaultApplications = {
"text/plain" = "nvim.desktop";
"text/html" = "brave-browser.desktop";
"application/pdf" = "brave-browser.desktop";
"x-scheme-handler/http" = "brave-browser.desktop";
"x-scheme-handler/https" = "brave-browser.desktop";
"x-scheme-handler/terminal" = "kitty.desktop";
"x-terminal-emulator" = "kitty.desktop";
"inode/directory" = "pcmanfm-qt.desktop";
"audio/mpeg" = "vlc.desktop";
"audio/mp3" = "vlc.desktop";
"audio/wav" = "vlc.desktop";
"audio/flac" = "vlc.desktop";
"video/mp4" = "vlc.desktop";
"video/x-matroska" = "vlc.desktop";
"video/webm" = "vlc.desktop";
"video/x-msvideo" = "vlc.desktop";
};
};
};
}

View file

@ -2,18 +2,10 @@
programs.zed-editor = {
enable = true;
package = pkgs.zed-editor;
extensions = [
"html" "html-snippets"
"svelte" "svelte-snippets"
"wakatime" "discord-presence"
"catppuccin" "catppuccin-icons"
"git-firefly"
"nix"
];
extensions = [ "nix" ];
userSettings = {
diff_view_style = "unified";
cli_default_open_behavior = "existing_window";
format_on_save = "off";
features.edit_prediction_provider = "copilot";
vim_mode = true;
git.inline_blame.enabled = true;
gutter.line_numbers = true;
@ -27,27 +19,6 @@
file_types.tailwindcss = [ "*.css" ];
auto_install_extensions.catppuccin-icons = true;
icon_theme = "Catppuccin Mocha";
git_panel.tree_view = true;
diagnostics = {
button = true;
include_warnings = true;
inline = {
enabled = true;
update_debounce_ms = 150;
padding = 4;
min_column = 0;
max_severity = null;
};
};
agent = {
tool_permissions.default = "allow";
default_model = {
provider = "copilot_chat";
model = "claude-sonnet-4.6";
effort = "high";
enable_thinking = false;
};
};
theme = {
mode = "dark";
light = "Catppuccin Mocha (sapphire)";

View file

@ -1,4 +1,4 @@
{ git, hostname, flake-path, zsh-theme, ... }: {
{ hostname, flake-path, zsh-theme, ... }: {
programs = {
pay-respects = {
enable = true;
@ -32,14 +32,14 @@
'';
shellAliases = {
"cd-gvfs" = "cd /run/user/$(id -u)/gvfs";
"wlp-set" = "awww img --transition-type=grow --transition-duration=1";
"wlp-set" = "swww img --transition-type=grow --transition-duration=1";
"ssh" = "TERM=xterm-256color ssh";
"cd" = "z";
"sys" = "sudo systemctl --runtime";
"sys-log" = "journalctl -o cat -f -b -u";
"sys-log" = "journalctl -f -b -u";
"user" = "systemctl --user --runtime";
"user-log" = "journalctl -o cat -f -b --user-unit";
"user-log" = "journalctl -f -b --user-unit";
"ts" = "sudo tailscale";
"tsip" = "tailscale ip -4";
@ -64,10 +64,9 @@
"wm-disp" = "wm-ctl dispatch dpms";
"gh-author-setup" = "git config user.name $(gh api -H \"Accept: application/vnd.github+json\" -H \"X-GitHub-Api-Version: 2022-11-28\" /user | jq -r .login) && git config user.email $(gh api -H \"Accept: application/vnd.github+json\" -H \"X-GitHub-Api-Version: 2022-11-28\" /user/emails | jq -r \".[1].email\")";
"fg-create-repo" = "git remote add origin ${git.server}/${git.username}/$(basename $PWDw).git && git push";
"convert-pdf" = "libreoffice --headless --convert-to pdf";
"mcl" = "portablemc start -l $(cat ~/.minecraft/portablemc-launch-params.json | jq -r .email) $(cat ~/.minecraft/portablemc-launch-params.json | jq -r .version)";
"mcl" = "portablemc start -l $(cat .minecraft/portablemc-launch-params.json | jq -r .email) $(cat .minecraft/portablemc-launch-params.json | jq -r .version)";
"mc" = "ferium upgrade; mcl";
};
initContent = ''

View file

@ -1,17 +1,9 @@
{ username, ctp-opt, ... }: {
{ username, ... }: {
imports = [
./core/shell.nix
./core/cli.nix
./core/zsh.nix
];
catppuccin = {
enable = true;
hyprlock.useDefaultConfig = false;
flavor = ctp-opt.flavor;
accent = ctp-opt.accent;
};
home = {
stateVersion = "24.11";
username = "${username}";

View file

@ -1,23 +1,23 @@
{ pkgs, ... }: {
imports = [
./rice/compositor.nix
./rice/lockscreen.nix
./rice/keybinds.nix
./rice/logout.nix
./rice/notifs.nix
./rice/hyprland.nix
./rice/hyprlock.nix
./rice/waybar.nix
./rice/rofi.nix
./rice/wlogout.nix
./rice/hypridle.nix
./rice/dunst.nix
./rice/cursor.nix
./rice/theme.nix
./rice/menu.nix
./rice/idle.nix
./rice/bar.nix
./misc/handlers.nix
./misc/phone.nix
./rice/keybinds.nix
./misc/kde-connect.nix
./core/apps.nix
./core/code.nix
./core/zed.nix
./core/xdg.nix
];
services = {
awww.enable = true;
swww.enable = true;
hyprpolkitagent.enable = true;
};

View file

@ -1,18 +0,0 @@
{ pkgs, ... }:
let
ferium-installer-script = pkgs.writeShellScript "ferium-installer" ''
mod=$(echo "$1" | awk -F'/' '{print $NF}')
${pkgs.kitty}/bin/kitty sh -c "ferium add $mod; read"
'';
in
{
xdg.desktopEntries."ferium-installer" = {
name = "Intercept Modrinth Links to Ferium";
exec = "${ferium-installer-script} %u";
mimeType = [ "x-scheme-handler/modrinth" ];
};
xdg.mimeApps.defaultApplications = {
"x-scheme-handler/modrinth" = "ferium-installer.desktop";
};
}

View file

@ -6,7 +6,7 @@
lock_cmd = "hyprlock";
unlock_cmd = "pkill -USR1 hyprlock";
before_sleep_cmd = "hyprctl dispatch dpms off && hyprlock";
after_sleep_cmd = "hyprctl dispatch dpms on";
after_sleep_cmd = "hyprctl dispatch dpms on && pkill -USR2 hyprlock";
};
listener = [
{

View file

@ -30,7 +30,7 @@
#"dunst &"
#"hypridle &"
#"awww-daemon &"
#"swww-daemon &"
"uwsm app -s s -- waybar &"
"uwsm app -s b -- sunshine &"
@ -51,7 +51,6 @@
"GTK_APPLICATION_PREFER_DARK_THEME,1"
"GTK_THEME,Adwaita:dark"
"QT_QPA_PLATFORMTHEME,kvantum"
"QT_STYLE_OVERRIDE,kvantum"
];
general = {
@ -141,7 +140,7 @@
layerrule = [
"no_anim on, match:namespace selection" # hyprshot overlay
"no_anim on, match:namespace hyprpicker"
"animation fade, match:namespace awww-daemon"
"animation fade, match:namespace swww-daemon"
"animation fade, match:namespace logout_dialog"
"animation fade, match:namespace hyprshutdown"
"above_lock 2, match:namespace notifications"
@ -156,8 +155,7 @@
"stay_focused on, suppress_event fullscreen maximize, dim_around on, float on, match:title ^(Hyprland Polkit Agent|Unlock Login Keyring|KeePassXC -.*)$"
"float on, match:title ^(Open|Print|Save|Rename|Move|Copy|Confirm).*"
"float on, match:title ^(Preferences|Settings|Options|About|Passbolt).*"
"float on, match:title ^(MainPicker|Volume Control|File Operation Progress|Network Connections|Choose an Application)$"
"float on, match:title ^(Please wait)$"
"float on, match:title ^(MainPicker|Volume Control|File Operation Progress|Network Connections|Choose an Application| )$"
];
};
};

View file

@ -95,10 +95,10 @@
"SUPER, N, exec, uwsm app -- rofi-network-manager"
"SUPER, J, exec, notify-send -u critical ${hostname} 'Caffein Mode' && notify-send '(SUPER+X to reset)' && systemctl --user stop hypridle"
"SUPER, K, exec, notify-send -u critical ${hostname} 'Focus Mode' && notify-send '(SUPER+X to reset)' && systemctl --user stop awww && pkill -SIGUSR1 waybar && hyprctl --batch 'keyword decoration:inactive_opacity 1.0; keyword decoration:blur:enabled 0; keyword general:gaps_in 0; keyword general:gaps_out 0; keyword general:border_size 1; keyword decoration:rounding 0; keyword decoration:shadow:enabled false'"
"SUPER, K, exec, notify-send -u critical ${hostname} 'Focus Mode' && notify-send '(SUPER+X to reset)' && systemctl --user stop swww && pkill -SIGUSR1 waybar && hyprctl --batch 'keyword decoration:inactive_opacity 1.0; keyword decoration:blur:enabled 0; keyword general:gaps_in 0; keyword general:gaps_out 0; keyword general:border_size 1; keyword decoration:rounding 0; keyword decoration:shadow:enabled false'"
"SUPER, B, submap, disabled-all-keybinds"
"SUPER, H, exec, notify-send ${hostname} 'Animations Off' && hyprctl keyword animations:enabled 0"
"SUPER, X, exec, dunstctl close-all && hyprctl reload && hyprctl dispatch submap reset && pkill -SIGUSR2 waybar && systemctl --user restart awww hypridle fusuma"
"SUPER, X, exec, dunstctl close-all && hyprctl reload && hyprctl dispatch submap reset && pkill -SIGUSR2 waybar && systemctl --user restart swww hypridle fusuma"
"SUPER, Z, exec, dunstctl close-all"
"SUPER SHIFT, S, exec, hyprshot -zm region -o ~/Pictures/Screenshots; killall -9 hyprpicker hyprshot"
@ -106,7 +106,9 @@
", PRINT, exec, hyprshot -zm region -o ~/Pictures/Screenshots; killall -9 hyprpicker hyprshot"
"SUPER, R, exec, rofi -show drun -show-icons -display-drun '' -run-command \"uwsm app -- {cmd}\""
"SUPER, RETURN, exec, ls ~/Projects | rofi -dmenu -p \"Open Project\" | xargs -I {} sh -c 'mkdir -p ~/Projects/\"{}\" && zeditor ~/Projects/\"{}\"'"
"SUPER, RETURN, exec, rofi -show window -show-icons -drun-display '' -window-format '{c} {t}'"
"SUPER CTRL, RETURN, exec, rofi rofi -dmenu -p 'run nixpkgs' -lines 0 < /dev/null | xargs -r -I {} kitty -- nix run 'nixpkgs#{}'"
"SUPER ALT, RETURN, exec, rofi rofi -dmenu -p 'shell nixpkgs' -lines 0 < /dev/null | xargs -r -I {} kitty -- nde`ix shell 'nixpkgs#{}'"
"SUPER, V, exec, rofi -modi clipboard:cliphist-rofi-img -show clipboard -show-icons"
# "SUPER, B, exec, rofi -show calc -modi calc -no-show-match -no-sort"
@ -124,9 +126,8 @@
"SUPER, W, fullscreen, 1"
"SUPER, S, fullscreen, 0"
"SUPER, F, togglefloating,"
"SUPER, G, layoutmsg, togglesplit"
"SUPER, G, togglesplit,"
"SUPER, L, exec, loginctl lock-session"
"SUPER SHIFT, L, exec, hyprctl dispatch dpms off && loginctl lock-session && sleep 1 && hyprctl dispatch dpms on"
"SUPER, down, togglespecialworkspace, hidden"
"SUPER SHIFT, down, movetoworkspace, special:hidden"

View file

@ -1,16 +1,23 @@
{ lib, pkgs, ctp-opt, rice, ... }: {
catppuccin = {
enable = true;
hyprlock.useDefaultConfig = false;
flavor = ctp-opt.flavor;
accent = ctp-opt.accent;
};
dconf = {
enable = true;
settings."org/gnome/desktop/interface" = {
color-scheme = "prefer-dark";
gtk-theme = lib.mkForce "Adwaita-dark";
gtk-theme = "Adwaita-dark";
};
};
gtk = {
enable = true;
gtk3.extraConfig.gtk-application-prefer-dark-theme = 1;
gtk4.theme = null;
iconTheme = {
name = "Papirus-Dark";
package = lib.mkForce pkgs.papirus-icon-theme;
@ -23,11 +30,6 @@
qt = {
enable = true;
kvantum = {
enable = true;
themes = with pkgs; [ catppuccin-kvantum ];
settings.General.theme = "catppuccin-${ctp-opt.flavor}-${ctp-opt.accent}";
};
platformTheme.name = "kvantum";
style = {
name = "kvantum";

View file

@ -45,8 +45,8 @@
interval = 1;
format = " {usage:2}% {avg_frequency}GHz";
on-click = "auto-cpufreq-gtk";
on-click-right = "pkexec auto-cpufreq --force powersave && notify-send ${hostname} \"CPU Governor Powersave Overide\"";
on-click-middle = "pkexec auto-cpufreq --force reset && notify-send ${hostname} \"CPU Governor Overide Reset\"";
on-click-right = "pkexec tlp power-saver && notify-send ${hostname} \"TLP set to: $(tlp-stat -s | grep 'Power profile' | awk -F '=' '{print $2}' | xargs)\"";
on-click-middle = "pkexec tlp start && notify-send ${hostname} \"TLP set to: $(tlp-stat -s | grep 'Power profile' | awk -F '=' '{print $2}' | xargs)\"";
};
"memory" = {
states = {

View file

@ -5,35 +5,26 @@
{
imports =
[ (modulesPath + "/installer/scan/not-detected.nix")
[ (modulesPath + "/profiles/qemu-guest.nix")
];
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usbhid" "uas" "usb_storage" "sd_mod" ];
boot.initrd.availableKernelModules = [ "uhci_hcd" "ehci_pci" "ahci" "virtio_pci" "virtio_scsi" "sd_mod" "sr_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{ device = "/dev/disk/by-uuid/e5a7d45d-b9e9-43e7-ba5f-f4e67821bd0b";
{ device = "/dev/disk/by-uuid/e33ab472-e518-4b4d-89d1-d75cfecb9f06";
fsType = "ext4";
};
fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/EC01-36B5";
{ device = "/dev/disk/by-uuid/880C-9F0A";
fsType = "vfat";
options = [ "fmask=0022" "dmask=0022" ];
options = [ "fmask=0077" "dmask=0077" ];
};
swapDevices = [ ];
# 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.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.enp2s0.useDHCP = lib.mkDefault true;
# networking.interfaces.wlp3s0.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

View file

@ -5,16 +5,15 @@
host = "127.0.0.1";
port = 11434;
user = "ollama";
home = "/mnt/data/apps/ollama";
home = "/mnt/data/ollama";
loadModels = [
"gemma3n:e4b" # "gemma3n:e2b"
"qwen3-coder-next:cloud" # "codellama:7b" "starcoder:3b"
# "codellama:7b" "starcoder:3b"
];
};
open-webui = {
enable = true;
port = 8080;
environmentFile = "/mnt/data/apps/ollama/.env";
environment = {
OLLAMA_BASE_URL = "http://localhost:11434";
# WEBUI_AUTH = "False";

View file

@ -1,8 +1,8 @@
{ homelab, ... }: {
{ config, homelab, ... }: {
services.pocket-id = {
enable = true;
credentials.ENCRYPTION_KEY = "/mnt/data/apps/pocketid/encryption-key";
dataDir = "/mnt/data/apps/pocketid/data";
credentials.ENCRYPTION_KEY = config.sops.secrets.pocketid_encryption_key.path;
dataDir = "/mnt/data/pocketid/data";
settings = {
PORT = "1411";
HOST = "127.0.0.1";

View file

@ -1,14 +0,0 @@
{ pkgs, ... }: {
environment.systemPackages = with pkgs; [ copyparty-most ];
systemd.services.copyparty = {
description = "File Sharing Service";
enable = true;
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = "${pkgs.copyparty-most}/bin/copyparty -c /mnt/share/cfg/files.conf";
Restart = "on-failure";
};
};
}

View file

@ -1,12 +0,0 @@
{ username, ... }: {
services.code-server = {
enable = true;
host = "127.0.0.1";
port = 8443;
user = username;
auth = "none";
disableTelemetry = true;
extensionsDir = "/mnt/data/apps/code-server/extensions";
userDataDir = "/mnt/data/apps/code-server/user-data";
};
}

View file

@ -1,6 +1,5 @@
{ homelab, lib, ... }: let
dockge-dir = "/mnt/data/apps/dockge";
stacks-dir = "${dockge-dir}/stacks";
stacks-dir = "/mnt/data/dockge/stacks";
in {
virtualisation.oci-containers.containers."dockge" = {
image = "louislam/dockge:nightly";
@ -9,7 +8,7 @@ in {
};
volumes = [
"${stacks-dir}:${stacks-dir}:rw"
"${dockge-dir}/data:/app/data:rw"
"/mnt/data/dockge/data:/app/data:rw"
"/var/run/docker.sock:/var/run/docker.sock:rw"
];
ports = [

View file

@ -1,4 +1,4 @@
{ timezone, homelab, ... }: let
{ config, timezone, homelab, ... }: let
rss = [
"https://www.raspberrypi.com/news/feed/"
"https://www.jeffgeerling.com/blog.xml"
@ -53,9 +53,31 @@
[ "Google Web Results Only" "!s" "https://google.com/search?udm=14&q={QUERY}" ]
];
monitor = [
[ "Hypervisor" "https://10.3.14.69:8006/" ]
[ "Router" "http://10.3.14.1:80/" ]
[ "DNS" "http://localhost:8088/" ]
[ "CDN" "http://nas.local:3000/" ]
[ "Proxy" "https://proxy.${homelab.domain}/" ]
];
external = [
[ "Proxmox" "proxmox" "https://server.proxy.${homelab.domain}" "http://server.dns.${homelab.domain}:8006/" ]
[ "OpenWRT" "openwrt" "https://router.proxy.${homelab.domain}" "http://router.dns.${homelab.domain}:80/" ]
[ "HomeAssistant" "homeassistant" "https://home.proxy.${homelab.domain}" "http://home.dns.${homelab.domain}:8123/" ]
[ "OpenMediaVault" "openmediavault" "https://nas.local:80" "http://nas.local:80/" ]
[ "ApacheHTTPD" "apache" "https://nas.local:3000" "http://nas.local:3000/" ]
];
services = [
[ "PocketID" "authentik" "https://auth.${homelab.domain}" "http://localhost:1411/" ]
[ "Forgejo" "forgejo" "https://git.${homelab.domain}" "http://localhost:5080/" ]
[ "AdGuardHome" "adguard" "https://dns.proxy.${homelab.domain}" "http://localhost:8088/" ]
[ "Traefik" "traefikproxy" "https://dynamic.proxy.${homelab.domain}/dashboard/" "http://localhost:81/dashboard/" ]
[ "Immich" "immich" "https://gallery.proxy.${homelab.domain}" "http://localhost:2283/" ]
[ "Jellyfin" "jellyfin" "https://media.proxy.${homelab.domain}" "http://localhost:8096/" ]
[ "VaultWarden" "vaultwarden" "https://pass.proxy.${homelab.domain}" "http://localhost:8060/" ]
[ "Ollama" "ollama" "https://ai.proxy.${homelab.domain}" "http://localhost:8080/" ]
[ "Dockge" "docker" "https://containers.proxy.${homelab.domain}" "http://localhost:5001/" ]
[ "Guacamole" "apacheguacamole" "https://remote.proxy.${homelab.domain}/guacamole" "http://localhost:8085/guacamole/" ]
];
bookmarks = [
[ "Tailscale" "tailscale" "https://login.tailscale.com/" ]
[ "Cloudflare" "cloudflare" "https://dash.cloudflare.com/" ]
@ -74,6 +96,7 @@ in {
};
services.glance = {
enable = true;
environmentFile = config.sops.secrets.glance_env.path;
settings = {
server = {
host = "127.0.0.1";
@ -89,101 +112,6 @@ in {
};
pages = [
{
name = "Dashboard";
show-mobile-header = true;
width = "slim";
columns = [
{
size = "small";
widgets = [
{
type = "monitor";
title = "Critical Systems";
cache = "15s";
style = "compact";
show-failing-only = true;
sites = map (e: {
same-tab = true;
allow-insecure = true;
title = builtins.elemAt e 0;
url = builtins.elemAt e 1;
}) monitor;
}
{
type = "dns-stats";
title = "DNS Stats";
service = "adguard";
url = "http://localhost:8088/";
hour-format = "12h";
}
{
type = "bookmarks";
groups = [
{
links = [{
same-tab = true;
title = "NixFlake";
icon = "si:nixos";
url = "https://flake.satr14.my.id";
}];
}
{
links = map (e: {
same-tab = true;
title = builtins.elemAt e 0;
icon = "si:${builtins.elemAt e 1}";
url = builtins.elemAt e 2;
alt-status-codes = [ 401 ];
}) bookmarks;
}
];
}
{
type = "to-do";
id = "tasks";
}
];
}
{
size = "full";
widgets = [
{
type = "server-stats";
servers = [{
type = "local";
mountpoints = {
"/boot".hide = true;
"/nix/store".hide = true;
"/var/lib/vaultwarden".hide = true;
"/var/lib/private/cryptpad".hide = true;
"/var/lib/acme/proxy.satr14.my.id".hide = true;
};
}];
}
{
type = "monitor";
cache = "1m";
title = "Services";
sites = map (e: {
same-tab = true;
allow-insecure = true;
title = builtins.elemAt e 0;
icon = "si:${builtins.elemAt e 1}";
url = builtins.elemAt e 2;
check-url = builtins.elemAt e 3;
}) homelab.dash;
}
{
type = "docker-containers";
title = "Containers";
format-container-names = true;
hide-by-default = true;
}
];
}
];
}
{
name = "Home";
show-mobile-header = true;
@ -282,6 +210,107 @@ in {
}
];
}
{
name = "Dashboard";
show-mobile-header = true;
width = "slim";
columns = [
{
size = "small";
widgets = [
{
type = "monitor";
title = "Critical Systems";
cache = "15s";
style = "compact";
show-failing-only = true;
sites = map (e: {
same-tab = true;
allow-insecure = true;
title = builtins.elemAt e 0;
url = builtins.elemAt e 1;
}) monitor;
}
{
type = "dns-stats";
title = "DNS Stats";
service = "adguard";
url = "http://localhost:8088/";
hour-format = "12h";
}
{
type = "bookmarks";
groups = [
{
links = [{
same-tab = true;
title = "NixFlake";
icon = "si:nixos";
url = "https://flake.satr14.my.id";
}];
}
{
links = map (e: {
same-tab = true;
title = builtins.elemAt e 0;
icon = "si:${builtins.elemAt e 1}";
url = builtins.elemAt e 2;
}) bookmarks;
}
];
}
{
type = "to-do";
id = "tasks";
}
];
}
{
size = "full";
widgets = [
{
type = "server-stats";
servers = [{
type = "local";
mountpoints."/nix/store".hide = true;
}];
}
{
type = "monitor";
cache = "1m";
title = "External";
sites = map (e: {
same-tab = true;
allow-insecure = true;
title = builtins.elemAt e 0;
icon = "si:${builtins.elemAt e 1}";
url = builtins.elemAt e 2;
check-url = builtins.elemAt e 3;
}) external;
}
{
type = "monitor";
cache = "1m";
title = "Services";
sites = map (e: {
same-tab = true;
allow-insecure = true;
title = builtins.elemAt e 0;
icon = "si:${builtins.elemAt e 1}";
url = builtins.elemAt e 2;
check-url = builtins.elemAt e 3;
}) services;
}
{
type = "docker-containers";
title = "Containers";
format-container-names = true;
hide-by-default = true;
}
];
}
];
}
];
};
};

View file

@ -1,7 +0,0 @@
{ pkgs, ... }: {
services.postgresql = {
enable = true;
dataDir = "/mnt/data/apps/postgresql";
package = pkgs.postgresql_16;
};
}

View file

@ -1,46 +0,0 @@
{ lib, pkgs, homelab, ... }: let
domain = "docs.${homelab.domain}";
sandbox = "docs-sandbox.${homelab.domain}";
in {
services.cryptpad = {
enable = true;
settings = {
websocketPort = 7091;
httpPort = 7090;
httpAddress = "127.0.0.1";
httpUnsafeOrigin = "https://${domain}";
httpSafeOrigin = "https://${sandbox}";
blockDailyCheck = true;
disableIntegratedEviction = true;
adminKeys = [
"[satr14@docs.satr14.my.id/f1A82fmBuqQka2bNqrCb1WbB9r2ex5A3rdys5xLX3Hc=]"
];
};
};
systemd.tmpfiles.rules = lib.singleton "L+ /var/lib/cryptpad/customize/application_config.js - - - - ${pkgs.writeText "cryptpad-application-config.js" ''
(() => {
const factory = (AppConfig) => {
AppConfig.disableAnonymousPadCreation = true;
AppConfig.disableAnonymousStore = true;
AppConfig.defaultDarkTheme = true;
return AppConfig;
};
if (typeof(module) !== 'undefined' && module.exports) {
module.exports = factory(
require('../www/common/application_config_internal.js')
);
} else if ((typeof(define) !== 'undefined' && define !== null) && (define.amd !== null)) {
define(['/common/application_config_internal.js'], factory);
}
})();
''}";
fileSystems."/var/lib/private/cryptpad" = {
device = "/mnt/data/apps/cryptpad";
depends = [ "/mnt/data" ];
options = [ "bind" "nofail" ];
fsType = "none";
};
}

View file

@ -1,4 +1,4 @@
{ lib, ... }: {
{ lib, homelab, ... }: {
users.users.immich.extraGroups = [ "video" "render" ];
services = {
@ -6,7 +6,7 @@
enable = true;
port = 2283;
host = "127.0.0.1";
mediaLocation = "/mnt/data/gallery";
mediaLocation = "/mnt/gallery";
accelerationDevices = null;
environment.DB_URL = lib.mkForce "postgresql:///immich?host=/var/run/postgresql&user=immich"; # https://github.com/immich-app/immich/issues/26140
machine-learning.enable = true;

View file

@ -1,10 +1,17 @@
{ pkgs, homelab, ... }: {
services = {
forgejo = {
services.forgejo = {
enable = true;
lfs.enable = true;
stateDir = "/mnt/data/apps/forgejo";
stateDir = "/mnt/data/forgejo";
package = pkgs.forgejo;
#secrets = {
# oauth2.JWT_SECRET = "/mnt/data/forgejo/custom/conf/oauth2_jwt_secret";
# server.LFS_JWT_SECRET = "/mnt/data/forgejo/custom/conf/lfs_jwt_secret";
# security = {
# INTERNAL_TOKEN = "/mnt/data/forgejo/custom/conf/internal_token";
# SECRET_KEY = "/mnt/data/forgejo/custom/conf/secret_key";
# };
#};
settings = {
server = {
DISABLE_SSH = false;
@ -25,7 +32,7 @@
DISABLE_REGISTRATION = true;
ENABLE_OPENID_SIGNIN = false;
ENABLE_OPENID_SIGNUP = false;
ENABLE_INTERNAL_SIGNIN = false;
ENABLE_INTERNAL_SIGNIN = true; # TODO: set false after migration complete
SHOW_REGISTRATION_BUTTON = false;
ALLOW_ONLY_EXTERNAL_REGISTRATION = true;
ALLOW_ONLY_INTERNAL_REGISTRATION = false;
@ -35,21 +42,7 @@
repository = {
DISABLE_STARS = true;
DISABLE_FORKS = true;
ENABLE_PUSH_CREATE_USER = true;
};
};
};
gitea-actions-runner.instances.nixos-deploy = {
enable = true;
name = "nixos-server-runner";
url = "http://localhost:5080"; #"https://git.proxy.${homelab.domain}";
tokenFile = "/mnt/data/apps/forgejo/token-runner";
labels = [ "self-hosted:host" ];
hostPackages = with pkgs; [ bash coreutils git nix openssh bun ];
};
};
systemd.services = {
"gitea-runner-nixos-deploy".restartIfChanged = false;
"forgejo".restartIfChanged = false;
};
}

View file

@ -1,114 +0,0 @@
{ inputs, lib, pkgs, ... }: let
production = true;
ram-allocation-mb = 12288;
rcon-pass = "howdy";
modpack = let
commit = "8523f89493ace13087eb68cd9fe3b5eb4f669440";
path = if production then "commit/${commit}" else "branch/main";
in pkgs.fetchPackwizModpack {
packHash = "sha256-xB9Oc/aneogSQ9r7L42vyVM6xwq+QkoTaXYNuUzeo6M=";
url = "https://git.satr14.my.id/satr14/server-modpack/raw/${path}/pack.toml";
};
in {
imports = [ inputs.mc.nixosModules.minecraft-servers ];
nixpkgs.overlays = [ inputs.mc.overlay ];
powerManagement.cpuFreqGovernor = "powersave"; # performance governor causes overheating and thermal throttling, works fine with powesave
boot.kernel.sysctl = {
"vm.nr_hugepages" = (ram-allocation-mb / 2) + 512; # (heap_mb / 2MB per page) + 512 pages (1GB) for ZGC off-heap overhead
"vm.swappiness" = 10;
};
services.minecraft-servers = {
enable = true;
eula = true;
managementSystem.systemd-socket.enable = true;
# ^^^ https://github.com/Infinidoge/nix-minecraft/issues/119
# TODO: figure out how to set gamerules on start
# gamerules to disable: locator_bar, mob_explosion_drop_decay, (and possibly) reduced_debug_info, global_sound_events
# gamerules to enable (temporarily): noend:disable_end
servers.da-s3 = {
enable = true;
autoStart = true;
restart = "always";
enableReload = production;
# extraReload = ''
# function rcon() {
# ${pkgs.rcon-cli}/bin/rcon-cli -p ${rcon-pass} $@
# }
# rcon "gamerule locator_bar false"
# rcon "gamerule mob_explosion_drop_decay false"
# rcon "gamerule reduced_debug_info false"
# rcon "gamerule global_sound_events false"
# '';
operators = lib.mkIf (!production) {
"satr14" = {
uuid = "54441a30-fe73-46e7-adca-c476bd4fc6d2";
bypassesPlayerLimit = true;
level = 4;
};
};
serverProperties = {
# server-ip = "localhost";
server-port = 25565;
server-name = "Minecraft Server";
motd = "§lSeason 3§r - §dExplorers Creativity 🔥";
log-ips = false; # TODO: figure out how to get ips from cloudflared tunnel
difficulty = "normal";
gamemode = "survival";
max-world-size = 25000;
spawn-protection = 0;
pvp = true;
online-mode = true;
enable-query = true;
enforce-secure-profile = false;
pevent-proxy-connections = false;
allow-flight = false;
player-idle-timeout = 0;
view-distance = 12;
simulation-distance = 4;
enable-rcon = true;
sync-chunk-writes = false;
"rcon.password" = rcon-pass;
"rcon.port" = 25575;
};
symlinks = lib.mapAttrs'
(name: _: lib.nameValuePair "mods/${name}" "${modpack}/mods/${name}")
(builtins.readDir "${modpack}/mods");
package = pkgs.fabricServers.fabric-1_21_11.override {
jre_headless = pkgs.javaPackages.compiler.temurin-bin.jdk-25;
loaderVersion = "0.19.2";
};
jvmOpts = let flags = [
"-Xms${toString ram-allocation-mb}M"
"-Xmx${toString ram-allocation-mb}M"
"-XX:+UseZGC" # Use ZGC (requires Java v25+, 8+ CPU cores, 10GB+ RAM)
"-XX:+UseCompactObjectHeaders" # Use compact object headers (requires Java v16+, saves a couple of bits per object)
"--add-modules=jdk.incubator.vector" # Exposes SIMD instructions (requires full JDK, useful with performance mods)
"-XX:+UseLargePages" # Large pages support (requires hugepages configured on the system)
"-XX:+AlwaysPreTouch" # Pre-allocates memory on startup, OS claims it immediately for JVM instead of negotiating it
"-XX:+DisableExplicitGC" # Disables mods from manually invoking the GC
"-XX:+PerfDisableSharedMem" # Disables constant /tmp writes for JVM metrics
"-XX:ZAllocationSpikeTolerance=5" # Helps when server is active with many players
"-XX:SoftMaxHeapSize=${toString (ram-allocation-mb - 2048)}M" # Leave 2GB headroom
"-XX:ZCollectionInterval=1" # Force a GC cycle at minimum every second
"-XX:ConcGCThreads=8" # Threads ZGC uses for concurrent work
]; in lib.concatStringsSep " " flags;
};
};
}

View file

@ -5,7 +5,6 @@
services = {
jellyfin = {
enable = true;
dataDir = "/mnt/data/apps/jellyfin";
hardwareAcceleration = {
enable = true;
device = "/dev/dri/renderD128";

View file

@ -1,9 +0,0 @@
{ homelab, ... }: {
services.ntfy-sh = {
enable = true;
settings = {
listen-http = "127.0.0.1:8067";
base-url = "https://ntfy.proxy.${homelab.domain}";
};
};
}

View file

@ -1,20 +1,14 @@
{ homelab, ... }: {
{ config, homelab, ... }: {
services.vaultwarden = {
enable = true;
domain = "pass.proxy.${homelab.domain}";
backupDir = "/mnt/data/apps/vaultwarden/backups";
environmentFile = "/mnt/data/apps/vaultwarden/.env";
backupDir = "/mnt/data/vaultwarden/backups";
environmentFile = config.sops.secrets.vaultwarden_env.path;
config = {
ROCKET_PORT = 8060;
ROCKET_ADDRESS = "127.0.0.1";
ROCKET_LOG = "critical";
SIGNUPS_ALLOWED = true;
};
};
fileSystems."/var/lib/vaultwarden" = {
device = "/mnt/data/apps/vaultwarden/data";
depends = [ "/mnt/data" ];
options = [ "bind" "nofail" ];
fsType = "none";
};
}

View file

@ -1,5 +1,28 @@
{ pkgs, homelab, lib, ... }: let
htpasswd = "/mnt/data/apps/nginx/htpasswd";
{ config, homelab, lib, ... }: let
base = "proxy.${homelab.domain}";
hosts = {
"server" = { dest = "https://server.dns.${homelab.domain}:8006"; auth = false; };
"router" = { dest = "http://router.dns.${homelab.domain}:80"; auth = false; };
"home" = { dest = "http://home.dns.${homelab.domain}:8123"; auth = false; };
"dynamic" = { dest = "http://127.0.0.1:8082"; auth = true; };
"dns" = { dest = "http://localhost:8088"; auth = true; };
"containers" = { dest = "http://localhost:5001"; auth = false; };
"gallery" = { dest = "http://localhost:2283"; auth = false; };
"remote" = { dest = "http://localhost:8085"; auth = false; };
"media" = { dest = "http://localhost:8096"; auth = false; };
"pass" = { dest = "http://localhost:8060"; auth = false; };
"auth" = { dest = "http://localhost:1411"; auth = false; };
"git" = { dest = "http://localhost:5080"; auth = false; };
"ai" = { dest = "http://localhost:8080"; auth = false; };
"@" = { dest = "http://localhost:5070"; auth = false; };
};
redirects = {
"www" = "https://proxy.${homelab.domain}";
"dash" = "https://${homelab.domain}";
"immich" = "https://gallery.proxy${homelab.domain}";
};
exta-conf = ''
# proxy_set_header X-Auth-User $remote_user;
proxy_read_timeout 600s;
@ -18,35 +41,24 @@ in {
security.acme = {
acceptTerms = true;
defaults.email = "admin@${homelab.domain}";
certs."${homelab.proxy.base}" = {
domain = "*.${homelab.proxy.base}";
extraDomainNames = [ homelab.proxy.base ];
environmentFile = "/mnt/data/apps/acme/cf-api.env";
certs."${base}" = {
domain = "*.${base}";
extraDomainNames = [ base ];
dnsProvider = "cloudflare";
# ^^^contents: CLOUDFLARE_DNS_API_TOKEN=XXXXX
environmentFile = config.sops.templates."cloudflare.env".path;
};
};
fileSystems."/var/lib/acme/${homelab.proxy.base}" = {
device = "/mnt/data/apps/acme/${homelab.proxy.base}";
depends = [ "/mnt/data" ];
options = [ "bind" "nofail" ];
fsType = "none";
};
services = {
nginx = {
enable = true;
package = pkgs.angie;
recommendedProxySettings = true;
recommendedTlsSettings = true;
recommendedGzipSettings = true;
recommendedOptimisation = true;
virtualHosts = {
"_" = {
default = true;
forceSSL = true;
useACMEHost = homelab.proxy.base;
useACMEHost = base;
# locations."/".return = "404";
locations."/" = {
proxyPass = "http://127.0.0.1:81"; # traefik for docker container dynamic proxy
@ -54,12 +66,12 @@ in {
extraConfig = exta-conf;
};
};
} // lib.mapAttrs' (subdomain: cfg: lib.nameValuePair "${subdomain}.${homelab.proxy.base}" {
useACMEHost = homelab.proxy.base;
} // lib.mapAttrs' (subdomain: cfg: lib.nameValuePair "${subdomain}.${base}" {
useACMEHost = base;
forceSSL = true;
locations."/".return = "301 ${cfg}";
}) homelab.proxy.redirects // lib.mapAttrs' (subdomain: cfg: lib.nameValuePair (if subdomain == "@" then homelab.proxy.base else "${subdomain}.${homelab.proxy.base}") {
useACMEHost = homelab.proxy.base;
locations."/".return = "301 ${base}";
}) redirects // lib.mapAttrs' (subdomain: cfg: lib.nameValuePair (if subdomain == "@" then base else "${subdomain}.${base}") {
useACMEHost = base;
forceSSL = true;
extraConfig = ''
access_log /var/log/nginx/${subdomain}.access.log;
@ -68,16 +80,13 @@ in {
locations."/" = {
proxyPass = cfg.dest;
proxyWebsockets = true;
basicAuthFile = if cfg.auth then htpasswd else null;
basicAuthFile = if cfg.auth then config.sops.secrets.nginx_htpasswd.path else null;
extraConfig = exta-conf;
};
}) homelab.proxy.hosts;
}) hosts;
};
traefik = {
enable = true;
dynamicConfigOptions = {
http.middlewares.auth.basicAuth.usersFile = htpasswd;
};
staticConfigOptions = {
entryPoints = {
traefik.address = "127.0.0.1:8082";
@ -97,6 +106,7 @@ in {
providers.docker = {
endpoint = "unix:///var/run/docker.sock";
exposedByDefault = false;
defaultRule = "Host(`ct-{{ normalize .Name }}.${base}`)";
};
};
};

View file

@ -0,0 +1,18 @@
{ ... }: {
services = {
guacamole-server = {
enable = true;
host = "127.0.0.1";
port = 4822;
};
guacamole-client = {
enable = true;
enableWebserver = true;
settings = {
guacd-hostname = "127.0.0.1";
guacd-port = 4822;
};
};
tomcat.port = 8085;
};
}

View file

@ -1,21 +0,0 @@
{ ... }: {
services.searx = {
enable = true;
redisCreateLocally = true;
environmentFile = "/mnt/data/apps/searxng/.env";
settings = {
server = {
bind_address = "127.0.0.1";
port = 8091;
secret_key = "$SECRET_KEY";
};
general = {
debug = false;
donation_url = false;
contact_url = false;
privacy_policy_url = false;
enable_metrics = true;
};
};
};
}

View file

@ -0,0 +1,34 @@
{ ... }: {
services = {
httpd = {
enable = true;
virtualHosts."cdn" = {
listen = [{ ip = "127.0.0.1"; port = 3000; }];
documentRoot = "/mnt/share";
};
};
samba = {
enable = true;
settings = {
global = {
workgroup = "WORKGROUP";
"disable netbios" = "yes";
"allow insecure wide links" = "yes";
"server min protocol" = "SMB2_02";
};
"NAS" = {
path = "/mnt/share";
browseable = "yes";
"read only" = "no";
"create mask" = "0664";
"force create mode" = "0664";
"directory mask" = "0775";
"force directory mode" = "0775";
"follow symlinks" = "yes";
"wide links" = "yes";
};
};
};
};
}

View file

@ -0,0 +1,59 @@
{ config, ... }: {
sops = {
defaultSopsFile = ../../../secrets/homelab.yaml;
age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
secrets = {
cloudflare_dns_api_token = {
owner = "acme";
group = "acme";
};
cloudflared_tunnel_credentials = {
owner = "cloudflared";
group = "cloudflared";
};
cloudflared_cert = {
owner = "cloudflared";
group = "cloudflared";
};
vaultwarden_env = {
owner = "vaultwarden";
group = "vaultwarden";
restartUnits = [ "vaultwarden.service" ];
};
glance_env = {
owner = "glance";
group = "glance";
restartUnits = [ "glance.service" ];
};
pocketid_encryption_key = {
owner = "root";
group = "root";
restartUnits = [ "pocket-id.service" ];
};
tailscale_authkey = {
owner = "root";
group = "root";
restartUnits = [ "tailscaled.service" ];
};
nginx_htpasswd = {
owner = "nginx";
group = "nginx";
restartUnits = [ "nginx.service" ];
};
};
templates."cloudflare.env" = {
owner = "acme";
group = "acme";
content = "CLOUDFLARE_DNS_API_TOKEN=${config.sops.placeholder.cloudflare_dns_api_token}";
};
};
}

View file

@ -1,11 +1,19 @@
{ pkgs, lib, homelab, ... }: {
{ config, pkgs, lib, homelab, ... }: let
routes = {
"git.${homelab.domain}" = "http://localhost:5080";
"auth.${homelab.domain}" = "http://localhost:1411";
"dash.${homelab.domain}" = "http://localhost:5070";
"media.${homelab.domain}" = "http://localhost:8096";
"gallery.${homelab.domain}" = "http://localhost:2284";
};
in {
services.cloudflared = {
enable = true;
tunnels.homelab = {
credentialsFile = "/mnt/data/apps/cloudflared/homelab.json";
certificateFile = "/mnt/data/apps/cloudflared/cert.pem";
credentialsFile = config.sops.secrets.cloudflared_tunnel_credentials.path;
certificateFile = config.sops.secrets.cloudflared_cert.path;
default = "http_status:404";
ingress = homelab.routes;
ingress = routes;
};
};
@ -23,7 +31,7 @@
script = lib.concatMapStringsSep "\n" (domain: ''
echo "Ensuring DNS route for ${domain}..."
${pkgs.cloudflared}/bin/cloudflared tunnel --origincert /mnt/data/apps/cloudflared/cert.pem route dns --overwrite-dns $(cat /mnt/data/apps/cloudflared/homelab.json | ${pkgs.jq}/bin/jq -r .TunnelID) ${domain} || true
'') (builtins.attrNames homelab.routes);
${pkgs.cloudflared}/bin/cloudflared tunnel --origincert ${config.sops.secrets.cloudflared_cert.path} route dns ${homelab.cf-tunnel-id} ${domain} || true
'') (builtins.attrNames routes);
};
}

View file

@ -1,82 +1,60 @@
{ pkgs, ... }: {
environment.systemPackages = with pkgs; [
# Disk & Storage
baobab
gnome-disk-utility
gparted
parted
ntfs3g
exfatprogs
smartmontools
rclone
ncdu
ventoy-full-qt
# System Monitoring & Hardware
htop
sysstat
powertop
lm_sensors
fastfetch
pciutils
usbutils
stress
stress-ng
# Networking
file-roller
gnome-network-displays
gnome-disk-utility
parted
smartmontools
lm_sensors
ntfs3g
virt-viewer
dconf2nix
pciutils
gparted
exfatprogs
pavucontrol
jq
powertop
fastfetch
ethtool
dig
dnslookup
nmap
netcat
traceroute
wakeonlan
cloudflared
cloud-utils
# Archives & Compression
file-roller
lsof
gucharmap
ncdu
zip
unzip
p7zip
# GUI Utilities
pavucontrol
gucharmap
lxappearance
blueman
shared-mime-info
usbutils
# Virtualization & Containers
virt-viewer
distrobox
# Android
android-tools
scrcpy
# Remote Access
freerdp
# Media
ffmpeg
# Printing
hplipWithPlugin
# CLI Essentials
android-tools
scrcpy
distrobox
ventoy-full-qt
ffmpeg
vim
wget
curl
openssl_3
htop
nmap
sysstat
netcat
p7zip
stress
stress-ng
wakeonlan
coreutils-full
jq
lsof
traceroute
lxappearance
freerdp
# Nix & Development
rcon-cli
dconf2nix
home-manager
nix-index
nixd

View file

@ -1,8 +1,8 @@
{ lib, homelab, ... }: let
{ config, lib, homelab, ... }: let
ts-flags = [
"--advertise-exit-node"
"--advertise-routes=10.3.14.0/24,192.168.1.0/24"
"--ssh"
"--ssh" # "--webclient"
];
in {
imports = [
@ -11,21 +11,16 @@ in {
./homelab/containers.nix
./homelab/gallery.nix
./homelab/tunnels.nix
./homelab/notify.nix
./homelab/search.nix
./homelab/remote.nix
./homelab/media.nix
./homelab/proxy.nix
./homelab/auth.nix
./homelab/pass.nix
./homelab/dash.nix
./homelab/code.nix
./homelab/docs.nix
./homelab/dns.nix
./homelab/git.nix
./homelab/cdn.nix
./homelab/ai.nix
./homelab/db.nix
./homelab/mc.nix
./homelab/sops.nix
./core/swapfile.nix
./core/oom.nix
@ -33,18 +28,13 @@ in {
./base.nix
];
users.users.root.openssh.authorizedKeys.keys = homelab.ssh-keys;
services = {
netbird.enable = true;
tailscale = {
services.tailscale = {
enable = true;
authKeyFile = "/mnt/data/apps/tailscale/authkey";
authKeyFile = config.sops.secrets.tailscale_authkey.path;
useRoutingFeatures = "server";
extraUpFlags = ts-flags;
extraSetFlags = ts-flags;
};
};
virtualisation = {
oci-containers.backend = "docker";

View file

@ -7,7 +7,6 @@
shell = pkgs.zsh;
extraGroups = [
"networkmanager"
"minecraft"
"wheel"
"dialout"
"libvirtd"

30
scripts/check-sops.sh Executable file
View file

@ -0,0 +1,30 @@
#!/usr/bin/env bash
# Pre-commit hook: block commits containing unencrypted sops secret files.
# Install with: ln -sf ../../scripts/check-sops.sh .git/hooks/pre-commit
set -euo pipefail
staged_secrets=$(git diff --cached --name-only --diff-filter=ACM -- 'secrets/*.yaml' 'secrets/*.yml' 'secrets/*.json')
if [ -z "$staged_secrets" ]; then
exit 0
fi
failed=0
for file in $staged_secrets; do
# sops-encrypted YAML/JSON files always contain a top-level "sops" key with metadata
if ! git show ":$file" | grep -q '"sops"\|sops:'; then
echo "ERROR: $file is not encrypted with sops! Encrypt it first:"
echo " sops $file"
echo
echo "hint: bypass with: git commit --no-verify"
failed=1
fi
done
if [ "$failed" -ne 0 ]; then
echo ""
echo "Commit aborted. Encrypt secret files before committing."
exit 1
fi

11
secrets/homelab.yaml Normal file
View file

@ -0,0 +1,11 @@
# This file should be encrypted with sops before committing.
# Run: sops secrets/homelab.yaml
# All values below are placeholders. Replace them with actual values.
cloudflare_dns_api_token: REPLACE_ME
cloudflared_tunnel_credentials: REPLACE_ME
cloudflared_cert: REPLACE_ME
vaultwarden_env: REPLACE_ME
glance_env: REPLACE_ME
pocketid_encryption_key: REPLACE_ME
tailscale_authkey: REPLACE_ME
nginx_htpasswd: REPLACE_ME