This episode explains the industry-standard configuration layout with sites-available and sites-enabled, reusable snippets in an includes directory, and environment variable injection via envsubst in Docker containers.

A healthy NGINX configuration is one a team can maintain for years. A single giant file holding every site, security header, and cache will become a nightmare as the team grows. This Episode 14 covers reusable, modular, and maintainable configuration architecture.
You'll follow the sites-available and sites-enabled convention, create an includes directory for reusable snippets like SSL and security headers, and use envsubst to inject environment variables into the configuration when Docker containers start.
Debian distributions introduced a convention now widely used: two directories with different roles.
/etc/nginx/sites-available/ — where all site configurations are stored, active or not./etc/nginx/sites-enabled/ — contains symlinks to the sites you want enabled.Enabling a site means creating a symlink; disabling means removing the symlink. The original configuration is never lost:
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/example.com
sudo nginx -t
sudo systemctl reload nginx
sudo rm /etc/nginx/sites-enabled/example.com
sudo systemctl reload nginxln -s creates a symlink from sites-available to sites-enabled. If you use conf.d/, the convention is the same but files that aren't included are automatically inactive.
Some configuration must exist on every site: security headers, SSL settings, or common location blocks. Instead of rewriting them over and over, create an includes directory:
sudo mkdir -p /etc/nginx/includesCreate /etc/nginx/includes/ssl.conf for the standard TLS configuration:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_dhparam /etc/nginx/dhparam.pem;
ssl_stapling on;
ssl_stapling_verify on;Then the /etc/nginx/includes/security-headers.conf file:
server_tokens off;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Content-Security-Policy "default-src 'self'" always;Every server block just includes both files:
server {
listen 443 ssl http2;
server_name example.com;
include /etc/nginx/includes/ssl.conf;
include /etc/nginx/includes/security-headers.conf;
root /var/www/example;
}A change to one snippet file applies to every site after a reload. That's the power of modularization.
In a Docker container, the server block is often the same across all environments, except for a few values like the upstream name or host. Hard-coding those values into the image is a mistake. The solution: a template with environment variable placeholders.
A configuration template, for example /etc/nginx/templates/default.conf.template:
server {
listen 80;
server_name ${SERVER_NAME};
location / {
proxy_pass http://${BACKEND_HOST}:${BACKEND_PORT};
proxy_set_header Host $host;
}
}Then use envsubst when the container starts to replace SERVER_NAME, BACKEND_HOST, and BACKEND_PORT:
SERVER_NAME=example.com BACKEND_HOST=app BACKEND_PORT=3000 \
envsubst '${SERVER_NAME} ${BACKEND_HOST} ${BACKEND_PORT}' \
< /etc/nginx/templates/default.conf.template \
> /etc/nginx/conf.d/default.conf
nginx -t
nginx -s reloadThe envsubst '{variable names}' command only replaces the listed variables, so genuine NGINX variables like $host aren't corrupted. The official nginx image has a /etc/nginx/templates/ directory that automatically processes .template files through envsubst at startup.
Episode 14 organized your configuration architecture: using the sites-available and sites-enabled convention, creating reusable snippets in includes, and injecting environment variables with envsubst for Docker containers.
Key takeaways:
sites-available stores all sites; sites-enabled holds the active symlinks.ssl.conf and security-headers.conf snippets reduce configuration duplication.envsubst replaces environment variables at container startup.envsubst so NGINX variables aren't corrupted.In the next episode we'll discuss customized logging, JSON logging, and log rotation — formatting the access log with log_format, producing JSON logs for SIEM integration, conditional logging, and rotating log files with logrotate.