How to Validate Nginx Site Configs During Ansible Deployment Using nginx -t


2 views

When managing multiple Nginx site configurations through Ansible, validating individual site configs presents unique challenges. Unlike the main nginx.conf which is a complete configuration file, site-specific configs are partial fragments that need to be wrapped in proper context for validation.

# This works for the main config
validate="/usr/sbin/nginx -c %s -t"

# But fails for site configs because they lack http{} context
server {
    listen 80;
    server_name example.com;
    # ... site configuration
}

Here's a complete Ansible solution that creates temporary validation contexts:

- name: Validate site configs before deployment
  block:
    - name: Create temporary validation wrapper
      template:
        src: nginx-validation-wrapper.j2
        dest: "/tmp/{{ item.name }}.validate.conf"
        mode: 0644
      with_items: sites
      vars:
        config_path: "/etc/nginx/conf.d/{{ item.name }}.conf"

    - name: Validate site configs
      command: "/usr/sbin/nginx -t -c /tmp/{{ item.name }}.validate.conf"
      register: validation_result
      ignore_errors: yes
      changed_when: false
      with_items: sites

    - name: Fail if validation errors exist
      fail:
        msg: "Nginx config validation failed: {{ validation_result.stderr }}"
      when: validation_result.rc != 0
      with_items: sites

Create nginx-validation-wrapper.j2 with this content:

events {
    worker_connections 1024;
}

http {
    include {{ config_path }};
}

Modify your original config deployment to include validation:

- name: Deploy site nginx config
  template:
    src: "nginx-site.conf.j2"
    dest: "/etc/nginx/conf.d/{{ item.name }}.conf"
    owner: root
    group: root
    mode: 0444
  with_items: sites
  notify: restart nginx
  # Only proceed after validation succeeds
  when: validation_result.rc == 0

For complex setups with included files:

http {
    include /etc/nginx/mime.types;
    include /etc/nginx/conf.d/*.conf;
    include {{ config_path }};
}

This ensures all necessary context exists during validation while still testing the specific config file.

For better performance in large environments:

- name: Batch validate all site configs
  command: "/usr/sbin/nginx -t -c /tmp/nginx-validation.conf"
  vars:
    config_content: |
      events { worker_connections 1024; }
      http {
        {% for site in sites %}
        include /etc/nginx/conf.d/{{ site.name }}.conf;
        {% endfor %}
      }

When managing Nginx configurations through Ansible, we often face a validation gap with site-specific configs. While the main nginx.conf can be validated using nginx -t -c %s, individual site configurations pose a special challenge because:

  • They require being wrapped in http {} context to pass syntax check
  • Traditional validation methods treat them as incomplete fragments
  • Ansible's template module executes validation before file placement

Here's a robust approach I've implemented in production environments:

- name: Validate site configs with proper context
  template:
    src: "nginx-site.conf.j2"
    dest: "/tmp/{{ item.name }}.conf.tmp"
    validate: "/bin/bash -c 'echo \"http { $(cat %s) }\" > %s.ctx && /usr/sbin/nginx -t -c %s.ctx'"
  with_items: sites
  register: validation_result
  check_mode: yes
  changed_when: false

- name: Deploy validated configs
  template:
    src: "nginx-site.conf.j2"
    dest: "/etc/nginx/conf.d/{{ item.name }}.conf"
    owner: root
    group: root
    mode: "0444"
  with_items: sites
  when: validation_result is success
  notify: restart nginx

The solution works through several key steps:

  1. Temporary File Creation: First creates a temp file with the raw config
  2. Context Wrapping: Uses bash to wrap the content in http{} block
  3. Syntax Check: Validates the wrapped configuration
  4. Safe Deployment: Only proceeds with actual deployment after validation

For more complex scenarios, consider these enhancements:

# For environments with custom Nginx paths
validate: "/bin/bash -c 'NGINX=$(which nginx); echo \"http { $(cat %s) }\" > %s.ctx && $NGINX -t -c %s.ctx'"

# Adding timestamped validation
validate: "/bin/bash -c 'TS=$(date +%s); echo \"http { $(cat %s) }\" > /tmp/nginx-val-\"$TS\".conf && nginx -t -c /tmp/nginx-val-\"$TS\".conf && rm -f /tmp/nginx-val-\"$TS\".conf'"

To properly handle validation errors in your playbook:

- name: Validate configurations
  # ... previous validation task ...

- name: Fail if validation errors
  fail:
    msg: "Nginx configuration validation failed"
  when: validation_result is failed

- name: Cleanup temp files on success  
  file:
    path: "/tmp/{{ item.name }}.conf.tmp"
    state: absent
  with_items: sites
  when: validation_result is success