# Nginx Domain Templating ## Overview The nginx configuration now uses environment variable templating to support dynamic domain configuration. This allows you to change the domain for all services by simply updating the `DOMAIN` environment variable in `.env`. ## How It Works 1. **Template Files**: Nginx configuration files are stored as templates with `.template` extension - `nginx/conf.d/api.conf.template` - `nginx/conf.d/default.conf.template` - `nginx/conf.d/services.conf.template` 2. **Environment Variable**: The `DOMAIN` variable in `.env` controls all subdomains ```bash DOMAIN=betteredmonton.org ``` 3. **Startup Process**: When the nginx container starts: - The entrypoint script (`nginx/entrypoint.sh`) runs first - It uses `envsubst` to replace `${DOMAIN}` in templates with the actual value - Generated `.conf` files are created in `/etc/nginx/conf.d/` - Nginx starts with the generated configuration 4. **Docker Configuration**: The `DOMAIN` env var is passed to nginx via `docker-compose.yml`: ```yaml environment: - DOMAIN=${DOMAIN:-cmlite.org} ``` ## Configured Subdomains All subdomains automatically use the `DOMAIN` value from `.env`: | Subdomain | Service | Port | |-----------|---------|------| | `${DOMAIN}` | Admin GUI (root domain) | - | | `app.${DOMAIN}` | Admin GUI | - | | `api.${DOMAIN}` | API Server | - | | `db.${DOMAIN}` | NocoDB | - | | `docs.${DOMAIN}` | MkDocs | - | | `code.${DOMAIN}` | Code Server | - | | `listmonk.${DOMAIN}` | Listmonk | - | | `grafana.${DOMAIN}` | Grafana | - | | `git.${DOMAIN}` | Gitea | - | | `n8n.${DOMAIN}` | n8n | - | | `mail.${DOMAIN}` | MailHog | - | | `qr.${DOMAIN}` | Mini QR | - | | `draw.${DOMAIN}` | Excalidraw | - | | `home.${DOMAIN}` | Homepage | - | ## Changing the Domain To change to a new domain: 1. **Update `.env`**: ```bash DOMAIN=newdomain.com ``` 2. **Rebuild nginx and restart admin**: ```bash docker compose build nginx docker compose up -d nginx admin ``` Note: Admin needs to restart to pick up the new DOMAIN for Vite's allowed hosts configuration. 3. **Update Pangolin resources** (if using Pangolin tunnel): - The Pangolin admin page will automatically show the new domain in the resource list - Create Public resources in Pangolin dashboard for each subdomain you need - Point each resource to `nginx:80` as the target 4. **Check nginx logs**: ```bash docker compose logs nginx --tail 20 ``` You should see: `Configuring nginx for domain: newdomain.com` ## Pangolin Resource Creation For each subdomain you want accessible through Pangolin: 1. Go to Pangolin dashboard → Resources → Create Resource 2. **Resource Type**: Public Site 3. **URL**: `https://subdomain.yourdomain.org` 4. **Target**: `nginx` (or the Newt connection ID) 5. **Protocol**: http 6. **Backend**: `nginx:80` Repeat for all required subdomains (app, api, db, etc.) ## Troubleshooting **Duplicate server name warning**: - Check nginx logs for conflicting `server_name` directives - This usually means a subdomain is defined twice in the templates **502 Bad Gateway**: - Verify the backend service is running: `docker compose ps` - Check nginx can reach the backend: `docker compose exec nginx wget -qO- http://backend:port` - Review nginx error logs: `docker compose logs nginx` **Domain not updating**: - Ensure you rebuilt nginx: `docker compose build nginx` - Verify the DOMAIN env var is set: `docker compose config | grep DOMAIN` - Check generated configs: `docker compose exec nginx cat /etc/nginx/conf.d/services.conf | grep server_name` ## Technical Details **Template Syntax**: ```nginx server_name app.${DOMAIN}; ``` **Generated Output** (with `DOMAIN=betteredmonton.org`): ```nginx server_name app.betteredmonton.org; ``` **Entrypoint Script** (`nginx/entrypoint.sh`): ```bash #!/bin/sh export DOMAIN=${DOMAIN:-cmlite.org} envsubst '${DOMAIN}' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf envsubst '${DOMAIN}' < /etc/nginx/conf.d/api.conf.template > /etc/nginx/conf.d/api.conf envsubst '${DOMAIN}' < /etc/nginx/conf.d/services.conf.template > /etc/nginx/conf.d/services.conf nginx -t exec /docker-entrypoint.sh "$@" ``` ## Files Modified - `nginx/Dockerfile` — Added gettext package, entrypoint script - `nginx/entrypoint.sh` — New file: templates configs with envsubst - `nginx/conf.d/*.template` — Template versions of all nginx configs - `docker-compose.yml` — Added DOMAIN environment variable to nginx and admin services - `docker-compose.yml` — Removed read-only conf.d volume mount (configs generated at runtime) - `docker-compose.yml` — Updated healthcheck (removed crond check) - `admin/vite.config.ts` — Dynamic allowed hosts based on DOMAIN env var ## Benefits 1. **Single Source of Truth**: Change domain in one place (`.env`) 2. **No Manual Edits**: No need to edit nginx configs or Vite config files 3. **Environment-Specific**: Use different domains for dev/staging/production 4. **Pangolin Integration**: Resource list automatically uses current domain 5. **Version Control Friendly**: Templates are committed, generated configs are not 6. **Vite Host Check**: Automatically allows the configured domain in Vite's dev server ## Vite Configuration The admin Vite dev server now dynamically configures allowed hosts based on the DOMAIN environment variable: ```typescript // admin/vite.config.ts const domain = process.env.DOMAIN || 'cmlite.org'; export default defineConfig({ server: { allowedHosts: [ `.${domain}`, // Allow all subdomains 'changemaker-v2-admin', // Container hostname 'localhost', '127.0.0.1', ], }, }); ``` This prevents Vite's host check from blocking requests when accessing the app through: - Pangolin tunnel with custom domain - Cloudflare tunnel - Any reverse proxy with custom domain - Docker container networking