Use environment variables in Nginx configuration

Render configuration templates before startup, preserve Nginx variables, and understand what the env directive actually does.

Best practices
Illustration of server operations and request logs

Nginx does not generally interpolate shell environment variables in its configuration. listen $PORT; does not turn an environment value into a port. Render a configuration template before starting Nginx, or use a module explicitly designed to read the process environment.

Render a template with the official container image

Create default.conf.template:

server {
    listen ${PORT};
    server_name ${SERVER_NAME};

    location / {
        proxy_pass ${UPSTREAM_URL};
        proxy_set_header Host $host;
    }
}

Mount it in the official image's template directory and supply values:

docker run --rm -p 8080:8080 \
  -e PORT=8080 \
  -e SERVER_NAME=example.com \
  -e UPSTREAM_URL=http://backend:9000 \
  -v "$PWD/default.conf.template:/etc/nginx/templates/default.conf.template:ro" \
  nginx

The backend name must be reachable and resolvable from the container, for example through the intended Docker network. Pin an approved image version or digest in a real deployment.

The official entrypoint renders .template files from /etc/nginx/templates into /etc/nginx/conf.d by default. Running a custom command or replacing the entrypoint can change whether that processing occurs. The output directory must be writable.

Use ${PORT}, not ${PORT:-80}: envsubst does not implement shell default-value expressions. Set defaults in the environment or entrypoint before rendering.

Render your own configuration explicitly

For an existing template and writable output directory:

export PORT="${PORT:-80}"
: "${SERVER_NAME:?Set SERVER_NAME}"
: "${UPSTREAM_URL:?Set UPSTREAM_URL}"

envsubst '$PORT $SERVER_NAME $UPSTREAM_URL' \
  < default.conf.template > /etc/nginx/conf.d/default.conf
nginx -t || exit 1
exec nginx -g 'daemon off;'

The quoted variable list limits substitution so Nginx variables such as $host, $uri, and $request_uri remain intact. Validate values before putting them into configuration: substitution does not escape arbitrary text for Nginx syntax. For changes to an already running service, render to a temporary file, validate the assembled configuration, and replace/reload through the normal deployment process.

Understand the env directive's boundary

# Main context
env UPSTREAM_URL;

This preserves the value in worker process environments. It does not create an Nginx variable named $UPSTREAM_URL, and it cannot make listen accept an arbitrary dynamic expression.

Modules such as OpenResty's Lua module can read the environment. This optional example declares the target variable before assigning it:

# Main context
env UPSTREAM_URL;

http {
    server {
        listen 80;
        resolver 10.0.0.53;
        location / {
            set $upstream "";
            access_by_lua_block {
                local target = os.getenv("UPSTREAM_URL")
                if not target or target == "" then
                    return ngx.exit(503)
                end
                ngx.var.upstream = target
            }
            proxy_pass $upstream;
        }
    }
}

This requires the Lua module and a trusted resolver appropriate to the environment. The upstream value must be controlled deployment configuration, not client input. Variable-based proxy_pass has its own URI and DNS rules; rendering a literal upstream at startup is usually simpler when values do not change per request.

Use a configuration generator when values come from several systems

Ansible/Jinja2, Consul Template, and similar tools can generate the final file. They still need syntax validation, a clear reload process, and protection for any secrets in the rendered output.

FAQ

Why does $HOSTNAME not resolve in nginx.conf?

Shell environment names and Nginx request variables are different namespaces. Use template rendering when a deployment environment value is required.

Will envsubst erase $uri?

Unrestricted envsubst substitutes shell-style names, including ones meant for Nginx. Use an explicit allowlist. The official image's entrypoint constructs a variable list from the environment; avoid environment-name collisions and use its filter option when appropriate.

Can I export a new value and reload?

Changing a separate shell's environment does not update an existing Nginx master's environment. Regenerate rendered configuration and reload it, or recreate/restart the service with its updated environment as required by the deployment model.

References: official image template processing, Nginx env, OpenResty Lua module.

Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting