5.8 KiB
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
-
Template Files: Nginx configuration files are stored as templates with
.templateextensionnginx/conf.d/api.conf.templatenginx/conf.d/default.conf.templatenginx/conf.d/services.conf.template
-
Environment Variable: The
DOMAINvariable in.envcontrols all subdomainsDOMAIN=betteredmonton.org -
Startup Process: When the nginx container starts:
- The entrypoint script (
nginx/entrypoint.sh) runs first - It uses
envsubstto replace${DOMAIN}in templates with the actual value - Generated
.conffiles are created in/etc/nginx/conf.d/ - Nginx starts with the generated configuration
- The entrypoint script (
-
Docker Configuration: The
DOMAINenv var is passed to nginx viadocker-compose.yml: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:
-
Update
.env:DOMAIN=newdomain.com -
Rebuild nginx and restart admin:
docker compose build nginx docker compose up -d nginx adminNote: Admin needs to restart to pick up the new DOMAIN for Vite's allowed hosts configuration.
-
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:80as the target
-
Check nginx logs:
docker compose logs nginx --tail 20You should see:
Configuring nginx for domain: newdomain.com
Pangolin Resource Creation
For each subdomain you want accessible through Pangolin:
- Go to Pangolin dashboard → Resources → Create Resource
- Resource Type: Public Site
- URL:
https://subdomain.yourdomain.org - Target:
nginx(or the Newt connection ID) - Protocol: http
- Backend:
nginx:80
Repeat for all required subdomains (app, api, db, etc.)
Troubleshooting
Duplicate server name warning:
- Check nginx logs for conflicting
server_namedirectives - 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:
server_name app.${DOMAIN};
Generated Output (with DOMAIN=betteredmonton.org):
server_name app.betteredmonton.org;
Entrypoint Script (nginx/entrypoint.sh):
#!/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 scriptnginx/entrypoint.sh— New file: templates configs with envsubstnginx/conf.d/*.template— Template versions of all nginx configsdocker-compose.yml— Added DOMAIN environment variable to nginx and admin servicesdocker-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
- Single Source of Truth: Change domain in one place (
.env) - No Manual Edits: No need to edit nginx configs or Vite config files
- Environment-Specific: Use different domains for dev/staging/production
- Pangolin Integration: Resource list automatically uses current domain
- Version Control Friendly: Templates are committed, generated configs are not
- 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:
// 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