changemaker.lite/docs/NGINX_DOMAIN_TEMPLATING.md

177 lines
5.8 KiB
Markdown

# 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