# 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 via `builtins.readDir`
- **Global configuration**: Settings in `default.nix` are 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](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](hosts/v6proxy/README.md) |
| git | 141.56.51.7 | LXC | Forgejo, Nginx | [hosts/git/README.md](hosts/git/README.md) |
| wiki | 141.56.51.13 | LXC | MediaWiki, MariaDB, Apache | [hosts/wiki/README.md](hosts/wiki/README.md) |
| redmine | 141.56.51.15 | LXC | Redmine, Nginx | [hosts/redmine/README.md](hosts/redmine/README.md) |
| nextcloud | 141.56.51.16 | LXC | Nextcloud, PostgreSQL, Redis, Nginx | [hosts/nextcloud/README.md](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):**
```bash
nix run github:nix-community/nixos-anywhere -- --flake .#proxy --target-host root@141.56.51.1
```
**For LXC containers (git, wiki, redmine, nextcloud):**
```bash
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**
```bash
nix build .#containers-git
# Result will be in result/tarball/nixos-system-x86_64-linux.tar.xz
```
**Step 2: Copy to Proxmox host**
```bash
scp result/tarball/nixos-system-x86_64-linux.tar.xz root@proxmox-host:/var/lib/vz/template/cache/
```
**Step 3: Create container on Proxmox**
```bash
# 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**
```bash
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-git`
- `nix build .#containers-wiki`
- `nix build .#containers-redmine`
- `nix 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:**
```bash
nix build .#installer-iso
# Result will be in result/iso/nixos-*.iso
```
**Build VM for testing:**
```bash
nix build .#installer-vm
```
**Deployment:**
1. Upload ISO to Proxmox storage
2. Create VM and attach ISO as boot device
3. Boot VM and follow installation prompts
4. Run installation commands manually
5. Reboot and remove ISO
### Method 4: Regular Updates
For already-deployed systems, apply configuration updates:
**Option A: Using nixos-rebuild from your local machine**
```bash
nixos-rebuild switch --flake .# --target-host root@
```
Example:
```bash
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:
```bash
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:
```bash
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:
```bash
nix fmt
```
### Testing Changes
Before deploying to production:
1. Test flake evaluation: `nix flake check`
2. Build configurations locally: `nix build .#nixosConfigurations..config.system.build.toplevel`
3. Review generated configurations
4. Deploy to test systems first if available
### Adding a New Host
1. **Create host directory:**
```bash
mkdir hosts/newhostname
```
2. **Create `hosts/newhostname/default.nix`:**
```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";
}
```
3. **The flake automatically discovers the new host** via `builtins.readDir ./hosts`
4. **If the host runs nginx**, the proxy automatically adds forwarding rules (you still need to add DNS records)
5. **Deploy:**
```bash
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.11
- `authentik`: 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:
```nix
networking = {
hostName = "";
interfaces..ipv4.addresses = [{
address = "";
prefixLength = 24;
}];
defaultGateway.address = "141.56.51.254";
};
```
- LXC containers use `eth0`
- VMs/bare metal typically use `ens18`
### Nginx + ACME Pattern
For web services:
```nix
services.nginx = {
enable = true;
virtualHosts."" = {
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:
```nix
networking.firewall.allowedTCPPorts = [ 80 443 ];
```
SSH ports vary:
- Proxy: port 1005 (admin access)
- Other hosts: port 22 (default)