first commit

This commit is contained in:
Louis Chih-Ming Lee 2026-01-26 01:16:35 +01:00
commit 2a3ea31491
52 changed files with 2991 additions and 0 deletions

View file

@ -0,0 +1,64 @@
{
pkgs,
lib,
domain,
isProd,
inputs,
config,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
(modulesPath + "/profiles/qemu-guest.nix")
./disko-config.nix
./secret.nix
./media.nix
./nginx.nix
./immich.nix
./forego.nix
# ./vm.nix
];
system.stateVersion = "25.11";
environment.systemPackages = with pkgs; [
vim
cifs-utils
btrfs-progs
forgejo
];
nix.settings.experimental-features = [
"nix-command"
"flakes"
];
boot.loader.grub = {
# enable = true;
efiSupport = true;
efiInstallAsRemovable = true;
};
services.openssh.enable = true;
users.users.root.openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDBUxBUar3CyZCZTet3s8s28Pu1d0viuDe6YoMQBVdFB louis@T14p"
];
networking.hostName = "webserver";
networking.firewall.allowedTCPPorts = [
80
443
22
];
security.acme = lib.mkIf isProd {
acceptTerms = true;
defaults.email = "admin@${domain}";
};
}

View file

@ -0,0 +1,86 @@
{ lib, ... }:
let
disk-id = "scsi-0QEMU_QEMU_HARDDISK_110162268";
data-id = "scsi-0HC_Volume_104473479";
in
{
disko.devices = {
# --- DISK 1: MAIN OS (38GB) ---
disk.main = {
# You were right! This IS the correct ID for the 38GB drive.
device = "/dev/disk/by-id/${disk-id}";
type = "disk";
content = {
type = "gpt";
partitions = {
boot = {
size = "1M";
type = "EF02";
priority = 1;
};
ESP = {
size = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
disk.volume = {
# This is the ID for your volume (from your ls output)
device = "/dev/disk/by-id/${disk-id}";
type = "disk";
content = {
type = "gpt";
partitions = {
data = {
size = "100%";
content = {
type = "btrfs";
extraArgs = [ "-f" ];
mountpoint = "/mnt/data";
subvolumes = {
"@postgresql" = {
mountpoint = "/mnt/data/postgresql";
mountOptions = [
"nodatacow"
"noatime"
];
};
"@forgejo" = {
mountpoint = "/mnt/data/forgejo";
mountOptions = [
"compress=zstd"
"noatime"
];
};
"@immich" = {
mountpoint = "/mnt/data/immich";
mountOptions = [
"compress=zstd"
"noatime"
];
};
};
};
};
};
};
};
};
}

View file

@ -0,0 +1,71 @@
{ lib, config, ... }:
let
volume-id = "scsi-0HC_Volume_104473479";
in
{
systemd.tmpfiles.rules = [
"d /mnt/ 0755 root root -"
"d /mnt/box 0770 root root -"
"d /mnt/box/immich 0770 immich immich -"
"d /mnt/box/immich 0770 immich immich -"
"d /mnt/box/immich/library 0770 immich immich -"
"d /mnt/box/immich/upload 0770 immich immich -"
"d /mnt/box/immich/thumbs 0770 immich immich -"
"d /mnt/box/immich/encoded-video 0770 immich immich -"
"d /mnt/box/immich/profile 0770 immich immich -"
"d /mnt/box/immich/backups 0770 immich immich -"
"d /mnt/volume 0777 root root -"
"d /mnt/volume/postgresql 0700 postgres postgres -"
"d /mnt/volume/forgejo 0750 forgejo forgejo -"
"d /mnt/volume/immich 0750 immich immich -"
];
fileSystems."/mnt/data/postgres" = {
device = "/dev/disk/by-id/${volume-id}";
fsType = "btrfs";
options = [
"subvol=@postgres"
"nodatacow" # <--- Disables Copy-on-Write for performance
"noatime"
];
};
fileSystems."/mnt/data/immich" = {
device = "/dev/disk/by-id/${volume-id}";
fsType = "btrfs";
options = [
"subvol=@immich"
"compress=zstd"
"noatime"
];
};
fileSystems."/mnt/data/forgejo" = {
device = "/dev/disk/by-id/${volume-id}";
fsType = "btrfs";
options = [
"subvol=@forgejo"
"compress=zstd"
"noatime"
];
};
#fileSystems."/mnt/box" = {
# device = "//u536222.your-storagebox.de/backup";
# fsType = "cifs";
# options = [
# "x-systemd.automount"
# "noauto"
# "rw"
# "credentials=${config.sops.secrets.storage_box_credentials.path}"
# "uid=900"
# "gid=100"
# "file_mode=0660"
# "dir_mode=0770"
# ];
#};
}

74
hosts/hetzner/forego.nix Normal file
View file

@ -0,0 +1,74 @@
{
domain,
isProd,
config,
pkgs,
...
}:
{
# 1. Access the Secret
sops.secrets.forgejo_db_password = {
owner = "forgejo";
# Restart forgejo if the password changes
restartUnits = [ "forgejo.service" ];
};
services.forgejo = {
enable = true;
# 2. STORAGE (SSD)
stateDir = "/mnt/data/forgejo";
# 3. DATABASE (Shared Postgres)
database = {
type = "postgres";
name = "forgejo";
user = "forgejo";
createDatabase = false; # We let NixOS manage this below
socket = "/run/postgresql"; # Ultra-fast socket connection
passwordFile = config.sops.secrets.forgejo_db_password.path;
};
# 4. SETTINGS
settings = {
server = {
DOMAIN = "git.${domain}";
ROOT_URL = "https://git.${domain}/";
HTTP_PORT = 3000;
# Run internal SSH on 2222 so it doesn't block your Admin SSH (22)
SSH_PORT = 2222;
START_SSH_SERVER = true;
};
# Disable registration to prevent random internet people from joining
service.DISABLE_REGISTRATION = true;
# Optional: Metrics for Grafana later
metrics.ENABLED = true;
};
};
# 5. POSTGRESQL PROVISIONING
# This automatically creates the DB and User when you deploy
services.postgresql = {
ensureDatabases = [ "forgejo" ];
ensureUsers = [
{
name = "forgejo";
ensureDBOwnership = true;
}
];
};
# 6. REVERSE PROXY
services.nginx.virtualHosts."git.${domain}" = {
forceSSL = isProd;
enableACME = isProd;
locations."/" = {
proxyPass = "http://127.0.0.1:3000";
};
};
# 7. FIREWALL
# Allow Git-over-SSH on the custom port
networking.firewall.allowedTCPPorts = [ 2222 ];
}

79
hosts/hetzner/immich.nix Normal file
View file

@ -0,0 +1,79 @@
{
domain,
lib,
isProd,
config,
pkgs,
...
}:
{
sops.secrets.immich_db_password = { };
sops.secrets.immich_jwt_secret = { };
sops.templates."immich.env".content = ''
DB_PASSWORD=${config.sops.placeholder.immich_db_password}
JWT_SECRET=${config.sops.placeholder.immich_jwt_secret}
'';
#users.users.immich.extraGroups = [ "users" ];
users.users.immich.uid = 900;
users.groups.immich.gid = 900;
services.immich = {
enable = true;
host = "127.0.0.1";
port = 2283;
mediaLocation = "/mnt/media/immich";
secretsFile = config.sops.templates."immich.env".path;
redis.enable = true;
database = {
enable = true;
createDB = true;
user = "immich";
name = "immich";
host = "/run/postgresql";
};
machine-learning.enable = true;
};
systemd.services.immich-server = {
requires = [ "mnt-media.mount" ];
after = [ "mnt-media.mount" ];
serviceConfig = {
DynamicUser = lib.mkForce false;
ReadWritePaths = [ "/mnt/media/immich" ];
BindPaths = [ "/mnt/media/immich" ];
};
};
services.postgresql = {
enable = true;
dataDir = "/mnt/data/postgresql";
ensureDatabases = [ "immich" ];
ensureUsers = [
{
name = "immich";
ensureDBOwnership = true;
}
];
};
services.nginx.virtualHosts."photo.${domain}" = {
forceSSL = isProd;
enableACME = isProd;
locations."/" = {
proxyPass = "http://127.0.0.1:2283";
proxyWebsockets = true;
extraConfig = "client_max_body_size 50G;";
};
};
}

34
hosts/hetzner/media.nix Normal file
View file

@ -0,0 +1,34 @@
{ config, ... }:
{
systemd.tmpfiles.rules = [
# 1. THE PARENT DIRS
# Change 0770 -> 0755 so users like 'postgres' can walk through the door.
"d /mnt/data 0755 root root -"
"d /mnt/media 0755 root root -"
# 2. THE SERVICES (SSD / Data)
# IMPORTANT: These MUST match where your services.postgresql.dataDir points
"d /mnt/data/postgresql 0700 postgres postgres -"
"d /mnt/data/forgejo 0750 forgejo forgejo -"
# 3. THE STORAGE (HDD / Media)
"d /mnt/media/immich 0750 immich immich -"
];
fileSystems."/mnt/media" = {
device = "//u536222.your-storagebox.de/backup";
fsType = "cifs";
options = [
"nofail"
"noperm"
"rw"
"credentials=${config.sops.secrets.storage_box_credentials.path}"
"uid=900"
"gid=900"
"forceuid"
"forcegid"
"file_mode=0660"
"dir_mode=0770"
];
};
}

18
hosts/hetzner/nginx.nix Normal file
View file

@ -0,0 +1,18 @@
{ domain, isProd, ... }:
{
services.nginx = {
enable = true;
recommendedProxySettings = true;
recommendedTlsSettings = isProd;
virtualHosts."${domain}" = {
forceSSL = isProd;
enableACME = isProd;
locations."/test" = {
return = "200 'Hello! You are accessing: ${domain}/test'";
extraConfig = "default_type text/plain;";
};
};
};
}

10
hosts/hetzner/secret.nix Normal file
View file

@ -0,0 +1,10 @@
{ ... }:
{
sops = {
defaultSopsFile = ./../../secrets.yaml;
defaultSopsFormat = "yaml";
age.keyFile = "/var/lib/sops-nix/key.txt";
};
sops.secrets.storage_box_credentials = { };
}

55
hosts/hetzner/vm.nix Normal file
View file

@ -0,0 +1,55 @@
{ lib, ... }:
{
virtualisation.vmVariant = {
virtualisation = {
memorySize = 4096;
cores = 2;
graphics = false;
sharedDirectories = {
sops-keys = {
source = "/home/louis/.config/sops/age";
target = "/var/lib/sops-nix";
};
};
forwardPorts = [
{
from = "host";
host.port = 8080;
guest.port = 80;
}
];
};
fileSystems."/mnt/volume" = lib.mkForce {
device = "none";
fsType = "tmpfs";
options = [
"size=2G"
"mode=777"
];
};
fileSystems."/mnt/box" = lib.mkForce {
device = "none";
fsType = "tmpfs";
options = [
"size=2G"
"mode=777"
];
};
sops.age.keyFile = lib.mkForce "/var/lib/sops-nix/keys.txt";
users.users.root.password = "root";
services.openssh.settings.PermitRootLogin = "yes";
services.openssh.settings.PasswordAuthentication = true;
documentation.enable = false;
systemd.services.NetworkManager-wait-online.enable = false;
networking.useDHCP = lib.mkDefault true;
services.qemuGuest.enable = true;
networking.enableIPv6 = false;
};
}