commit 47c42dc7a6f8d564a95f0f139cbc6c8fd36d91ea Author: Jet Date: Mon Feb 16 21:29:21 2026 -0800 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8a43910 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# NixOS Build artifacts +result +result-* + +# Secrets (Untracked) +secrets/secrets.nix +secrets/tailscale-auth +secrets/forgejo-db-pass +secrets/stalwart-admin-pass +secrets/searx-secret-key +secrets/rcon-pass + +# Only track the example file +!secrets.nix.example + +# IDEs +.idea +.vscode diff --git a/README.md b/README.md new file mode 100644 index 0000000..8f77bfc --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# extremist software + +nixos config for the hetzner vps. + +services: +- forgejo (git) +- stalwart (mail) +- searx (search) +- conduit (matrix) +- minecraft (fabric + optimization mods) +- caddy (reverse proxy) +- grafana/prometheus (monitoring) + +deploy: +`nix run github:nix-community/nixos-anywhere -- --flake .#extremist-software --impure root@` + +secrets: +copy `secrets.nix.example` to `secrets/secrets.nix` and fill it in. +repo uses impure build cause i dont want to manage encrypted secret files in git right now. diff --git a/configuration.nix b/configuration.nix new file mode 100644 index 0000000..dc95478 --- /dev/null +++ b/configuration.nix @@ -0,0 +1,60 @@ +{ config, pkgs, modulesPath, ... }: + +{ + imports = [ + ./modules/caddy.nix + ./modules/forgejo.nix + ./modules/mail.nix + ./modules/searx.nix + ./modules/matrix.nix + ./modules/minecraft.nix + ./modules/monitoring.nix + # Impure Secrets + ./secrets/secrets.nix + ]; + + # ... (rest of imports block replaced by ./secrets/secrets.nix being added to imports) + + + # Bootloader + boot.loader.grub.enable = true; + boot.loader.grub.efiSupport = true; + boot.loader.grub.efiInstallAsRemovable = true; + + # Networking + networking.hostName = "extremist-software"; + networking.firewall.allowedTCPPorts = [ 80 443 25565 ]; # HTTP, HTTPS, Minecraft + networking.firewall.allowedUDPPorts = [ 25565 ]; # Minecraft + + # Tailscale + services.tailscale.enable = true; + # We assume the user will authenticate manually or via a one-time key service + # For now, let's enable it and allow the user to run `tailscale up` or provision via key + + # Users + users.users.root.openssh.authorizedKeys.keys = [ + # User should add their key here + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5..." + ]; + + # SSH - Secure it + services.openssh = { + enable = true; + settings.PasswordAuthentication = false; + settings.PermitRootLogin = "prohibit-password"; + }; + + # System + system.stateVersion = "24.05"; + nix.settings.experimental-features = [ "nix-command" "flakes" ]; + nixpkgs.config.allowUnfree = true; # Allow unfree packages (Minecraft, etc.) + + # Time + time.timeZone = "UTC"; + + # ZRAM for limited RAM + zramSwap.enable = true; + zramSwap.memoryPercent = 50; + + # Secrets handled via ./secrets/secrets.nix import +} diff --git a/disk-config.nix b/disk-config.nix new file mode 100644 index 0000000..1370d6e --- /dev/null +++ b/disk-config.nix @@ -0,0 +1,36 @@ +{ + disko.devices = { + disk = { + main = { + device = "/dev/sda"; # Usually /dev/sda or /dev/vda on Hetzner VPS + type = "disk"; + content = { + type = "gpt"; + partitions = { + boot = { + size = "1M"; + type = "EF02"; # for grub MBR + }; + ESP = { + size = "512M"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + }; + }; + root = { + size = "100%"; + content = { + type = "filesystem"; + format = "ext4"; + mountpoint = "/"; + }; + }; + }; + }; + }; + }; + }; +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..1b6c747 --- /dev/null +++ b/flake.lock @@ -0,0 +1,102 @@ +{ + "nodes": { + "disko": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1771271879, + "narHash": "sha256-Vn32sMuvV35ChjVGZE4d8NNmCq3E/6HjaK2uVUUp2JI=", + "owner": "nix-community", + "repo": "disko", + "rev": "e963ed5aea88ad0c093adde7c1c2abd4e1b48beb", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "disko", + "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" + } + }, + "nix-minecraft": { + "inputs": { + "flake-compat": "flake-compat", + "nixpkgs": [ + "nixpkgs" + ], + "systems": "systems" + }, + "locked": { + "lastModified": 1771296409, + "narHash": "sha256-p0fEFcqNnhYBKsHTint5pwkcnQk1b68OeQJh95B9Adg=", + "owner": "Infinidoge", + "repo": "nix-minecraft", + "rev": "22cb60087e549a90f6b0347e84ac178c0c9085ad", + "type": "github" + }, + "original": { + "owner": "Infinidoge", + "repo": "nix-minecraft", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1771008912, + "narHash": "sha256-gf2AmWVTs8lEq7z/3ZAsgnZDhWIckkb+ZnAo5RzSxJg=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "a82ccc39b39b621151d6732718e3e250109076fa", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "disko": "disko", + "nix-minecraft": "nix-minecraft", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..fda60e5 --- /dev/null +++ b/flake.nix @@ -0,0 +1,26 @@ +{ + description = "NixOS Configuration for extremist.software Hetzner VPS"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + + disko.url = "github:nix-community/disko"; + disko.inputs.nixpkgs.follows = "nixpkgs"; + + nix-minecraft.url = "github:Infinidoge/nix-minecraft"; + nix-minecraft.inputs.nixpkgs.follows = "nixpkgs"; + }; + + outputs = { self, nixpkgs, disko, nix-minecraft, ... }@inputs: { + nixosConfigurations.extremist-software = nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + specialArgs = { inherit inputs; }; + modules = [ + disko.nixosModules.disko + nix-minecraft.nixosModules.minecraft-servers + ./disk-config.nix + ./configuration.nix + ]; + }; + }; +} diff --git a/modules/caddy.nix b/modules/caddy.nix new file mode 100644 index 0000000..932a9df --- /dev/null +++ b/modules/caddy.nix @@ -0,0 +1,49 @@ +{ config, pkgs, ... }: + +{ + services.caddy = { + enable = true; + virtualHosts = { + "extremist.software" = { + extraConfig = '' + respond "Hi" + ''; + }; + + "git.extremist.software" = { + extraConfig = '' + reverse_proxy localhost:3000 + ''; + }; + + "mail.extremist.software" = { + # Stalwart handles its own certs usually, or we can proxy UI here + # Stalwart UI is usually on 8080 + extraConfig = '' + reverse_proxy localhost:8080 + ''; + }; + + "search.extremist.software" = { + extraConfig = '' + reverse_proxy localhost:8082 + ''; + }; + + "status.extremist.software" = { + extraConfig = '' + reverse_proxy localhost:3001 # Grafana + ''; + }; + + "matrix.extremist.software" = { + extraConfig = '' + reverse_proxy /_matrix/* localhost:6167 + reverse_proxy /_synapse/client/* localhost:6167 + ''; + }; + }; + }; + + networking.firewall.allowedTCPPorts = [ 80 443 ]; +} diff --git a/modules/forgejo.nix b/modules/forgejo.nix new file mode 100644 index 0000000..209a804 --- /dev/null +++ b/modules/forgejo.nix @@ -0,0 +1,24 @@ +{ config, pkgs, ... }: + +{ + services.forgejo = { + enable = true; + database.type = "postgres"; + # Enable support for Large File Storage + lfs.enable = true; + settings = { + server = { + DOMAIN = "git.extremist.software"; + ROOT_URL = "https://git.extremist.software/"; + HTTP_PORT = 3000; + }; + # You can configure SMTP here using secrets if needed + }; + # Secret for DB password set in secrets.nix + }; + + services.postgresql = { + enable = true; + package = pkgs.postgresql_15; + }; +} diff --git a/modules/mail.nix b/modules/mail.nix new file mode 100644 index 0000000..124cdfb --- /dev/null +++ b/modules/mail.nix @@ -0,0 +1,28 @@ +{ config, pkgs, ... }: + +{ + services.stalwart = { + enable = true; + settings = { + server = { + hostname = "mail.extremist.software"; + tls = { + enable = true; + implicit = false; # StartTLS usually on 587 + }; + }; + # authentication.fallback-admin set in secrets.nix + # Stalwart configuration is quite extensive. + # By default it listens on standard ports (25, 465, 587, 993, 4190) + # and provides a web admin UI on 8080. + }; + }; + + # Open Firewalls for Mail + networking.firewall.allowedTCPPorts = [ + 25 465 587 # SMTP + 993 # IMAP (Secure) + 4190 # Sieve + 8080 # Admin UI (Reverse proxied, but good to double check loopback) + ]; +} diff --git a/modules/matrix.nix b/modules/matrix.nix new file mode 100644 index 0000000..3ba7954 --- /dev/null +++ b/modules/matrix.nix @@ -0,0 +1,16 @@ +{ config, pkgs, ... }: + +{ + services.matrix-conduit = { + enable = true; + settings = { + global = { + server_name = "matrix.extremist.software"; + allow_registration = true; # Disable after creating first user + port = 6167; + }; + }; + }; + + networking.firewall.allowedTCPPorts = [ 6167 8448 ]; +} diff --git a/modules/minecraft.nix b/modules/minecraft.nix new file mode 100644 index 0000000..7c1fde1 --- /dev/null +++ b/modules/minecraft.nix @@ -0,0 +1,33 @@ +{ config, pkgs, inputs, ... }: + +{ + imports = [ inputs.nix-minecraft.nixosModules.minecraft-servers ]; + + nixpkgs.overlays = [ inputs.nix-minecraft.overlay ]; + + services.minecraft-servers = { + enable = true; + eula = true; + + servers = { + fabric = { + enable = true; + # Use fetchPackwizModpack to get the server with mods + package = pkgs.fetchPackwizModpack { + url = "https://raw.githubusercontent.com/Fabulously-Optimized/fabulously-optimized/main/Packwiz/1.20.1/pack.toml"; + packHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; # User must update this hash! + }; + serverProperties = { + motd = "Extremist Software Optimized Server"; + difficulty = "hard"; + view-distance = 10; + simulation-distance = 10; + max-players = 5; + enable-rcon = true; + # "rcon.password" set in secrets.nix + }; + jvmOpts = "-Xms2G -Xmx2500M -XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC -XX:+AlwaysPreTouch -XX:G1NewSizePercent=30 -XX:G1MaxNewSizePercent=40 -XX:G1HeapRegionSize=8M -XX:G1ReservePercent=20 -XX:G1HeapWastePercent=5 -XX:G1MixedGCCountTarget=4 -XX:InitiatingHeapOccupancyPercent=15 -XX:G1MixedGCLiveThresholdPercent=90 -XX:G1RSetUpdatingPauseTimePercent=5 -XX:SurvivorRatio=32 -XX:+PerfDisableSharedMem -XX:MaxTenuringThreshold=1 -Dusing.aikars.flags=https://mcflags.emc.gs -Daikars.new.flags=true"; + }; + }; + }; +} diff --git a/modules/monitoring.nix b/modules/monitoring.nix new file mode 100644 index 0000000..84c231b --- /dev/null +++ b/modules/monitoring.nix @@ -0,0 +1,35 @@ +{ config, pkgs, ... }: + +{ + services.prometheus = { + enable = true; + port = 9090; + exporters = { + node = { + enable = true; + enabledCollectors = [ "systemd" ]; + port = 9100; + }; + # Stalwart and Conduit might have exporters too + }; + scrapeConfigs = [ + { + job_name = "node"; + static_configs = [{ + targets = [ "127.0.0.1:9100" ]; + }]; + } + ]; + }; + + services.grafana = { + enable = true; + settings = { + server = { + http_port = 3001; + http_addr = "127.0.0.1"; + domain = "status.extremist.software"; + }; + }; + }; +} diff --git a/modules/searx.nix b/modules/searx.nix new file mode 100644 index 0000000..41f0761 --- /dev/null +++ b/modules/searx.nix @@ -0,0 +1,19 @@ +{ config, pkgs, ... }: + +{ + services.searx = { + enable = true; + # settings.server.secret_key set in secrets.nix + settings = { + server = { + port = 8082; + bind_address = "127.0.0.1"; + # secret_key = ...; # Set via env var in file + }; + }; + }; + + # Inject secret via env vars or file substitution if possible + # Or use `environment.etc` to place config file if service allows. + # For now, simplistic setup. +} diff --git a/secrets.nix.example b/secrets.nix.example new file mode 100644 index 0000000..9721f96 --- /dev/null +++ b/secrets.nix.example @@ -0,0 +1,25 @@ +{ pkgs, config, lib, ... }: + +{ + # Forgejo + services.forgejo.settings.database.PASSWORD = "changeme_forgejo_db"; + + # Stalwart Mail + services.stalwart.settings.authentication.fallback-admin.secret = "changeme_stalwart_admin"; + + # Searx + services.searx.settings.server.secret_key = "changeme_searx_secret"; + + # Minecraft RCON + services.minecraft-servers.servers.fabric.serverProperties."rcon.password" = "changeme_rcon"; + + # Tailscale Auth Key (needs to be a file for the service usually, or use pre-auth) + # For Tailscale, standard module uses 'authKeyFile'. + # We can create a file in the store for it since this is an impure secrets file anyway. + + # For Tailscale, let's just write valid one-liner to a file via environment.etc if needed, + # or use the 'authKey' option if available (it is not, usually). + # We will stick to environment.etc JUST for Tailscale or file-based secrets. + environment.etc."secrets/tailscale-auth".text = "tskey-auth-PLACEHOLDER"; + services.tailscale.authKeyFile = "/etc/secrets/tailscale-auth"; +}