Flake lock file updates:
• Updated input 'authentik':
'github:nix-community/authentik-nix/3df5c213032b8d28073d4baead699acea62ab50d?narHash=sha256-PPAgCKlRpxcZlEJ8NH2CGVaEogOc4nOs/eNF0hlAC2E%3D' (2026-02-21)
→ 'github:nix-community/authentik-nix/7e4730351fb6df479c46a1bf7e23d46a0b0c5d46?narHash=sha256-hcstQ1Z9aQSJM3AVCLb0/OPTicbME9nhP01GiPrOjZM%3D' (2026-03-07)
• Updated input 'authentik/authentik-go':
'github:goauthentik/client-go/280022b0a8de5c8f4b2965d1147a1c4fa846ba64?narHash=sha256-Yyna75Nd6485tZP9IpdEa5QNomswe9hRfM%2Bw3MuET9E%3D' (2026-02-05)
→ 'github:goauthentik/client-go/4c1444ee54d945fbcc5ae107b4f191ca0352023d?narHash=sha256-zTEmvxe%2BBpfWYvAl675PnhXCH4jV4GUTFb1MrQ1Eyno%3D' (2026-02-23)
• Updated input 'authentik/authentik-src':
'github:goauthentik/authentik/19ad8d3ae3f266ec1096bc4461fdf6bcda1aa079?narHash=sha256-alTyrMBbjZbw4jhEna8saabf93sqSrZCu%2BZ5xH3pZ7M%3D' (2026-02-12)
→ 'github:goauthentik/authentik/0dccbd4193c45c581e9fb7cd89df0c1487510f1f?narHash=sha256-0Vpf1hj9C8r%2BrhrCgwoNazpQ%2BmwgjdjDhuoKCxYQFWw%3D' (2026-03-03)
• Updated input 'authentik/flake-compat':
'github:edolstra/flake-compat/65f23138d8d09a92e30f1e5c87611b23ef451bf3?narHash=sha256-4VBOP18BFeiPkyhy9o4ssBNQEvfvv1kXkasAYd0%2BrrA%3D' (2025-12-07)
→ 'github:edolstra/flake-compat/5edf11c44bc78a0d334f6334cdaf7d60d732daab?narHash=sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns%3D' (2025-12-29)
• Updated input 'authentik/flake-parts':
'github:hercules-ci/flake-parts/a34fae9c08a15ad73f295041fec82323541400a9?narHash=sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw%3D' (2025-12-15)
→ 'github:hercules-ci/flake-parts/57928607ea566b5db3ad13af0e57e921e6b12381?narHash=sha256-AnYjnFWgS49RlqX7LrC4uA%2BsCCDBj0Ry/WOJ5XWAsa0%3D' (2026-02-02)
• Updated input 'authentik/flake-parts/nixpkgs-lib':
'github:nix-community/nixpkgs.lib/2075416fcb47225d9b68ac469a5c4801a9c4dd85?narHash=sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo%3D' (2025-12-14)
→ 'github:nix-community/nixpkgs.lib/72716169fe93074c333e8d0173151350670b824c?narHash=sha256-cBEymOf4/o3FD5AZnzC3J9hLbiZ%2BQDT/KDuyHXVJOpM%3D' (2026-02-01)
• Updated input 'authentik/nixpkgs':
'github:NixOS/nixpkgs/1412caf7bf9e660f2f962917c14b1ea1c3bc695e?narHash=sha256-AIdl6WAn9aymeaH/NvBj0H9qM%2BXuAuYbGMZaP0zcXAQ%3D' (2026-01-13)
→ 'github:NixOS/nixpkgs/2fc6539b481e1d2569f25f8799236694180c0993?narHash=sha256-0MAd%2B0mun3K/Ns8JATeHT1sX28faLII5hVLq0L3BdZU%3D' (2026-02-23)
• Updated input 'authentik/pyproject-build-systems':
'github:pyproject-nix/build-system-pkgs/042904167604c681a090c07eb6967b4dd4dae88c?narHash=sha256-4bocaOyLa3AfiS8KrWjZQYu%2BIAta05u3gYZzZ6zXbT0%3D' (2025-11-20)
→ 'github:pyproject-nix/build-system-pkgs/04e9c186e01f0830dad3739088070e4c551191a4?narHash=sha256-7uXPiWB0YQ4HNaAqRvVndYL34FEp1ZTwVQHgZmyMtC8%3D' (2026-02-18)
• Updated input 'authentik/pyproject-nix':
'github:pyproject-nix/pyproject.nix/2c8df1383b32e5443c921f61224b198a2282a657?narHash=sha256-xaKvtPx6YAnA3HQVp5LwyYG1MaN4LLehpQI8xEdBvBY%3D' (2025-11-26)
→ 'github:pyproject-nix/pyproject.nix/eb204c6b3335698dec6c7fc1da0ebc3c6df05937?narHash=sha256-nFJSfD89vWTu92KyuJWDoTQJuoDuddkJV3TlOl1cOic%3D' (2026-02-19)
• Updated input 'authentik/uv2nix':
'github:pyproject-nix/uv2nix/4cca323a547a1aaa9b94929c4901bed5343eafe8?narHash=sha256-90d//IZ4GXipNsngO4sb2SAPbIC/a2P%2BIAdAWOwpcOM%3D' (2025-12-13)
→ 'github:pyproject-nix/uv2nix/abe65de114300de41614002fe9dce2152ac2ac23?narHash=sha256-gCojeIlQ/rfWMe3adif3akyHsT95wiMkLURpxTeqmPc%3D' (2026-02-27)
• Updated input 'disko':
'github:nix-community/disko/a4cb7bf73f264d40560ba527f9280469f1f081c6?narHash=sha256-A5uE/hMium5of/QGC6JwF5TGoDAfpNtW00T0s9u/PN8%3D' (2026-02-23)
→ 'github:nix-community/disko/7b9f7f88ab3b339f8142dc246445abb3c370d3d3?narHash=sha256-khlHllTsovXgT2GZ0WxT4%2BRvuMjNeR5OW0UYeEHPYQo%3D' (2026-03-09)
• Updated input 'mailserver':
'git+https://gitlab.com/simple-nixos-mailserver/nixos-mailserver?ref=nixos-25.11&rev=23f0a53ca6e58e61e1ea2b86791c69b79c91656d' (2025-12-24)
→ 'git+https://gitlab.com/simple-nixos-mailserver/nixos-mailserver?ref=nixos-25.11&rev=9cdd6869e513df8153db4b920c8f15d394e150f7' (2026-03-12)
• Updated input 'nixpkgs':
'github:nixos/nixpkgs/e764fc9a405871f1f6ca3d1394fb422e0a0c3951?narHash=sha256-sdaqdnsQCv3iifzxwB22tUwN/fSHoN7j2myFW5EIkGk%3D' (2026-02-24)
→ 'github:nixos/nixpkgs/0590cd39f728e129122770c029970378a79d076a?narHash=sha256-BHoB/XpbqoZkVYZCfXJXfkR%2BGXFqwb/4zbWnOr2cRcU%3D' (2026-03-11)
• Updated input 'sops':
'github:Mic92/sops-nix/b027513c32e5b39b59f64626b87fbe168ae02094?narHash=sha256-YV17Q5lEU0S9ppw08Y%2Bcs4eEQJBuc79AzblFoHORLMU%3D' (2026-02-23)
→ 'github:Mic92/sops-nix/d1ff3b1034d5bab5d7d8086a7803c5a5968cd784?narHash=sha256-M3zEnq9OElB7zqc%2BmjgPlByPm1O5t2fbUrH3t/Hm5Ag%3D' (2026-03-09)
|
||
|---|---|---|
| docs | ||
| hosts | ||
| keys | ||
| .gitignore | ||
| .pre-commit-config.yaml | ||
| .sops.yaml | ||
| default.nix | ||
| flake.lock | ||
| flake.nix | ||
| hardware-configuration.nix | ||
| hetzner-disk.nix | ||
| README.md | ||
StuRa HTW Dresden Infrastructure - NixOS Configuration
Declarative infrastructure management for StuRa HTW Dresden using NixOS and a flake-based configuration. This repository replaces the hand-configured FreeBSD relay system with a modern, reproducible infrastructure.
Architecture
Overview
This infrastructure uses a flake-based approach with automatic host discovery:
- Centralized reverse proxy: HAProxy at 141.56.51.1 routes all traffic via SNI inspection and HTTP host headers
- IPv6 gateway: Hetzner VPS at 2a01:4f8:1c19:96f8::1 forwards IPv6 traffic to the IPv4 proxy
- Automatic host discovery: Each subdirectory in
hosts/becomes a NixOS configuration viabuiltins.readDir - Global configuration: Settings in
default.nixare automatically applied to all hosts - ACME certificates: All services use Let's Encrypt certificates managed locally on each host
Network
- Network: 141.56.51.0/24
- Gateway: 141.56.51.254
- DNS: 141.56.1.1, 141.56.1.2 (HTW internal)
- Domain: htw.stura-dresden.de
Repository Structure
stura-infra/
├── flake.nix # Main flake configuration with auto-discovery
├── default.nix # Global settings applied to all hosts
├── hosts/ # Host-specific configurations
│ ├── proxy/ # Central reverse proxy (HAProxy)
│ │ ├── default.nix
│ │ ├── hardware-configuration.nix
│ │ ├── hetzner-disk.nix
│ │ └── README.md
│ ├── v6proxy/ # IPv6 gateway (Hetzner VPS)
│ │ ├── default.nix
│ │ ├── hardware-configuration.nix
│ │ ├── hetzner-disk.nix
│ │ └── README.md
│ ├── git/ # Forgejo git server
│ │ └── default.nix
│ ├── wiki/ # MediaWiki instance
│ │ └── default.nix
│ ├── nextcloud/ # Nextcloud instance
│ │ └── default.nix
│ └── redmine/ # Redmine project management
│ └── default.nix
└── README.md # This file
Host Overview
| Host | IP | Type | Services | Documentation |
|---|---|---|---|---|
| proxy | 141.56.51.1 | VM | HAProxy, SSH Jump | hosts/proxy/README.md |
| v6proxy | 178.104.18.93 (IPv4) 2a01:4f8:1c19:96f8::1 (IPv6) |
Hetzner VPS | HAProxy (IPv6 Gateway) | hosts/v6proxy/README.md |
| git | 141.56.51.7 | LXC | Forgejo, Nginx | hosts/git/README.md |
| wiki | 141.56.51.13 | LXC | MediaWiki, MariaDB, Apache | hosts/wiki/README.md |
| redmine | 141.56.51.15 | LXC | Redmine, Nginx | hosts/redmine/README.md |
| nextcloud | 141.56.51.16 | LXC | Nextcloud, PostgreSQL, Redis, Nginx | hosts/nextcloud/README.md |
Deployment Methods
Method 1: Initial Installation with nixos-anywhere (Recommended)
Use nixos-anywhere for initial system installation. This handles disk partitioning (via disko) and bootstrapping automatically.
For VM hosts (proxy):
nix run github:nix-community/nixos-anywhere -- --flake .#proxy --target-host root@141.56.51.1
For LXC containers (git, wiki, redmine, nextcloud):
nix run github:nix-community/nixos-anywhere -- --flake .#git --target-host root@141.56.51.7
This method is ideal for:
- First-time installation on bare metal or fresh VMs
- Complete system rebuilds
- Migration to new hardware
Method 2: Container Tarball Deployment to Proxmox
Build and deploy LXC container tarballs for git, wiki, redmine, and nextcloud hosts.
Step 1: Build container tarball locally
nix build .#containers-git
# Result will be in result/tarball/nixos-system-x86_64-linux.tar.xz
Step 2: Copy to Proxmox host
scp result/tarball/nixos-system-x86_64-linux.tar.xz root@proxmox-host:/var/lib/vz/template/cache/
Step 3: Create container on Proxmox
# Example for git host (container ID 107, adjust as needed)
pct create 107 /var/lib/vz/template/cache/nixos-system-x86_64-linux.tar.xz \
--hostname git \
--net0 name=eth0,bridge=vmbr0,ip=141.56.51.7/24,gw=141.56.51.254 \
--memory 2048 \
--cores 2 \
--rootfs local-lvm:8 \
--unprivileged 1 \
--features nesting=1
# Configure storage and settings via Proxmox web interface if needed
Step 4: Start container
pct start 107
Step 5: Post-deployment configuration
- Access container:
pct enter 107 - Follow host-specific post-deployment steps in each host's README.md
Available container tarballs:
nix build .#containers-gitnix build .#containers-wikinix build .#containers-redminenix build .#containers-nextcloud
Note: The proxy host is a full VM and does not have a container tarball. Use Method 1 or 3 for proxy deployment.
Method 3: ISO Installer
Build a bootable ISO installer for manual installation on VMs or bare metal.
Build ISO:
nix build .#installer-iso
# Result will be in result/iso/nixos-*.iso
Build VM for testing:
nix build .#installer-vm
Deployment:
- Upload ISO to Proxmox storage
- Create VM and attach ISO as boot device
- Boot VM and follow installation prompts
- Run installation commands manually
- Reboot and remove ISO
Method 4: Regular Updates
For already-deployed systems, apply configuration updates:
Option A: Using nixos-rebuild from your local machine
nixos-rebuild switch --flake .#<hostname> --target-host root@<ip>
Example:
nixos-rebuild switch --flake .#proxy --target-host root@141.56.51.1
Note: This requires an SSH config entry for the proxy (uses port 1005):
# ~/.ssh/config
Host 141.56.51.1
Port 1005
Option B: Using auto-generated update scripts
The flake generates convenience scripts for each host:
nix run .#git-update
nix run .#wiki-update
nix run .#redmine-update
nix run .#nextcloud-update
nix run .#proxy-update
These scripts automatically extract the target IP from the configuration.
Option C: Remote execution (no local Nix installation)
If Nix isn't installed locally, run the command on the target system:
ssh root@141.56.51.1 "nixos-rebuild switch --flake git+https://codeberg.org/stura-htw-dresden/stura-infra#proxy"
Replace proxy with the appropriate hostname and adjust the IP address.
Required DNS Records
The following DNS records must be configured for the current infrastructure:
| Name | Type | IP | Service |
|---|---|---|---|
| *.htw.stura-dresden.de | CNAME | proxy.htw.stura-dresden.de | Reverse proxy |
| proxy.htw.stura-dresden.de | A | 141.56.51.1 | Proxy IPv4 |
| proxy.htw.stura-dresden.de | AAAA | 2a01:4f8:1c19:96f8::1 | IPv6 Gateway (v6proxy) |
Note: All public services point to the proxy IPs. The IPv4 proxy (141.56.51.1) handles SNI-based routing to backend hosts. The IPv6 gateway (v6proxy at 2a01:4f8:1c19:96f8::1) forwards all IPv6 traffic to the IPv4 proxy. Backend IPs are internal and not exposed in DNS.
Additional services managed by the proxy (not in this repository):
- stura.htw-dresden.de → Plone
- tix.htw.stura-dresden.de → Pretix
- vot.htw.stura-dresden.de → OpenSlides
- mail.htw.stura-dresden.de → Mail server
Development
Code Formatting
Format all Nix files using the RFC-style formatter:
nix fmt
Testing Changes
Before deploying to production:
- Test flake evaluation:
nix flake check - Build configurations locally:
nix build .#nixosConfigurations.<hostname>.config.system.build.toplevel - Review generated configurations
- Deploy to test systems first if available
Adding a New Host
-
Create host directory:
mkdir hosts/newhostname -
Create
hosts/newhostname/default.nix:{ config, lib, pkgs, modulesPath, ... }: { imports = [ "${modulesPath}/virtualisation/proxmox-lxc.nix" # For LXC containers # Or for VMs: # ./hardware-configuration.nix ]; networking = { hostName = "newhostname"; interfaces.eth0.ipv4.addresses = [{ # or ens18 for VMs address = "141.56.51.XXX"; prefixLength = 24; }]; defaultGateway.address = "141.56.51.254"; firewall.allowedTCPPorts = [ 80 443 ]; }; # Add your services here services.nginx.enable = true; # ... system.stateVersion = "25.11"; } -
The flake automatically discovers the new host via
builtins.readDir ./hosts -
If the host runs nginx, the proxy automatically adds forwarding rules (you still need to add DNS records)
-
Deploy:
nix run github:nix-community/nixos-anywhere -- --flake .#newhostname --target-host root@141.56.51.XXX
Repository Information
- Repository: https://codeberg.org/stura-htw-dresden/stura-infra
- ACME Email: cert@stura.htw-dresden.de
- NixOS Version: 25.11
- Architecture: x86_64-linux
Flake Inputs
nixpkgs: NixOS 25.11authentik: Identity provider (nix-community/authentik-nix)mailserver: Simple NixOS mailserver (nixos-25.11 branch)sops: Secret management (Mic92/sops-nix)disko: Declarative disk partitioning
Common Patterns
Network Configuration
All hosts follow this pattern:
networking = {
hostName = "<name>";
interfaces.<interface>.ipv4.addresses = [{
address = "<ip>";
prefixLength = 24;
}];
defaultGateway.address = "141.56.51.254";
};
- LXC containers use
eth0 - VMs/bare metal typically use
ens18
Nginx + ACME Pattern
For web services:
services.nginx = {
enable = true;
virtualHosts."<fqdn>" = {
forceSSL = true;
enableACME = true;
locations."/" = {
# service config
};
};
};
This automatically:
- Integrates with the proxy's ACME challenge forwarding
- Generates HAProxy backend configuration
- Requests Let's Encrypt certificates
Firewall Rules
Hosts only need to allow traffic from the proxy:
networking.firewall.allowedTCPPorts = [ 80 443 ];
SSH ports vary:
- Proxy: port 1005 (admin access)
- Other hosts: port 22 (default)