# Website hosting with Nginx reverse proxy and Route53 DDNS { config, pkgs, inputs, lib, ... }: let domain = config.homelab.domain; hostedZoneId = config.homelab.aws.hostedZoneId; # Configurable list of DNS records to update dnsRecords = [ domain "jellyfin.${domain}" "git.${domain}" # Add more records here as needed # "api.${domain}" # "mail.${domain}" ]; updateRoute53 = pkgs.writeShellScript "update-route53" '' #!/usr/bin/env bash set -euo pipefail HOSTED_ZONE_ID="${hostedZoneId}" DNS_RECORDS=(${lib.concatStringsSep " " (map lib.escapeShellArg dnsRecords)}) # Get current public IP CURRENT_IP=$(${pkgs.curl}/bin/curl -s https://ifconfig.me) # Validate IP format if ! echo "$CURRENT_IP" | grep -qE '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$'; then echo "[$(date)] ERROR: Invalid IP format: $CURRENT_IP" >&2 exit 1 fi # Function to update a DNS record update_record() { local RECORD_NAME=$1 local DNS_IP=$(${pkgs.dig}/bin/dig +short "$RECORD_NAME" @8.8.8.8 | tail -n1) if [ "$CURRENT_IP" != "$DNS_IP" ]; then echo "[$(date)] $RECORD_NAME IP changed: $DNS_IP -> $CURRENT_IP" ${pkgs.awscli2}/bin/aws route53 change-resource-record-sets \ --hosted-zone-id "$HOSTED_ZONE_ID" \ --change-batch "{ \"Changes\": [{ \"Action\": \"UPSERT\", \"ResourceRecordSet\": { \"Name\": \"$RECORD_NAME\", \"Type\": \"A\", \"TTL\": 300, \"ResourceRecords\": [{\"Value\": \"$CURRENT_IP\"}] } }] }" echo "[$(date)] $RECORD_NAME DNS updated successfully to $CURRENT_IP" else echo "[$(date)] $RECORD_NAME IP unchanged: $CURRENT_IP" fi } # Update all configured records for record in "''${DNS_RECORDS[@]}"; do update_record "$record" done ''; in { networking.firewall.allowedTCPPorts = [ 80 443 ]; services.nginx = { enable = true; recommendedGzipSettings = true; recommendedOptimisation = true; recommendedProxySettings = true; recommendedTlsSettings = true; virtualHosts."${domain}" = { forceSSL = true; enableACME = true; root = "${inputs.thelenlucas.packages.${pkgs.system}.default}"; }; virtualHosts."jellyfin.${domain}" = { forceSSL = true; enableACME = true; locations."/" = { proxyPass = "http://localhost:8096"; }; }; virtualHosts."git.${domain}" = { forceSSL = true; enableACME = true; locations."/" = { proxyPass = "http://localhost:3000"; }; }; # Local git access to avoid NAT hairpinning virtualHosts."git.homelab" = { locations."/" = { proxyPass = "http://localhost:3000"; }; }; }; security.acme = { acceptTerms = true; defaults.email = "thelenlucas@gmail.com"; }; environment.systemPackages = [ pkgs.awscli2 ]; systemd.services.route53-ddns = { description = "Update Route 53 with current IP periodically"; after = [ "network-online.target" ]; wants = [ "network-online.target" ]; serviceConfig = { Type = "oneshot"; ExecStart = updateRoute53; }; }; systemd.timers.route53-ddns = { description = "Route 53 DDNS Update Timer"; wantedBy = [ "timers.target" ]; timerConfig = { OnBootSec = "1min"; OnUnitActiveSec = "5min"; Persistent = true; }; }; }