This commit is contained in:
Julian Sutter 2026-02-15 16:57:07 -08:00
parent 532e01cbf1
commit 29eb6493be
3 changed files with 854 additions and 7 deletions

148
servers/README.md Normal file
View file

@ -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 <service-name>
sudo journalctl -u <service-name> -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)

314
servers/forgejo.nix Normal file
View file

@ -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
];
};
}