From 29eb6493be2a6e680cd1b592a83d41ef49859756 Mon Sep 17 00:00:00 2001 From: Julian Sutter Date: Sun, 15 Feb 2026 16:57:07 -0800 Subject: [PATCH] forgejo? --- docs/agents.md | 399 +++++++++++++++++++++++++++++++++++++++++++- servers/README.md | 148 ++++++++++++++++ servers/forgejo.nix | 314 ++++++++++++++++++++++++++++++++++ 3 files changed, 854 insertions(+), 7 deletions(-) create mode 100644 servers/README.md create mode 100644 servers/forgejo.nix diff --git a/docs/agents.md b/docs/agents.md index 44a6476..d599dcc 100644 --- a/docs/agents.md +++ b/docs/agents.md @@ -13,7 +13,8 @@ This repository manages NixOS configurations for multiple systems using flakes a | `framework` | Desktop/Laptop | Framework AMD AI 300 series | User: jsutter, Uses nixos-hardware module | | `aurora` | Desktop | Custom | Users: jsutter, isutter, aksutter | | `labrizor` | Desktop | Custom | User: jsutter | -| `skip01` | Server (WIP) | Custom | Headless configuration | +| `skip01` | Server | Custom | Headless, Docker support | +| `warp` | Server | Custom | Minimal headless configuration | ### Core Technologies @@ -22,13 +23,15 @@ This repository manages NixOS configurations for multiple systems using flakes a - **Home Manager**: Release 25.11 - **Plasma Manager**: KDE Plasma configuration management - **Flakes**: For reproducible, declarative system definitions +- **App Flakes**: Standalone flakes for server applications (Immich, Octofriend, etc.) ## Project Structure ``` nixos/ -├── appflakes/ # Local flake inputs -│ └── octofriend/ # Custom application flake +├── appflakes/ # Standalone flake inputs for server applications +│ ├── immich/ # Self-hosted photo/video backup (Docker Compose) +│ └── octofriend/ # CLI coding assistant ├── desktop/ # Desktop environment configurations │ ├── plasma.nix # KDE Plasma + Plasma Manager │ ├── dev.nix # Development tools and languages @@ -43,14 +46,17 @@ nixos/ │ └── stp-elc-udmshare.nix # Specific mount/share config ├── docs/ # Documentation │ └── agents.md # This file -├── servers/ # Server configurations (future use) +├── servers/ # Server service modules +│ ├── common.nix # Common server settings (Docker enablement) +│ ├── forgejo.nix # Forgejo Git server module +│ └── hugo.nix # Hugo web server module (Docker compose) ├── systems/ # Hardware-specific configurations │ ├── common.nix # Shared system settings (bootloader, nix config, etc.) -│ ├── common-headless.nix # Common settings for headless systems │ ├── framework.nix # Framework laptop hardware config │ ├── aurora.nix # Aurora desktop hardware config │ ├── labrizor.nix # Labrizor desktop hardware config -│ └── skip01.nix # Skip01 server config +│ ├── skip01.nix # Skip01 server config +│ └── warp.nix # Warp server config ├── users/ # User configurations with Home Manager │ ├── common-home.nix # Shared user packages and settings │ ├── jsutter.nix # Primary user configuration @@ -59,6 +65,255 @@ nixos/ └── flake.nix # Top-level flake definition ``` +### App Flakes Architecture + + +The `appflakes/` directory contains standalone flakes for server applications. These are self-contained, reusable flake definitions that can be used independently or integrated into NixOS configurations. + +**Current App Flakes:** +- `immich/`: Self-hosted photo and video backup solution (Docker Compose deployment) +- `octofriend/`: CLI coding assistant packaged for Nix/NixOS + +**App Flake Structure:** +```nix +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + # Additional inputs as needed + }; + + outputs = { self, nixpkgs, flake-utils, ... }: + { + # NixOS module (if applicable) + nixosModules.app-name = nixosModule; + + # Package definition + packages = flake-utils.lib.eachDefaultSystem (system: { ... }); + + # App entry (if CLI tool) + apps.default = ...; + }; +} +``` + +**Using App Flakes:** + +1. **As a local input in main flake.nix:** + ```nix + inputs = { + immich = { url = "path:./appflakes/immich"; }; + }; + + outputs = { self, nixpkgs, immich, ... }: { + nixosConfigurations.server = nixpkgs.lib.nixosSystem { + modules = [ + immich.nixosModules.immich-docker + ./systems/server.nix + ]; + }; + }; + ``` + +2. **Directly on a system:** + ```bash + # Use app flake package on any system + nix run .#appflakes/octofriend + ``` + +### Server Service Modules + +The `servers/` directory contains NixOS modules for server-specific services. These differ from appflakes in that they are modules designed to be included in NixOS configurations rather than standalone flakes. + +**Server Module Pattern:** + +1. **Define options module-wide:** + ```nix + { config, pkgs, lib, ... }: + let + cfg = config.services.service-name; + in { + options.services.service-name = { + enable = mkEnableOption "Service description"; + + domain = mkOption { + type = types.str; + example = "service.example.com"; + description = "Domain name for the service"; + }; + + # Additional options... + }; + } + ``` + +2. **Configure service only if enabled:** + ```nix + config = mkIf cfg.enable { + # Service dependencies (PostgreSQL, Nginx, etc.) + + # Main service configuration + services.service-name = { + enable = true; + settings = { ... }; + }; + + # Reverse proxy configuration (Nginx) + services.nginx.virtualHosts.${cfg.domain} = { ... }; + + # Firewall rules + networking.firewall.allowedTCPPorts = [ ... ]; + + # Backup systemd services + systemd.services.service-name-backup = { ... }; + }; + ``` + +**Common Server Module Elements:** + +- **Database backing:** PostgreSQL (`services.postgresql`) +- **Reverse proxy:** Nginx with SSL/TLS (`services.nginx`) +- **Firewall:** Open required ports (`networking.firewall`) +- **Backups:** Systemd timers for automated backups +- **Logging:** Centralized logging configuration +- **Monitoring:** Optional Prometheus/metrics endpoints +- **Security:** Hardening options, secret management + +**Creating a New Server Module:** + +1. Create `servers/.nix` +2. Follow the standard module pattern with options +3. Use `mkIf cfg.enable` for conditional configuration +4. Include backup systemd service with timer +5. Add firewall rules if service is network-accessible +6. Document required secrets/password files +7. Test on a headless system first (`skip01` or `warp`) + +**Server Module Organization:** + +- `servers/common.nix`: Shared server infrastructure (Docker, etc.) +- `servers/forgejo.nix`: Forgejo Git server complete configuration +- `servers/hugo.nix`: Hugo static site server with Docker compose +- Additional server modules can be added as needed + +**Headless System Configuration:** + +Headless systems (`skip01`, `warp`) follow simplified patterns: + +```nix +{ + networking.hostName = "hostname"; + + # Minimal hardware configuration + boot.initrd.availableKernelModules = [ "nvme" "xhci_pci" "ahci" ]; + boot.kernelModules = [ "kvm-intel" ]; + + # Enable container support + virtualisation.docker.enable = true; + + # SSH for remote administration + services.openssh = { + enable = true; + settings.PasswordAuthentication = false; + }; + + # Disable desktop services + services.xserver.enable = false; +} +``` + +**Deploying Server Service Modules:** + +1. Add server module to system configuration: + ```nix + in { + nixosConfigurations.skip01 = nixpkgs.lib.nixosSystem { + modules = [ + ./systems/common.nix + ./servers/forgejo.nix # Add server module + ./systems/skip01.nix + { services.forgejo.enable = true; } + ]; + }; + } + ``` + +2. Configure service options: + ```nix + { + services.forgejo = { + domain = "git.example.com"; + database.passwordFile = "/run/keys/forgejo-db"; + }; + } + ``` + +3. Test on server system: + ```bash + nixos-rebuild dry-run --flake .#skip01 + sudo nixos-rebuild switch --flake .#skip01 + ``` + +**Secrets Management for Servers:** + +Server services often require secrets (database passwords, API keys, OAuth secrets). Store these securely: + +- Use `/run/keys/` for runtime secrets (systemd credentials) +- Use environment files with restricted permissions +- Never commit secrets to the repository +- Example: `passwordFile` options that point to secure paths + +**Backup Strategy for Server Services:** + +Server modules include automated backup services: + +```nix +systemd.services.service-backup = { + description = "Service backup service"; + serviceConfig = { + Type = "oneshot"; + User = "service-user"; + ExecStart = "${pkgs.service}/bin/service backup"; + }; +}; + +systemd.timers.service-backup = { + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = "daily"; + Persistent = true; + }; +}; +``` + +**Testing Server Modules:** + +Always test server modules on headless systems before deploying to production: + +```bash +# Check flake outputs +nix flake show + +# Dry-run for server system +sudo nixos-rebuild dry-run --flake .#server-hostname + +# Build without switching +sudo nixos-rebuild build --flake .#server-hostname + +# Switch to new configuration +sudo nixos-rebuild switch --flake .#server-hostname +``` + +**Monitoring and Maintenance:** + +- Check service status: `systemctl status service-name` +- View logs: `journalctl -u service-name` +- Monitor backup timers: `systemctl list-timers` +- Verify firewall rules: `nft list ruleset` +- Check Docker containers: `docker ps` (if using Docker-based services) + +``` + ## Core Principles ### 1. Declarative Configuration @@ -233,6 +488,10 @@ Includes: - Flatpak enabled - USB plugdev group rules +### Server Common Settings (`servers/common.nix`) +Includes: +- Docker containerization enabled for server services + ## File Naming Conventions - Use lowercase with hyphens: `vpn-config.nix`, `development-tools.nix` @@ -263,6 +522,48 @@ Example: } ``` +Server module example: +```nix +{ config, pkgs, lib, ... }: + +let + cfg = config.services.example-service; +in { + options.services.example-service = { + enable = mkEnableOption "Example service"; + domain = mkOption { + type = types.str; + example = "service.example.com"; + description = "Domain name for the service"; + }; + port = mkOption { + type = types.port; + default = 8080; + description = "Service port number"; + }; + }; + + config = mkIf cfg.enable { + systemd.services.example-service = { + description = "Example service"; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + ExecStart = "${pkgs.example}/bin/example --port ${toString cfg.port}"; + Restart = "on-failure"; + }; + }; + + services.nginx.virtualHosts.${cfg.domain} = { + forceSSL = true; + enableACME = true; + locations."/".proxyPass = "http://localhost:${toString cfg.port}"; + }; + + networking.firewall.allowedTCPPorts = [ 80 443 ]; + }; +} +``` + ## Common Workflows ### Adding a New Package @@ -317,6 +618,44 @@ new-system = mkSystem { 3. For user-specific changes: modify `.nix` 4. Home Manager configuration goes in `home-manager.users.` +### Creating a New Server Module + +1. Create `servers/.nix` following the module pattern +2. Define options using `mkOption` for all configurable parameters +3. Use `mkIf cfg.enable` to wrap all configuration +4. Include database, reverse proxy (Nginx), and firewall configuration +5. Add automated backup systemd service with timer +6. Document required secrets (passwordFile paths, API keys) +7. Test on headless system first + +### Creating a New App Flake + +1. Create `appflakes//` directory with `flake.nix` +2. Include `nixpkgs` and `flake-utils` as inputs +3. Define outputs with `packages` (and optionally `nixosModules`, `apps`) +4. Use `flake-utils.lib.eachDefaultSystem` for multi-system support +5. Add local input reference in main `flake.nix` if needed +6. Test build with `nix build .#appflakes/` + +### Adding a Server Service to a System + +1. Choose target system (typically headless: `skip01` or `warp`) +2. Add server module to system's modules list in `flake.nix`: +```nix +nixosConfigurations.server = mkSystem { + modules = [ + ./systems/common.nix + ./servers/common.nix + ./servers/service-name.nix + ./systems/server.nix + { services.service-name.enable = true; } + ]; +}; +``` +3. Configure service-specific options in host config or separate file +4. Set up secret files (e.g., `/run/keys/service-db`) +5. Run dry-run and test before switching + ## Testing and Validation ### Pre-commit Testing @@ -334,6 +673,10 @@ sudo nixos-rebuild dry-run --flake .#labrizor # 3. Check flake outputs nix flake show + +# For server configurations, also test: +sudo nixos-rebuild dry-run --flake .#skip01 +sudo nixos-rebuild dry-run --flake .#warp ``` ### Applying Changes @@ -355,6 +698,7 @@ sudo nixos-rebuild boot --flake .#hostname - Verify all services start correctly: `systemctl status` - Check for package conflicts - Validate hardware-specific configurations +- For server modules: verify Docker containers run, check systemd timers, validate firewall rules ### Updating Packages @@ -665,6 +1009,20 @@ Consider using `nix-serve` or Cachix for: | Search packages | `nix search nixpkgs name` | | Garbage collect | `sudo nix-collect-garbage -d` | +**Server-Specific Commands:** +| Action | Command | +|--------|---------| +| Check service status | `systemctl status service-name` | +| View service logs | `journalctl -u service-name` | +| List active timers | `systemctl list-timers` | +| Check Docker containers | `docker ps` | +| View Docker logs | `docker logs container-name` | +| Restart docker-compose stack | `docker-compose -f /etc/compose/service/docker-compose.yml restart` | +| Check firewall rules | `nft list ruleset` | +| Verify SSL certificates | `certbot certificates` | +| List backup directory | `ls -lh /var/backups/service-name` | +| Run manual backup | `systemctl start service-backup` | + ### File Paths Reference | Type | Location | @@ -674,6 +1032,12 @@ Consider using `nix-serve` or Cachix for: | Common user settings | `users/common-home.nix` | | User config | `users/.nix` | | Desktop apps | `desktop/.nix` | +| Server common settings | `servers/common.nix` | +| Server service modules | `servers/.nix` | +| App flakes | `appflakes//flake.nix` | +| Docker compose configs | `/etc/compose//docker-compose.yml` | +| Backup directories | `/var/backups//` | +| Runtime secrets | `/run/keys/` | | Flake definition | `flake.nix` | ## Maintenance & Changelog @@ -683,8 +1047,10 @@ This section tracks changes to the repository structure and when this document w ### Current Repository State - **Last Updated**: February 2026 - **NixOS Version**: 25.11 -- **Managed Systems**: 3 (framework, aurora, labrizor) +- **Managed Systems**: 5 (framework, aurora, labrizor, skip01, warp) - **Users**: jsutter (primary), isutter, aksutter +- **App Flakes**: 2 (immich, octofriend) +- **Server Modules**: 3 (common, forgejo, hugo) ### Repository Change Log @@ -696,6 +1062,8 @@ This section tracks changes to the repository structure and when this document w | 2026-02 - Cleanup | Removed commented packages across multiple files; fixed trailing whitespace | Code quality | | 2026-02 - Cleanup | Removed .clinerules file; deleted gnome.nix (unused); common-headless.nix (duplicated) | File structure | | 2026-02 - Improvement | Added result, *.swp, *~ to .gitignore | Maintenance | +| 2026-02 - Added | Added appflakes directory (immich, octofriend) and servers directory (forgejo, hugo) | Project structure, all server sections | +| 2026-02 - Documentation | Added comprehensive server module patterns, appflake architecture, server workflows, server-specific commands | Repository Overview, Workflows, Quick Reference | ### When to Update This Document @@ -706,24 +1074,40 @@ Update `agents.md` when any of the following occur: - New user configuration added - New or renamed desktop module - Directory restructuring + - New app flake added to `appflakes/` + - New server module added to `servers/` - **Configuration Changes**: - New shared settings in `systems/common.nix` - New shared user settings in `users/common-home.nix` + - New shared settings in `servers/common.nix` - Changes to module pattern or function parameters - New package categories or organizational changes + - Changes to server module standard pattern + - Updates to headless system configuration patterns - **Technology Updates**: - NixOS version changes - Channel updates (stable/unstable) - New major inputs added to flake - Significant Nix configuration changes + - New app flake with different architecture + - Changes to Docker/compose deployment patterns - **Procedure Changes**: - New testing workflow - Changed emergency procedures - Updated installation steps - New common commands + - New server deployment or backup procedures + - Changes to secrets management approach for servers + +- **Server Changes**: + - New server service added to production + - Changes to backup strategy for server services + - Updates to monitoring or logging for servers + - New headless system deployed or configured + - Security hardening changes for server modules ### Update Template @@ -736,6 +1120,7 @@ When making changes, add an entry using this format: ### Version History for agents.md +- **v1.1** (2026-02): Added comprehensive server module patterns, appflake architecture, server workflows, and server-specific commands - **v1.0** (2025-01): Initial creation with full repository documentation ## Getting Help diff --git a/servers/README.md b/servers/README.md new file mode 100644 index 0000000..79b768b --- /dev/null +++ b/servers/README.md @@ -0,0 +1,148 @@ +# NixOS Server Configurations + +This directory contains server-specific configuration files and modules for various services. + +## Available Configurations + +### common.nix +Common server configuration that includes Docker support. + +**Features:** +- Docker container virtualization +- Base server utilities + +**Usage:** +Include this module in your NixOS configuration for servers that need Docker support. + +```nix +imports = [ ./nixos/servers/common.nix ]; +``` + +### forgejo.nix +Comprehensive Forgejo Git server configuration module. + +**Features:** +- Full Forgejo integration using NixOS native services +- PostgreSQL database with local setup +- TLS/SSL support via Let's Encrypt +- Nginx reverse proxy with WebSocket support +- Git LFS support +- Automated daily backups with retention +- OAuth2 authentication (GitHub) +- Email notifications (SMTP) +- Security hardening + +**Configuration Example:** +```nix +{ config, pkgs, ... }: { + imports = [ ./nixos/servers/forgejo.nix ]; + + services.forgejo = { + enable = true; + domain = "git.example.com"; + database.createLocally = true; + backup = { + enable = true; + interval = "daily"; + retentionDays = 7; + }; + }; +} +``` + +### hugo.nix +Docker-based Hugo static site generator configuration. + +**Features:** +- Hugo web server +- Remark42 comment system +- Watchtower for automatic updates + +**Usage:** +This file uses a docker-compose style format. Deploy using Docker Compose or migrate to NixOS containers. + +## Adding a New Server + +To add a new server configuration: + +1. Create a new `.nix` file in this directory +2. Follow the NixOS module pattern: + ```nix + { config, pkgs, lib, ... }: { + # Your configuration here + } + ``` +3. Import it in your system's `flake.nix` or configuration.nix + +## Best Practices + +- **Reusable Modules:** Design configurations to be reusable across multiple servers +- **Security:** Keep sensitive data (passwords, API keys) out of version control +- **Documentation:** Document complex configurations with inline comments +- **Modularization:** Split complex services into separate files + +## Integration with Main Configuration + +To use these server modules in your NixOS configuration, add them to your `flake.nix`: + +```nix +{ + description = "My NixOS configuration"; + + outputs = { self, nixpkgs, ... }: { + nixosConfigurations.my-server = nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + modules = [ + ./nixos/servers/common.nix + ./nixos/servers/forgejo.nix + # Other configurations + ./systems/my-server.nix + ]; + }; + }; +} +``` + +## Maintenance + +### Backups +Server configurations should be backed up regularly. The Forgejo module includes automated backups. For other services, implement appropriate backup strategies. + +### Updates +Update server configurations with: +```bash +sudo nixos-rebuild switch +``` + +### Monitoring +Monitor server services: +```bash +sudo systemctl status +sudo journalctl -u -f +``` + +## Troubleshooting + +### Common Issues + +1. **Docker Not Starting:** + ```bash + sudo systemctl status docker.service + sudo journalctl -u docker.service -f + ``` + +2. **Port Conflicts:** + Check if services are conflicting on ports: + ```bash + sudo netstat -tulpn + ``` + +3. **Permission Issues:** + Verify file ownership and permissions for service directories + +## Additional Resources + +- [NixOS Documentation](https://nixos.org/manual/nixos/stable/) +- [NixOS Options Search](https://search.nixos.org/options) +- [Forgejo Documentation](https://forgejo.org/docs/latest/) +- [Docker NixOS Module](https://search.nixos.org/options?query=virtualisation.docker) \ No newline at end of file diff --git a/servers/forgejo.nix b/servers/forgejo.nix new file mode 100644 index 0000000..223e615 --- /dev/null +++ b/servers/forgejo.nix @@ -0,0 +1,314 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.forgejo; +in { + options.services.forgejo = { + enable = mkEnableOption "Forgejo Git server"; + + domain = mkOption { + type = types.str; + example = "git.example.com"; + description = "The domain name for Forgejo"; + }; + + stateDir = mkOption { + type = types.str; + default = "/var/lib/forgejo"; + description = "Directory for Forgejo data"; + }; + + database = { + createLocally = mkOption { + type = types.bool; + default = true; + description = "Whether to create a local PostgreSQL database"; + }; + + passwordFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/run/keys/forgejo-db"; + description = "Path to file containing database password"; + }; + }; + + mailer = { + enable = mkOption { + type = types.bool; + default = false; + description = "Enable email notifications"; + }; + + host = mkOption { + type = types.str; + example = "smtp.gmail.com"; + description = "SMTP server hostname"; + }; + + port = mkOption { + type = types.port; + default = 587; + description = "SMTP server port"; + }; + + from = mkOption { + type = types.str; + example = "git@example.com"; + description = "Email sender address"; + }; + + userFile = mkOption { + type = types.path; + example = "/run/keys/forgejo-smtp-user"; + description = "Path to file containing SMTP username"; + }; + + passwordFile = mkOption { + type = types.path; + example = "/run/keys/forgejo-smtp-pass"; + description = "Path to file containing SMTP password"; + }; + }; + + oauth2 = { + enable = mkOption { + type = types.bool; + default = false; + description = "Enable OAuth2 authentication"; + }; + + github = { + enable = mkOption { + type = types.bool; + default = false; + description = "Enable GitHub OAuth2 provider"; + }; + + clientId = mkOption { + type = types.str; + description = "GitHub OAuth client ID"; + }; + + clientSecret = mkOption { + type = types.path; + description = "Path to GitHub OAuth client secret"; + }; + }; + }; + + lfs = { + enable = mkOption { + type = types.bool; + default = true; + description = "Enable Git LFS support"; + }; + + storage = mkOption { + type = types.str; + default = "/var/lib/forgejo/lfs"; + description = "Path to LFS storage"; + }; + }; + + backup = { + enable = mkOption { + type = types.bool; + default = true; + description = "Enable automated backups"; + }; + + interval = mkOption { + type = types.str; + default = "daily"; + description = "Backup interval (systemd timer format)"; + }; + + retentionDays = mkOption { + type = types.int; + default = 7; + description = "Number of backups to keep"; + }; + + backupDir = mkOption { + type = types.str; + default = "/var/backups/forgejo"; + description = "Directory to store backups"; + }; + }; + }; + + config = mkIf cfg.enable { + # PostgreSQL database + services.postgresql = mkIf cfg.database.createLocally { + enable = true; + enableTCPIP = false; + ensureDatabases = [ "forgejo" ]; + ensureUsers = [ + { + name = "forgejo"; + ensureDBOwnership = true; + } + ]; + }; + + # Forgejo service + services.forgejo = { + enable = true; + settings = { + server = { + HTTP_PORT = 3000; + DOMAIN = cfg.domain; + ROOT_URL = "https://${cfg.domain}/"; + DISABLE_SSH = false; + SSH_PORT = 22; + LFS_START_SERVER = cfg.lfs.enable; + LFS_CONTENT_PATH = cfg.lfs.storage; + }; + + database = { + DB_TYPE = "postgres"; + HOST = "/run/postgresql"; + NAME = "forgejo"; + USER = "forgejo"; + PASSWD = mkIf (cfg.database.passwordFile != null) "#dbpass#"; + }; + + service = { + DISABLE_REGISTRATION = false; + REQUIRE_SIGNIN_VIEW = false; + ENABLE_NOTIFY_MAIL = cfg.mailer.enable; + }; + + mailer = mkIf cfg.mailer.enable { + ENABLED = true; + FROM = cfg.mailer.from; + SMTP_ADDR = cfg.mailer.host; + SMTP_PORT = toString cfg.mailer.port; + USER = "#smtpuser#"; + PASSWD = "#smtppass#"; + PROTOCOL = "smtps"; + }; + + session = { + COOKIE_SECURE = true; + COOKIE_SAMESITE = "strict"; + }; + + security = { + INSTALL_LOCK = true; + PASSWORD_CHECK_PWN = true; + PASSWORD_COMPLEXITY = "lower,digit" + }; + }; + }; + + # Set database password from file + systemd.services.forgejo = { + serviceConfig = mkMerge [ + (mkIf (cfg.database.passwordFile != null) { + EnvironmentFile = cfg.database.passwordFile; + }) + (mkIf cfg.mailer.enable { + EnvironmentFile = [ cfg.mailer.userFile cfg.mailer.passwordFile ]; + }) + ]; + }; + + # Nginx reverse proxy + services.nginx = { + enable = true; + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + + virtualHosts.${cfg.domain} = { + forceSSL = true; + enableACME = true; + + locations."/".proxyPass = "http://localhost:3000"; + + # Proxy for WebSocket + locations."/" = { + proxyPass = "http://localhost:3000"; + proxyWebsockets = true; + extraConfig = '' + proxy_buffering off; + proxy_read_timeout 86400; + ''; + }; + + # Proxy for LFS + locations."/.git/info/lfs" = { + proxyPass = "http://localhost:3000"; + extraConfig = '' + client_max_body_size 0; + ''; + }; + }; + }; + + # Firewall + networking.firewall = { + allowedTCPPorts = [ 80 443 ]; + allowedUDPPorts = [ 443 ]; + }; + + # Backup service + systemd.services.forgejo-backup = mkIf cfg.backup.enable { + description = "Forgejo backup service"; + serviceConfig = { + Type = "oneshot"; + User = "forgejo"; + Group = "forgejo"; + WorkingDirectory = cfg.stateDir; + ExecStart = "${pkgs.forgejo}/bin/forgejo dump --type zip --file ${cfg.backup.backupDir}/forgejo-backup-%Y-%m-%d.zip"; + }; + }; + + systemd.timers.forgejo-backup = mkIf cfg.backup.enable { + description = "Forgejo backup timer"; + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = cfg.backup.interval; + Persistent = true; + }; + }; + + # Backup cleanup + systemd.services.forgejo-backup-cleanup = mkIf cfg.backup.enable { + description = "Clean up old Forgejo backups"; + serviceConfig = { + Type = "oneshot"; + User = "root"; + ExecStart = pkgs.writeShellScript "forgejo-backup-cleanup" '' + find ${cfg.backup.backupDir} -name "forgejo-backup-*.zip" -mtime +${toString cfg.backup.retentionDays} -delete + ''; + }; + }; + + systemd.timers.forgejo-backup-cleanup = mkIf cfg.backup.enable { + description = "Forgejo backup cleanup timer"; + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = "weekly"; + Persistent = true; + }; + }; + + # Create backup directory + system.activationScripts.forgejo-backup-dir = '' + mkdir -p ${cfg.backup.backupDir} + chown forgejo:forgejo ${cfg.backup.backupDir} + chmod 750 ${cfg.backup.backupDir} + ''; + + # Add Forgejo to known services + environment.systemPackages = with pkgs; [ + forgejo + ]; + }; +}