Buy

Nginx Configuration & Ansible Templates

Nginx is still using its generic, boring, but very polite default HTML page:

You're welcome for using Nginx!

It's time to point Nginx at our code! Google for "Symfony Nginx" to find a documentation page about Configuring a Web Server.

Scroll down to the Nginx section: it has a great block of Nginx config that'll work with our app. Here's the trick: we somehow need to put this configuration up onto the server and customize it. How can we do that?

Ansible does have a module for copying files from your local machine to the remote host. But in this case, there's a different module that's even more perfect: the template module. It says:

Templates a file out to a remote server

In normal-person English, this means that it executes a file through Jinja - allowing us to print dynamic variables - and then copies it up to the remote server. I love this module.

Create a templates/ directory and inside, a symfony.conf file. Paste in the raw Nginx configuration:

55 lines ansible/templates/symfony.conf
server {
server_name domain.tld www.domain.tld;
root /var/www/project/web;
location / {
# try to serve file directly, fallback to app.php
try_files $uri /app.php$is_args$args;
}
# DEV
# This rule should only be placed on your development environment
# In production, don't include this and don't deploy app_dev.php or config.php
location ~ ^/(app_dev|config)\.php(/|$) {
fastcgi_pass unix:/var/run/php/php7.1-fpm.sock;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
# When you are using symlinks to link the document root to the
# current version of your application, you should pass the real
# application path instead of the path to the symlink to PHP
# FPM.
# Otherwise, PHP's OPcache may not properly detect changes to
# your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126
# for more information).
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
}
# PROD
location ~ ^/app\.php(/|$) {
fastcgi_pass unix:/var/run/php/php7.1-fpm.sock;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
# When you are using symlinks to link the document root to the
# current version of your application, you should pass the real
# application path instead of the path to the symlink to PHP
# FPM.
# Otherwise, PHP's OPcache may not properly detect changes to
# your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126
# for more information).
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
# Prevents URIs that include the front controller. This will 404:
# http://domain.tld/app.php/some-path
# Remove the internal directive to allow URIs like this
internal;
}
# return 404 for all other php files not matching the front controller
# this prevents access to other php files you don't want to be accessible.
location ~ \.php$ {
return 404;
}
error_log /var/log/nginx/project_error.log;
access_log /var/log/nginx/project_access.log;
}

Now, a few things need to change in here. Like, server_name should be mootube.l and root should point to the correct directory. Actually, root is already correct by chance... but we can do better!

Variables in your Template

All variables available in your playbook are available in a template. So at the top of our playbook, let's add a few more: server_name: mootube.l and symfony_web_dir set to {{ symfony_root_dir }}/web:

137 lines ansible/playbook.yml
---
- hosts: vb
vars:
server_name: mootube.l
symfony_root_dir: /var/www/project
symfony_web_dir: "{{ symfony_root_dir }}/web"
... lines 8 - 137

The web directory is our document root.

Let's put those variables to work! First, use {{ server_name }} and then set the root to {{ symfony_web_dir }}:

55 lines ansible/templates/symfony.conf
server {
server_name {{ server_name }};
root {{ symfony_web_dir }};
... lines 4 - 53
}

At the bottom, tweak the logs paths - instead of the word project, use {{ server_name }}. Do it for the access_log too:

55 lines ansible/templates/symfony.conf
server {
... lines 2 - 51
error_log /var/log/nginx/{{ server_name }}_error.log;
access_log /var/log/nginx/{{ server_name }}_access.log;
}

Oh, and while we're here, see those php5-fpm references? Change them to php7.1-fpm.sock in both places. FPM will already be configured to put its sock file here:

55 lines ansible/templates/symfony.conf
server {
... lines 2 - 11
location ~ ^/(app_dev|config)\.php(/|$) {
fastcgi_pass unix:/var/run/php/php7.1-fpm.sock;
... lines 14 - 24
}
# PROD
location ~ ^/app\.php(/|$) {
fastcgi_pass unix:/var/run/php/php7.1-fpm.sock;
... lines 29 - 43
}
... lines 45 - 53
}

Using the template Module

Let's add our task to use the template: "Add Symfony config template to the Nginx available sites". Add become: true and use the template module:

137 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 8
tasks:
... lines 10 - 46
- name: Add Symfony config template to the Nginx available sites
become: true
template:
... lines 50 - 137

The two important options are dest and src. Set src to, well templates/symfony.conf, and dest to /etc/nginx/sites-available/{{ server_name }}.conf:

137 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 8
tasks:
... lines 10 - 46
- name: Add Symfony config template to the Nginx available sites
become: true
template:
src: templates/symfony.conf
dest: "/etc/nginx/sites-available/{{ server_name }}.conf"
... lines 52 - 137

Enabling the Nginx Site

Cool! Once it's in that directory, we need to enable it... which means we need to create a symbolic link from sites-enabled to that file in sites-available. And we already know the perfect module for this: file!

Add the new task: "Enable Symfony config template from Nginx available sites". We still need become: true and use the file module:

137 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 8
tasks:
... lines 10 - 46
- name: Add Symfony config template to the Nginx available sites
... lines 48 - 52
- name: Enable Symfony config template from Nginx available sites
become: true
file:
... lines 56 - 137

This time, for src, copy the sites-available line from dest above. For dest, just change it to sites-enabled:

137 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 8
tasks:
... lines 10 - 46
- name: Add Symfony config template to the Nginx available sites
... lines 48 - 52
- name: Enable Symfony config template from Nginx available sites
become: true
file:
src: "/etc/nginx/sites-available/{{ server_name }}.conf"
dest: "/etc/nginx/sites-enabled/{{ server_name }}.conf"
state: link
... lines 59 - 137

To create the symbolic link, use state: link. Earlier we created a directory with state: directory.

Updating /etc/hosts

The last thing I'll do - which is optional - is to add a task named "Add enabled Nginx site /etc/hosts". I'll use lineinfile to modify /etc/hosts and look for a line that contains {{ server_name }}. Then, I'll replace it with 127.0.0.1 {{ server_name }}:

137 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 8
tasks:
... lines 10 - 52
- name: Enable Symfony config template from Nginx available sites
... lines 54 - 59
- name: Add enabled Nginx site to /etc/hosts
become: true
lineinfile:
dest: /etc/hosts
regexp: "{{ server_name }}"
line: "127.0.0.1 {{ server_name }}"
... lines 66 - 137

This will guarantee that we have a /etc/hosts entry for mootube.l that points to 127.0.0.1 inside the VM. It's not a big deal - but it'll let us refer to mootube.l in the VM.

Of course, the first time this runs, it will not find a line in that file that matches mootube.l. But that's no problem: lineinfile will guarantee that the line value exists by adding it at the bottom.

Before you try this, make sure you have sites-available and sites-enabled:

137 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 8
tasks:
... lines 10 - 46
- name: Add Symfony config template to the Nginx available sites
... line 48
template:
... line 50
dest: "/etc/nginx/sites-available/{{ server_name }}.conf"
... line 52
- name: Enable Symfony config template from Nginx available sites
... line 54
file:
src: "/etc/nginx/sites-available/{{ server_name }}.conf"
dest: "/etc/nginx/sites-enabled/{{ server_name }}.conf"
... lines 58 - 137

Ok, run it!

ansible-playbook ansible/playbook.yml -i ansible/hosts.ini

Woohoo! No errors! Let's check it out! First, the file does live in sites-available/. And yea, awesome! The variables inside worked. Our sites-enabled directory does have the link, and /etc/hosts has our new mootube.l line at the bottom:

# /etc/hosts
# ...
127.0.0.1 mootube.l

Wow, we just killed it!

So, in theory, we should be able to go to our browser and refresh http://mootube.l to see our site. Refresh! Um.... we still see the same, boring - but polite - Nginx Configuration page.

Why? Some of you are probably screaming the answer at your computer... and also scaring your co-workers. It's because, we have not restarted or reloaded Nginx. Yea, this is easy to do manually - but come on! We need to teach our playbook to know when Nginx - and also PHP FPM - need to be restarted. We can do that with... handlers!

Leave a comment!