Nginx Ansible Deployment
Introduction
Deploying Nginx manually on multiple servers can be time-consuming and error-prone. Ansible, an open-source automation tool, provides a powerful solution to this challenge by allowing you to define your infrastructure as code and automate the deployment process. This guide will walk you through how to deploy Nginx using Ansible, making your infrastructure management more efficient, consistent, and scalable.
Prerequisites
Before we begin, make sure you have:
- Basic knowledge of Linux commands
- Ansible installed on your control machine (version 2.9+)
- Target servers accessible via SSH
- Basic understanding of YAML syntax
Understanding Ansible for Nginx Deployment
Ansible uses a declarative approach to define the desired state of your servers. For Nginx deployment, this means:
- Playbooks: YAML files that define what tasks to execute on which hosts
- Roles: Reusable collections of tasks for specific functions (like installing Nginx)
- Inventory: A list of target servers organized into groups
- Handlers: Tasks that only run when notified by other tasks (e.g., restarting Nginx after config changes)
Setting Up Your Ansible Environment
1. Create Your Project Structure
Let's start by setting up a proper project structure:
mkdir -p nginx-ansible-deployment/roles/nginx/{tasks,handlers,templates,defaults}
cd nginx-ansible-deployment
touch inventory.ini ansible.cfg playbook.yml
touch roles/nginx/tasks/main.yml
touch roles/nginx/handlers/main.yml
touch roles/nginx/templates/nginx.conf.j2
touch roles/nginx/defaults/main.yml
2. Configure Ansible
Create basic configuration in ansible.cfg
:
[defaults]
inventory = inventory.ini
host_key_checking = False
roles_path = ./roles
3. Define Your Inventory
Create your inventory file (inventory.ini
) with your target servers:
[webservers]
web1 ansible_host=192.168.1.101
web2 ansible_host=192.168.1.102
[loadbalancers]
lb1 ansible_host=192.168.1.103
[all:vars]
ansible_user=ubuntu
ansible_ssh_private_key_file=~/.ssh/id_rsa
Creating the Nginx Role
1. Define Default Variables
In roles/nginx/defaults/main.yml
, set default values:
---
# Nginx version and installation options
nginx_package_name: nginx
nginx_service_name: nginx
nginx_service_state: started
nginx_service_enabled: yes
# Configuration options
nginx_worker_processes: auto
nginx_worker_connections: 1024
nginx_keepalive_timeout: 65
nginx_client_max_body_size: 1m
# HTTP configuration
nginx_http_port: 80
nginx_https_port: 443
nginx_enable_https: false
# Directories
nginx_conf_dir: /etc/nginx
nginx_log_dir: /var/log/nginx
# Default website
nginx_default_site_enabled: yes
nginx_default_site_template: default.conf.j2
2. Create Tasks
In roles/nginx/tasks/main.yml
, define installation and configuration tasks:
---
# Install Nginx
- name: Install Nginx package
apt:
name: "{{ nginx_package_name }}"
state: present
update_cache: yes
become: yes
tags:
- nginx
- install
# Ensure directories exist
- name: Ensure Nginx configuration directories exist
file:
path: "{{ item }}"
state: directory
owner: root
group: root
mode: 0755
with_items:
- "{{ nginx_conf_dir }}"
- "{{ nginx_conf_dir }}/sites-available"
- "{{ nginx_conf_dir }}/sites-enabled"
- "{{ nginx_conf_dir }}/conf.d"
become: yes
tags:
- nginx
- configuration
# Configure Nginx main configuration
- name: Configure Nginx main configuration
template:
src: nginx.conf.j2
dest: "{{ nginx_conf_dir }}/nginx.conf"
owner: root
group: root
mode: 0644
become: yes
notify: restart nginx
tags:
- nginx
- configuration
# Configure default site
- name: Configure default site
template:
src: "{{ nginx_default_site_template }}"
dest: "{{ nginx_conf_dir }}/sites-available/default"
owner: root
group: root
mode: 0644
when: nginx_default_site_enabled
become: yes
notify: restart nginx
tags:
- nginx
- configuration
# Enable default site
- name: Enable default site
file:
src: "{{ nginx_conf_dir }}/sites-available/default"
dest: "{{ nginx_conf_dir }}/sites-enabled/default"
state: link
when: nginx_default_site_enabled
become: yes
notify: restart nginx
tags:
- nginx
- configuration
# Start and enable Nginx service
- name: Ensure Nginx service is running and enabled
service:
name: "{{ nginx_service_name }}"
state: "{{ nginx_service_state }}"
enabled: "{{ nginx_service_enabled }}"
become: yes
tags:
- nginx
- service
3. Define Handlers
In roles/nginx/handlers/main.yml
, create handlers for service management:
---
- name: restart nginx
service:
name: "{{ nginx_service_name }}"
state: restarted
become: yes
- name: reload nginx
service:
name: "{{ nginx_service_name }}"
state: reloaded
become: yes
4. Create Templates
In roles/nginx/templates/nginx.conf.j2
, create a Jinja2 template for the Nginx configuration:
user www-data;
worker_processes {{ nginx_worker_processes }};
pid /run/nginx.pid;
events {
worker_connections {{ nginx_worker_connections }};
# multi_accept on;
}
http {
# Basic Settings
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout {{ nginx_keepalive_timeout }};
types_hash_max_size 2048;
client_max_body_size {{ nginx_client_max_body_size }};
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Logging Settings
access_log {{ nginx_log_dir }}/access.log;
error_log {{ nginx_log_dir }}/error.log;
# Gzip Settings
gzip on;
gzip_disable "msie6";
# Virtual Host Configs
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
Also, create a default site template (roles/nginx/templates/default.conf.j2
):
server {
listen {{ nginx_http_port }} default_server;
listen [::]:{{ nginx_http_port }} default_server;
root /var/www/html;
index index.html index.htm;
server_name _;
location / {
try_files $uri $uri/ =404;
}
}
Creating Your Playbook
Now, let's create the main playbook to tie everything together. In playbook.yml
:
---
- name: Deploy Nginx to webservers
hosts: webservers
become: yes
roles:
- nginx
- name: Deploy Nginx as load balancer
hosts: loadbalancers
become: yes
vars:
nginx_default_site_template: loadbalancer.conf.j2
roles:
- nginx
tasks:
- name: Create load balancer configuration
template:
src: templates/loadbalancer.conf.j2
dest: "{{ nginx_conf_dir }}/sites-available/loadbalancer"
owner: root
group: root
mode: 0644
notify: restart nginx
- name: Enable load balancer configuration
file:
src: "{{ nginx_conf_dir }}/sites-available/loadbalancer"
dest: "{{ nginx_conf_dir }}/sites-enabled/loadbalancer"
state: link
notify: restart nginx
Create a load balancer configuration template (templates/loadbalancer.conf.j2
):
upstream backend {
{% for host in groups['webservers'] %}
server {{ hostvars[host]['ansible_host'] }}:{{ nginx_http_port }};
{% endfor %}
}
server {
listen {{ nginx_http_port }};
server_name _;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Running Your Ansible Deployment
Now you're ready to deploy Nginx using Ansible:
# Check syntax
ansible-playbook playbook.yml --syntax-check
# Dry run to see what changes would be made
ansible-playbook playbook.yml --check
# Run the actual deployment
ansible-playbook playbook.yml
Example Output
PLAY [Deploy Nginx to webservers] ********************************************
TASK [Gathering Facts] *******************************************************
ok: [web1]
ok: [web2]
TASK [nginx : Install Nginx package] *****************************************
changed: [web1]
changed: [web2]
TASK [nginx : Ensure Nginx configuration directories exist] ******************
ok: [web1] => (item=/etc/nginx)
ok: [web2] => (item=/etc/nginx)
...
TASK [nginx : Configure Nginx main configuration] ****************************
changed: [web1]
changed: [web2]
TASK [nginx : Configure default site] ****************************************
changed: [web1]
changed: [web2]
TASK [nginx : Enable default site] *******************************************
ok: [web1]
ok: [web2]
TASK [nginx : Ensure Nginx service is running and enabled] ******************
changed: [web1]
changed: [web2]
RUNNING HANDLER [nginx : restart nginx] *************************************
changed: [web1]
changed: [web2]
PLAY [Deploy Nginx as load balancer] ****************************************
...
PLAY RECAP *****************************************************************
web1 : ok=7 changed=5 unreachable=0 failed=0 skipped=0
web2 : ok=7 changed=5 unreachable=0 failed=0 skipped=0
lb1 : ok=9 changed=7 unreachable=0 failed=0 skipped=0
Advanced Configurations
Custom Nginx Configurations
To add custom Nginx configurations, you can create additional templates and tasks:
# In your playbook or role
- name: Configure custom Nginx settings
template:
src: custom_nginx.conf.j2
dest: "{{ nginx_conf_dir }}/conf.d/custom.conf"
owner: root
group: root
mode: 0644
notify: restart nginx
Securing Nginx with SSL/TLS
To enable HTTPS, create an SSL configuration template and adjust your role:
# In defaults/main.yml
nginx_enable_https: true
nginx_ssl_certificate: /etc/nginx/ssl/nginx.crt
nginx_ssl_certificate_key: /etc/nginx/ssl/nginx.key
# Add tasks to generate or copy SSL certificates
- name: Ensure SSL directory exists
file:
path: /etc/nginx/ssl
state: directory
owner: root
group: root
mode: 0700
when: nginx_enable_https
# Create a template for HTTPS configuration
Troubleshooting Common Issues
Nginx Service Won't Start
If Nginx fails to start, check:
- Configuration syntax:
ansible webservers -m command -a "nginx -t" -b
- Port conflicts:
ansible webservers -m command -a "netstat -tulpn | grep 80" -b
Permission Issues
If you encounter permission errors:
ansible webservers -m command -a "ls -la /var/log/nginx" -b
ansible webservers -m file -a "path=/var/log/nginx state=directory owner=www-data group=www-data recurse=yes" -b
Real-World Example: Multi-Environment Deployment
In real-world scenarios, you might need to deploy to different environments:
environments/
├── production/
│ ├── inventory.ini
│ └── group_vars/
│ ├── all.yml
│ ├── webservers.yml
│ └── loadbalancers.yml
├── staging/
│ ├── inventory.ini
│ └── group_vars/
│ ├── all.yml
│ ├── webservers.yml
│ └── loadbalancers.yml
Example deployment to staging:
ansible-playbook -i environments/staging/inventory.ini playbook.yml
Summary
In this guide, we've learned:
- How to structure an Ansible project for Nginx deployment
- Creating reusable Nginx roles with tasks, handlers, and templates
- Configuring Nginx for both web servers and load balancers
- Executing and troubleshooting Ansible deployments
- Advanced configuration options for real-world scenarios
By automating Nginx deployment with Ansible, you've gained the ability to:
- Deploy consistently across multiple servers
- Quickly update configurations when needed
- Document your infrastructure as code
- Scale your deployment process efficiently
Additional Resources
Further Learning
Exercises
- Basic Exercise: Modify the Nginx configuration to change the worker connections to 2048.
- Intermediate Exercise: Add a new virtual host for a specific domain with custom logging.
- Advanced Exercise: Configure Nginx with HTTPS using Let's Encrypt certificates.
Next Steps
Now that you understand Nginx deployment with Ansible, you can:
- Integrate this process into your CI/CD pipeline
- Explore Ansible Galaxy for more Nginx roles
- Create a more complex load balancing setup with SSL termination
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)