From e8318215a9ae02ec9c5c2d9810786f358d6c74e1 Mon Sep 17 00:00:00 2001 From: Hikari Date: Tue, 3 Mar 2026 16:26:37 -0800 Subject: [PATCH] docs: add README and per-file header comments - Rewrote README with full directory structure, add/remove site workflows, deploy instructions, and a test suite reference table - Added a one-line header comment to each sites-available/*.conf explaining what category of sites belongs in that file --- README.md | 128 +++++++++++++++++++- nginx/nginx/sites-available/afp.conf | 1 + nginx/nginx/sites-available/aria.conf | 1 + nginx/nginx/sites-available/bots.conf | 1 + nginx/nginx/sites-available/cdn.conf | 1 + nginx/nginx/sites-available/celestine.conf | 1 + nginx/nginx/sites-available/content.conf | 1 + nginx/nginx/sites-available/data.conf | 1 + nginx/nginx/sites-available/docs.conf | 1 + nginx/nginx/sites-available/eclaire.conf | 1 + nginx/nginx/sites-available/elowyn.conf | 1 + nginx/nginx/sites-available/forms.conf | 1 + nginx/nginx/sites-available/games.conf | 1 + nginx/nginx/sites-available/git.conf | 1 + nginx/nginx/sites-available/hikari.conf | 1 + nginx/nginx/sites-available/library.conf | 1 + nginx/nginx/sites-available/lucinda.conf | 1 + nginx/nginx/sites-available/lynira.conf | 1 + nginx/nginx/sites-available/mommy.conf | 1 + nginx/nginx/sites-available/monitoring.conf | 1 + nginx/nginx/sites-available/nails.conf | 1 + nginx/nginx/sites-available/notes.conf | 1 + nginx/nginx/sites-available/portfolio.conf | 2 + nginx/nginx/sites-available/rosalia.conf | 1 + nginx/nginx/sites-available/scheduling.conf | 1 + nginx/nginx/sites-available/security.conf | 1 + nginx/nginx/sites-available/support.conf | 1 + nginx/nginx/sites-available/vitalia.conf | 1 + nginx/nginx/sites-available/wtf.conf | 1 + 29 files changed, 154 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9ed3afe..5edf64a 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,132 @@ # Nginx Configs -This repository holds our NGINX configs and offers a basic script for pulling the latest versions from our servers. +This repository holds the nginx configuration for NHCarrigan's production server, with scripts for deploying and pulling changes. -## Live Version +## Directory Structure -These can't really be viewed live... +``` +nginx/nginx/ # Maps directly to /etc/nginx/ on the server +├── nginx.conf # Global settings (workers, gzip, TLS, logging) +├── conf.d/ +│ ├── cloudflare_ips.conf # Real-IP trust for Cloudflare ranges (auto-updated by cron) +│ ├── logging.conf # Custom log formats (custom_format + json_analytics) +│ └── tuning.conf # Performance tweaks (server_names_hash_bucket_size) +├── sites-available/ # One .conf file per logical group of sites +│ └── *.conf +├── sites-enabled/ # Symlinks to active configs in sites-available/ +│ └── * -> ../sites-available/*.conf +└── ... # Standard nginx package files (mime.types, proxy_params, etc.) +``` + +## Adding a New Site + +1. **Identify the right file.** Each `sites-available/*.conf` has a comment at the top describing what belongs there. Pick the most appropriate file, or create a new one if the site does not fit anywhere. + +2. **Add the server block**, following this template for a proxied app: + ```nginx + server { + listen 443 ssl; + server_name yourapp.nhcarrigan.com; + ssl_certificate /etc/letsencrypt/live/yourapp.nhcarrigan.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/yourapp.nhcarrigan.com/privkey.pem; + + location / { + proxy_set_header Host $host; + proxy_pass http://127.0.0.1:; + proxy_redirect off; + } + } + ``` + Or this template for a static site: + ```nginx + server { + listen 443 ssl; + server_name yoursite.nhcarrigan.com; + ssl_certificate /etc/letsencrypt/live/yoursite.nhcarrigan.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/yoursite.nhcarrigan.com/privkey.pem; + + root /home/naomi/yoursite; + + location / { + index index.html; + } + } + ``` + +3. **Keep server blocks sorted alphabetically** by `server_name` within the file. The CI sort check will fail if they are out of order. Note that hyphenated names sort before the bare domain in C locale (e.g. `app-api` before `app`). + +4. **If you created a new `.conf` file**, create the corresponding symlink in `sites-enabled/`: + ```bash + cd nginx/nginx/sites-enabled + ln -s ../sites-available/newfile.conf newfile.conf + ``` + +5. **Get the SSL certificate** on the server before deploying: + ```bash + sudo certbot certonly --nginx -d yourapp.nhcarrigan.com + ``` + +6. **Run the tests** locally to verify everything passes: + ```bash + bash test.sh + ``` + +7. **Deploy** (see below). + +## Removing a Site + +1. Delete the server block from the relevant `sites-available/*.conf` file. +2. If the entire `.conf` file is now empty, delete the file and its `sites-enabled/` symlink. +3. Run `bash test.sh` to confirm nothing is broken. +4. Deploy. + +## Deploying Changes + +Push the local `nginx/nginx/` directory to the server (the `--delete` flag removes any files on the server that no longer exist locally): + +```bash +bash push.sh +``` + +Then reload nginx to apply the changes: + +```bash +ssh prod "sudo systemctl reload nginx" +``` + +To pull the current server config back into this repository: + +```bash +bash pull.sh +``` + +## Testing + +The test suite runs static analysis checks against the config files without requiring a live nginx instance: + +```bash +bash test.sh +``` + +The following checks are run: + +| # | Check | +|---|-------| +| 1 | No deprecated TLS versions (TLSv1 / TLSv1.1) | +| 2 | No duplicate `server_name` values | +| 3 | Every `sites-available/*.conf` has a `sites-enabled` symlink | +| 4 | No broken symlinks in `sites-enabled` | +| 5 | No orphaned symlinks in `sites-enabled` | +| 6 | No port-80 listeners in custom server blocks | +| 7 | `ssl_certificate` and `ssl_certificate_key` counts match per file | +| 8 | All plain-HTTP `proxy_pass` targets are local | +| 9 | All SSL cert paths use `/etc/letsencrypt/live/` | +| 10 | Certs use `fullchain.pem` / keys use `privkey.pem` | +| 11 | No raw IP addresses as `server_name` | +| 12 | `conf.d` contains only expected files | +| 13 | Server blocks are sorted alphabetically by `server_name` within each file | + +CI additionally runs an nginx syntax check (`nginx -t`) using stub SSL certificates, catching any configuration errors that static analysis cannot detect. ## Feedback and Bugs diff --git a/nginx/nginx/sites-available/afp.conf b/nginx/nginx/sites-available/afp.conf index 21ed3e0..9834466 100644 --- a/nginx/nginx/sites-available/afp.conf +++ b/nginx/nginx/sites-available/afp.conf @@ -1,3 +1,4 @@ +# AFP service proxy. server { listen 443 ssl; server_name afp.nhcarrigan.com; diff --git a/nginx/nginx/sites-available/aria.conf b/nginx/nginx/sites-available/aria.conf index 7639f70..a9be343 100644 --- a/nginx/nginx/sites-available/aria.conf +++ b/nginx/nginx/sites-available/aria.conf @@ -1,3 +1,4 @@ +# Aria bot, Cordelia AI assistant, trans-related services, and legacy redirects. server { listen 443 ssl; server_name aria.nhcarrigan.com; diff --git a/nginx/nginx/sites-available/bots.conf b/nginx/nginx/sites-available/bots.conf index 5e7d57c..f8937e5 100644 --- a/nginx/nginx/sites-available/bots.conf +++ b/nginx/nginx/sites-available/bots.conf @@ -1,3 +1,4 @@ +# Discord bots and automated services (one entry per bot, sorted alphabetically). server { listen 443 ssl; server_name altaria.nhcarrigan.com; diff --git a/nginx/nginx/sites-available/cdn.conf b/nginx/nginx/sites-available/cdn.conf index 9f95a74..1d41f2f 100644 --- a/nginx/nginx/sites-available/cdn.conf +++ b/nginx/nginx/sites-available/cdn.conf @@ -1,3 +1,4 @@ +# CDN reverse proxy to Hetzner object storage, with legacy path redirects and CORS headers. server { listen 443 ssl; listen [::]:443 ssl; diff --git a/nginx/nginx/sites-available/celestine.conf b/nginx/nginx/sites-available/celestine.conf index 6dcfa54..9eed0a5 100644 --- a/nginx/nginx/sites-available/celestine.conf +++ b/nginx/nginx/sites-available/celestine.conf @@ -1,3 +1,4 @@ +# Celestine webhook handler and legacy hooks redirect. server { listen 443 ssl; server_name celestine.nhcarrigan.com; diff --git a/nginx/nginx/sites-available/content.conf b/nginx/nginx/sites-available/content.conf index a792a02..4e1a06b 100644 --- a/nginx/nginx/sites-available/content.conf +++ b/nginx/nginx/sites-available/content.conf @@ -1,3 +1,4 @@ +# Static content and publishing sites: blog, books, donate, music, secrets, style, testimonials. server { listen 443 ssl; server_name blog.nhcarrigan.com; diff --git a/nginx/nginx/sites-available/data.conf b/nginx/nginx/sites-available/data.conf index ead034c..d6ef3c6 100644 --- a/nginx/nginx/sites-available/data.conf +++ b/nginx/nginx/sites-available/data.conf @@ -1,3 +1,4 @@ +# Data service proxy. server { listen 443 ssl; server_name data.nhcarrigan.com; diff --git a/nginx/nginx/sites-available/docs.conf b/nginx/nginx/sites-available/docs.conf index 041add9..32bcafb 100644 --- a/nginx/nginx/sites-available/docs.conf +++ b/nginx/nginx/sites-available/docs.conf @@ -1,3 +1,4 @@ +# Documentation and informational sites: contact, docs, manual, sitemap, socials. server { listen 443 ssl; server_name contact.nhcarrigan.com; diff --git a/nginx/nginx/sites-available/eclaire.conf b/nginx/nginx/sites-available/eclaire.conf index 5fb85a3..77998ac 100644 --- a/nginx/nginx/sites-available/eclaire.conf +++ b/nginx/nginx/sites-available/eclaire.conf @@ -1,3 +1,4 @@ +# Eclaire Angular SPA. server { listen 443 ssl; server_name eclaire.nhcarrigan.com; diff --git a/nginx/nginx/sites-available/elowyn.conf b/nginx/nginx/sites-available/elowyn.conf index 88db33b..48c1f51 100644 --- a/nginx/nginx/sites-available/elowyn.conf +++ b/nginx/nginx/sites-available/elowyn.conf @@ -1,3 +1,4 @@ +# Elowyn Angular SPA. server { listen 443 ssl; server_name elowyn.nhcarrigan.com; diff --git a/nginx/nginx/sites-available/forms.conf b/nginx/nginx/sites-available/forms.conf index 4ac6b90..7c667d6 100644 --- a/nginx/nginx/sites-available/forms.conf +++ b/nginx/nginx/sites-available/forms.conf @@ -1,3 +1,4 @@ +# Grist forms platform (forms-api backend + forms frontend with CSS injection) and legacy form URL redirects. server { listen 443 ssl; server_name forms-api.nhcarrigan.com; diff --git a/nginx/nginx/sites-available/games.conf b/nginx/nginx/sites-available/games.conf index cb7acb9..dcfa7ca 100644 --- a/nginx/nginx/sites-available/games.conf +++ b/nginx/nginx/sites-available/games.conf @@ -1,3 +1,4 @@ +# Games and gaming projects: beccalia, games hub, goblin, loan, lore, silly, wompwomp, yurigpt. server { listen 443 ssl; server_name beccalia.nhcarrigan.com; diff --git a/nginx/nginx/sites-available/git.conf b/nginx/nginx/sites-available/git.conf index e54d528..73f2498 100644 --- a/nginx/nginx/sites-available/git.conf +++ b/nginx/nginx/sites-available/git.conf @@ -1,3 +1,4 @@ +# Self-hosted Gitea instance. server { listen 443 ssl; server_name git.nhcarrigan.com; diff --git a/nginx/nginx/sites-available/hikari.conf b/nginx/nginx/sites-available/hikari.conf index 5f3fe4c..e02cd8e 100644 --- a/nginx/nginx/sites-available/hikari.conf +++ b/nginx/nginx/sites-available/hikari.conf @@ -1,3 +1,4 @@ +# Hikari desktop app (Angular SPA + API backend) and legacy redirect subdomains. server { listen 443 ssl; server_name announcements.nhcarrigan.com; diff --git a/nginx/nginx/sites-available/library.conf b/nginx/nginx/sites-available/library.conf index 1894511..37b4ca1 100644 --- a/nginx/nginx/sites-available/library.conf +++ b/nginx/nginx/sites-available/library.conf @@ -1,3 +1,4 @@ +# Library service proxy. server { listen 443 ssl; server_name library.nhcarrigan.com; diff --git a/nginx/nginx/sites-available/lucinda.conf b/nginx/nginx/sites-available/lucinda.conf index a14a8eb..7d8aebd 100644 --- a/nginx/nginx/sites-available/lucinda.conf +++ b/nginx/nginx/sites-available/lucinda.conf @@ -1,3 +1,4 @@ +# Lucinda full-stack app (Angular SPA + API backend). server { listen 443 ssl; server_name lucinda.nhcarrigan.com; diff --git a/nginx/nginx/sites-available/lynira.conf b/nginx/nginx/sites-available/lynira.conf index 3f5db4c..12efa5e 100644 --- a/nginx/nginx/sites-available/lynira.conf +++ b/nginx/nginx/sites-available/lynira.conf @@ -1,3 +1,4 @@ +# Lynira.link domain (bare + www). server { listen 443 ssl; server_name lynira.link; diff --git a/nginx/nginx/sites-available/mommy.conf b/nginx/nginx/sites-available/mommy.conf index e05e5a1..844ba94 100644 --- a/nginx/nginx/sites-available/mommy.conf +++ b/nginx/nginx/sites-available/mommy.conf @@ -1,3 +1,4 @@ +# Mommy bot suite: mommy-bot Discord bot, mommy-slack Slack bot, mommy web front-end. server { listen 443 ssl; server_name mommy-bot.nhcarrigan.com; diff --git a/nginx/nginx/sites-available/monitoring.conf b/nginx/nginx/sites-available/monitoring.conf index 39061b2..bb4d897 100644 --- a/nginx/nginx/sites-available/monitoring.conf +++ b/nginx/nginx/sites-available/monitoring.conf @@ -1,3 +1,4 @@ +# Monitoring stack: analytics, incidents, logs, telemetry, uptime. server { listen 443 ssl; server_name analytics.nhcarrigan.com; diff --git a/nginx/nginx/sites-available/nails.conf b/nginx/nginx/sites-available/nails.conf index febcd26..b6dfe45 100644 --- a/nginx/nginx/sites-available/nails.conf +++ b/nginx/nginx/sites-available/nails.conf @@ -1,3 +1,4 @@ +# Nails app: Angular front-end SPA and API backend. server { listen 443 ssl; server_name nails-api.nhcarrigan.com; diff --git a/nginx/nginx/sites-available/notes.conf b/nginx/nginx/sites-available/notes.conf index 7f7dfc4..5e825fe 100644 --- a/nginx/nginx/sites-available/notes.conf +++ b/nginx/nginx/sites-available/notes.conf @@ -1,3 +1,4 @@ +# SilverBullet notes instance and Planka project board. server { listen 443 ssl; server_name board.nhcarrigan.com; diff --git a/nginx/nginx/sites-available/portfolio.conf b/nginx/nginx/sites-available/portfolio.conf index 5de0b76..642319e 100644 --- a/nginx/nginx/sites-available/portfolio.conf +++ b/nginx/nginx/sites-available/portfolio.conf @@ -1,3 +1,5 @@ +# Personal portfolio and vanity domains (naomi.lgbt, naomi.party, nhcarrigan.com, nhcarrigan.link, resume) +# plus a wildcard catch-all that redirects *.naomi.lgbt → *.nhcarrigan.com. server { listen 443 ssl; server_name naomi.lgbt; diff --git a/nginx/nginx/sites-available/rosalia.conf b/nginx/nginx/sites-available/rosalia.conf index 4806f6a..7d397f6 100644 --- a/nginx/nginx/sites-available/rosalia.conf +++ b/nginx/nginx/sites-available/rosalia.conf @@ -1,3 +1,4 @@ +# Rosalia alerting service and legacy alerts redirect. server { listen 443 ssl; server_name alerts.nhcarrigan.com; diff --git a/nginx/nginx/sites-available/scheduling.conf b/nginx/nginx/sites-available/scheduling.conf index 2f60743..b313404 100644 --- a/nginx/nginx/sites-available/scheduling.conf +++ b/nginx/nginx/sites-available/scheduling.conf @@ -1,3 +1,4 @@ +# Scheduling shortcuts that redirect to zcal.co (cyc, meet) and tasks redirect. server { listen 443 ssl; server_name cyc.nhcarrigan.com; diff --git a/nginx/nginx/sites-available/security.conf b/nginx/nginx/sites-available/security.conf index 89d430e..09d441c 100644 --- a/nginx/nginx/sites-available/security.conf +++ b/nginx/nginx/sites-available/security.conf @@ -1,3 +1,4 @@ +# Security tooling: SonarQube code quality gate and DefectDojo vulnerability management. server { listen 443 ssl; server_name quality.nhcarrigan.com; diff --git a/nginx/nginx/sites-available/support.conf b/nginx/nginx/sites-available/support.conf index be67f78..8b1f3cd 100644 --- a/nginx/nginx/sites-available/support.conf +++ b/nginx/nginx/sites-available/support.conf @@ -1,3 +1,4 @@ +# Discourse community support forum and legacy chat/forum redirects. server { listen 443 ssl; server_name chat.nhcarrigan.com; diff --git a/nginx/nginx/sites-available/vitalia.conf b/nginx/nginx/sites-available/vitalia.conf index 3bf9200..7e47f4e 100644 --- a/nginx/nginx/sites-available/vitalia.conf +++ b/nginx/nginx/sites-available/vitalia.conf @@ -1,3 +1,4 @@ +# Vitalia app: Angular front-end SPA and API backend. server { listen 443 ssl; server_name vitalia-api.nhcarrigan.com; diff --git a/nginx/nginx/sites-available/wtf.conf b/nginx/nginx/sites-available/wtf.conf index 30b3fc8..685b6fe 100644 --- a/nginx/nginx/sites-available/wtf.conf +++ b/nginx/nginx/sites-available/wtf.conf @@ -1,3 +1,4 @@ +# wtf.naomi.lgbt personal project. server { listen 443 ssl; server_name wtf.naomi.lgbt;