Deploying NGINX with Ansible

Deploy NGINX with Ansible

By David Mytton,
CEO & Founder of Server Density.

Published on the 16th February, 2016.

Last week we looked at how you can deploy NGINX with Puppet. This week, we’ll turn our attention to Ansible.

We will not spend time building an Ansible NGINX role from scratch. Instead we will endeavour to reuse existing modules.

For those of you familiar to the role/profiles design pattern used in Puppet, bear in mind that there is no such thing in Ansible. Ansible modules are roles. In this article, we will use those to install NGINX and PHP-FPM.

Inventories, Playbooks, Roles, and Tasks

The Ansible approach to automation is different to Puppet. Puppet cookbooks are closer to code (because they are written in Ruby DSL) while Ansible playbooks resemble shell scripts.

In Puppet, execution order is calculated during runtime (in order to satisfy defined states dependencies). Ansible, on the other hand, runs tasks in a sequential manner.

Tasks are all possible actions that Ansible can execute. Those could be specific commands, or higher level procedures, found in various core modules (for example: create a directory, install a package or write a configuration file).

As you probably know, Ansible runs through SSH and follows a push approach. Unlike other automation tools, it doesn’t rely on installed agents sitting on managed hosts.

If you need something similar to the management consoles available in Puppet, you should check out Ansible Tower.

With Ansible you don’t need tools like Hiera to separate code from configuration. In a similar fashion to Puppet, what you do is import variables from YAML files into playbooks.

The inventory holds a list of Ansible-managed servers. When working with cloud infrastructure (AWS, Google or Azure) this list is updated as and when servers are added or removed (dynamic inventory).

When it comes to 3rd party module repositories, Ansible Galaxy is the equivalent of Puppet Forge.

There are many Ansible roles you could use to install NGINX. These are our favorites:

  1. geerlingguy/ansible-role-nginx
  2. jdauphant/ansible-role-nginx
  3. debops/ansible-nginx

The first one is the most popular, in terms of downloads. We chose the second because its configuration is defined around NGINX URL locations (which is how we prefer to work). In any case, all three modules are actively maintained.

In terms of PHP, we decided to use debops/ansible-php5. We could have used geerlingguy/ansible-role-php but we found the former was easier to use. By the way, the DebOps project maintains 60+ Ansible roles and their documentation is awesome.


We assume Ansible is installed in your laptop. You should also have a server (virtual machine or container) to deploy NGINX and PHP.

Using Ansible to deploy NGINX

The following is an example deployment of NGINX (and PHP-FPM) on Ubuntu 14.04 or later, using the Ansible roles we discussed earlier.

The first step is to configure our Ansible environment using ansible.cfg. This file contains the locations of our inventory and roles:

File: ansible.cfg

hostfile = hosts
roles_path = galaxy:roles

The hosts file is our inventory. This defines the list of hosts, together with their group membership and login credentials. In my case, the hosts file looks like this:

File: hosts

[lemp] ansible_ssh_user=bencer

Then we need to download our 3rd party modules:

$ mkdir galaxy
$ ansible-galaxy install jdauphant.NGINX
$ ansible-galaxy install debops.php5

To save time, and avoid pain, you could also deploy server monitoring with your automation tool. Obviously, we chose Server Density (check out my fork of CorbanR’s Ansible role):

$ mkdir roles && cd roles
$ git clone
NGINX monitoring

Here is Server Density monitoring NGINX in a LEMP stack setup. By the way, if you are looking for a hosted monitoring that integrates with Ansible, you should sign up for a 2-week trial of Server Density.

The playbook file contains the roles for each group of our inventory. It also includes 3 configuration files, one for each role. This is how my lemp.yaml looks like:

File: lemp.yaml

- hosts: lemp
  sudo: yes

    - vars_nginx.yaml
    - vars_php.yaml
    - vars_sd.yaml

    - role: jdauphant.nginx
    - role: debops.php5
    - role: serverdensity

Let’s now set the specifics for each role. Here is our NGINX configuration:

File: vars_nginx.yaml

nginx_official_repo: True
keep_only_specified: True


    - listen *:80
    - server_name www.{{ vdomain }}
    - return 301 http://{{ vdomain }}$request_uri
    - listen *:80
    - server_name {{ vdomain }}
    - index index.html index.htm index.php
    - access_log /var/log/nginx/{{ vdomain }}.access.log combined
    - error_log /var/log/nginx/{{ vdomain }}.error.log
    - root /srv/www/{{ vdomain }}/
    - location /server-status {
            stub_status on;
            access_log false;
            deny all; }
    - location /php-status {
            fastcgi_param SCRIPT_FILENAME /srv/www/{{ vdomain }}$fastcgi_script_name;
            fastcgi_pass php;
            include fastcgi_params;
            access_log false;
            deny all; }
    - location / { try_files $uri $uri/ /index.php$is_args$args; }
    - location ~ .php$ { 
        try_files $uri /index.php =404;
        fastcgi_buffer_size 128k;
        fastcgi_buffers 4 256k;
        fastcgi_busy_buffers_size 256k;
        fastcgi_connect_timeout 60;
        fastcgi_ignore_client_abort off;
        fastcgi_index index.php;
        fastcgi_intercept_errors on;
        fastcgi_param CONTENT_LENGTH $content_length;
        fastcgi_param CONTENT_TYPE $content_type;
        fastcgi_param QUERY_STRING $query_string;
        fastcgi_param REQUEST_METHOD $request_method;
        fastcgi_param SCRIPT_FILENAME /srv/www/{{ vdomain }}$fastcgi_script_name;
        fastcgi_pass php;
        fastcgi_read_timeout 180;
        fastcgi_send_timeout 180;
        fastcgi_split_path_info ^(.+\.php)(.*)$;
        fastcgi_temp_file_write_size 256k;
        include fastcgi_params; }

    - upstream php { server unix:/var/run/php5-fpm.sock fail_timeout=10s; }

And here is our PHP-FPM configuration:

File: vars_php.yaml

  - php5-json
  - php5-curl
  - php5-mcrypt

php5_pools: [ '{{ php5_pool_default }}' ]
  enabled: True
  name: 'www-data'
  listen: '/var/run/php5-fpm.sock'
  pm_status: True
  pm_status_path: '/php-status'

And finally, here is our Server Density monitoring configuration, including the plugin configuration needed for monitoring NGINX and PHP:

File: vars_sd.yaml

sd_account: bencer
api_token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
backup_configcfg: no
delete_example: True
group_name: staging
log_level: DEBUG
  - nginx
  - phpfpm
      - nginx_status_url:
      - status_url:

We are now ready to run the Ansible playbook that deploys NGINX and PHP-FPM.

$ ansible-playbook lemp.yml

Summary and Further Reading

Ansible makes NGINX deployments easy. It’s also quite approachable, especially for sysadmins. In fact, it’s closer to shell scripts than any other automation tool.

Once we deploy NGINX, the most likely next step is to automate the deployment of our app. We liked lamosty’s WordPress example, even if it doesn’t leverage 3rd party modules. Ansible offers another great example for deploying WordPress. There are plenty of other examples too.

What about you? Are you using and sharing roles on Ansible Galaxy? Let us know in the comments.

You can also make pull requests against this GitHub repository. It contains the Ansible example we used in this article.

Everything we know about NGINX: the eBook

We’ve worked with NGINX for more than 7 years.
So we thought it was time we wrote a book about it. It’s packed with insights, tricks, and pretty much everything we've learned from using NGINX.

Fill in the form below to get your free copy!

Help us speak your language. What is your primary tech stack?

What infrastructure do you currently work with?

Articles you care about. Delivered.

Help us speak your language. What is your primary tech stack?

Maybe another time