first commit
This commit is contained in:
commit
2a3ea31491
52 changed files with 2991 additions and 0 deletions
64
hosts/hetzner/configuration.nix
Normal file
64
hosts/hetzner/configuration.nix
Normal 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}";
|
||||
};
|
||||
}
|
||||
86
hosts/hetzner/disko-config.nix
Normal file
86
hosts/hetzner/disko-config.nix
Normal 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"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
71
hosts/hetzner/filesystem.nix
Normal file
71
hosts/hetzner/filesystem.nix
Normal 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
74
hosts/hetzner/forego.nix
Normal 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
79
hosts/hetzner/immich.nix
Normal 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
34
hosts/hetzner/media.nix
Normal 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
18
hosts/hetzner/nginx.nix
Normal 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
10
hosts/hetzner/secret.nix
Normal 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
55
hosts/hetzner/vm.nix
Normal 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;
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue