Community Blog Preparing a LEMP Ubuntu 18 Server with WordPress using Ansible

Preparing a LEMP Ubuntu 18 Server with WordPress using Ansible

This blog post will cover parameterization and the use of the Ansible Galaxy to configure the servers by automation.

By Ankit Mehta, Alibaba Cloud Tech Share Author. Tech Share is Alibaba Cloud's incentive program to encourage the sharing of technical knowledge and best practices within the cloud community.

This blog post will cover parameterization and use of the Ansible Galaxy to configure the servers. If you are new to Ansible, read the previous blog post on how Ansible works and some basic configuration.

Parameterization is one of the most important segments of automation. Parameterization can help you reuse the script and file without rewriting the complete solution again.

People frequently ask, "I have only one VPS, will I get any benefits using Ansible?" the answer is yes, whether one VPS or multiple VPS servers. Ansible scripts can be used to manage all servers. This will not only retain the configuration but also help in configuring new servers, applying changes to the SSL, and also can help to configure and deploy websites on new cloud hosting provider.

For this blog post we will implement the following solution with Ansible where:


  1. Website Domain is using DNS service from Cloudflare
  2. Website is hosted on Alibaba Cloud Elastic Compute Service (ECS)
  3. The web server is configured with the following
    • Custom SSH port 1992
    • PHP version 7.2
    • MySQL
    • Fail2Ban
    • Swap size 1 GB
    • NginX WebServer
    • SSH for root is blocked
    • Firewall to allow only SSH , HTTP and HTTPS
    • SSL from CloudFlare

To achieve above, the blog is divided into two segments

  1. Prepare LEMP server on Ubuntu 18.04 (Linux, Nginx, MySQL, PHP)
  2. Prepare server for one domain with Cloudflare SSL

Part 1: Preparing LEMP Server on Ubuntu 18.04


Make sure that the server is ready and accessible by Ansible host. To configure Ansible host, refer to the Ansible Basics blog post.

Install NGINX

There are multiple Ansible Galaxy scripts available to install NGINX. However, from my own experience, it is better to use the APT to install Nginx. A complete list of Ansible Galaxy can be found on https://galaxy.ansible.com/

The following block will install the NGINX on the server and make sure that the services are enabled

- hosts: all
    - name: ensure nginx is at the latest version
      apt: name=nginx state=latest
      become: yes

    - name: start nginx
          name: nginx
          state: started
      become: yes

Save the above block as install-nginx.yml and run it with the following command.

ansible-playbook -i hosts.yml install-nginx.yml -b -v 

Install PHP

Different Ubuntu version comes with different PHP version support. During the time of this post PHP version 7.2

Ansible galaxy helps in reducing the tasks install the required dependencies quickly. "Ansible Galaxy refers to the Galaxy website where users can share roles, and to a command line tool for installing, creating and managing roles."

To install the PHP, two ansible galaxy roles are needed: PHP version and PHP (installation sequence can be of any order).

To install PHP version, enter the following command

ansible-galaxy install geerlingguy.php-versions 

To install PHP version, enter the following command

ansible-galaxy install geerlingguy.php 


Once the Ansible Galaxy roles are installed, prepare the installation yaml file. The Ansible roles require a variable file. For the demonstration purpose, the variable files are stored in vars/main.yml file. In this case, we added PHP version, PHP settings (settings for php.ini file) and required PHP extensions in the vars file.

The following are the contents of the main.yml file

#PHP version
php_version: '7.2'
php_enable_webserver: true
php_webserver_daemon: "nginx"
php_packages_state: "present"
php_install_recommends: true
php_executable: "php"
php_enable_php_fpm: true
php_fpm_listen: ""
php_fpm_listen_allowed_clients: ""
php_fpm_pm_max_children: 50
php_fpm_pm_start_servers: 5
php_fpm_pm_min_spare_servers: 5
php_fpm_pm_max_spare_servers: 5
php_use_managed_ini: true
php_fpm_pool_user: "www-data" # default varies by OS
php_fpm_pool_group: "www-data" # default varies by OS
php_memory_limit: "256M"
php_max_execution_time: "60"
php_max_input_time: "60"
php_max_input_vars: "1000"
php_realpath_cache_size: "32K"
php_file_uploads: "On"
php_upload_max_filesize: "64M"
php_max_file_uploads: "20"
php_post_max_size: "32M"
php_date_timezone: "Asia/Bangkok"
php_allow_url_fopen: "On"
php_sendmail_path: "/usr/sbin/sendmail -t -i"
php_output_buffering: "4096"
php_short_open_tag: false
php_error_reporting: "E_ALL & ~E_DEPRECATED & ~E_STRICT"
php_display_errors: "Off"
php_display_startup_errors: "On"
php_expose_php: "Off"
php_session_cookie_lifetime: 0
php_session_gc_probability: 1
php_session_gc_divisor: 1000
php_session_gc_maxlifetime: 1440
php_session_save_handler: files
php_opcache_zend_extension: "opcache.so"
php_opcache_enable: "1"
php_opcache_enable_cli: "0"
php_opcache_memory_consumption: "96"
php_opcache_interned_strings_buffer: "16"
php_opcache_max_accelerated_files: "4096"
php_opcache_max_wasted_percentage: "5"
php_opcache_validate_timestamps: "1"
php_opcache_revalidate_path: "0"
php_opcache_revalidate_freq: "2"
php_opcache_max_file_size: "0"
php_memory_limit: "128M"
php_max_execution_time: "90"
php_upload_max_filesize: "256M"
  - php
  - php-cli
  - php-common
  - php-devel
  - php-gd
  - php-mbstring
  - php-pdo
  - php-pecl-apcu
  - php-xml
  - php-fpm

The code snippet below is the installation yaml file (php-install.yml)

- hosts: all
    - vars/main.yml
    - geerlingguy.php-versions
    - geerlingguy.php

To install the php following command needs to be entered

ansible-playbook -i hosts.yml php-install.yml -b -v

Install MariaDB

Likewise many packages available to install MySQL/MariaDB. However, the best option is to use the direct install package. MariaDB / MySQL requires Python-py MySQL on the server. The following installation block will install dependencies and MySQL.

  - hosts: all
      - name: "Install Python MySQL Dependency"
          name: ['python-mysqldb',

      - name: select default locale
          name: locales
          question: locales/default_environment_locale
          value: en_US.UTF-8
          vtype: select

      - name: set timezone to Asia/Bangkok
          name: Asia/Bangkok

      - name: "Install Python MySQL Module"
          name: mysqlclient
          extra_args: -i https://pypi.python.org/pypi/
          executable: pip3

Save the above block as mysql-install.yml and run the following command to install.

ansible-playbook  -i hosts.yml mysql-install.yml -b -v

Configure Swap

Linux uses swap space to increase the amount of virtual memory available to a host. It can use one or more dedicated swap partitions or a swap file on a regular filesystem or logical volume. Ansible galaxy role will help to configure the swap space easily.

Install the swap ansible role with the following command

ansible-galaxy install geerlingguy.swap

Set the expected swap space and pressure details in vars/main.yml

swap_file_path: /swapfile
swap_file_size: '1024'
swap_swappiness: 60
swap_file_state: present
swap_file_create_command: "dd if=/dev/zero of={{ swap_file_path }} bs=1M count={{ swap_file_size }}"

Save the following code block as swap-install.yml

- hosts: all
    - vars/main.yml
    - geerlingguy.swap

To install the swap run the following command

ansible-playbook -i hosts.yml swap-install.yml -b -v

Update the parameter swap_file_size if more swap file size is needed. With current configuration it will create a swap file partition of 1GB

Configure Security

Web server security is the most integral part of any server (ECS) configuration. To improve the security it is recommended to disable the root login, disable the password login, change the SSH port and activate the fail2ban. (Please note that there are many more features and services to improve the Linux server security.)

To install the Linux security ansible role run the following command

ansible-galaxy install geerlingguy.security

Add following variables to vars/main.yml

# Security Settings
security_ssh_port: 65239
security_ssh_password_authentication: "no"
security_ssh_permit_root_login: "no"
security_ssh_usedns: "no"
security_autoupdate_enabled: true
security_fail2ban_enabled: true

Save the following code block as install-security.yml

- hosts: all
    - vars/main.yml
    - geerlingguy.security

To install the security run,

ansible-galaxy -i hosts.yml install-security.yml -b -v

For the example above, the SSH port will be changed to 65239 after the successful run of the test.

Configure Firewall

By applying the firewall configuration the server can be prevented from known network attacks. As the application is going to use the port HTTP, HTTPS, and SSH, it is recommended to block access to all other ports. To install the firewall Ansible galaxy role, run the following command.

ansible-galaxy install geerlingguy.firewall

Provide the port details in vars/main.yml file

#Firewall Settings
firewall_state: started
firewall_enabled_at_boot: true
  - "443"
  - "65239"
  - "80"

Save the following code block as install-firewall.yml

- hosts: all
    - vars/main.yml
    - geerlingguy.firewall

To apply the firewall changes run the following command

ansible-playbook -i hosts.yml install-firewall.yml -b -v

Note: Make sure to apply the firewall changes for the ECS instance as well. To apply the changes navigate to ECS > Security Group > Add Rule


Sample LEMP installation script can be found at https://github.com/ankyit/ansible-lemp-wordpress/tree/master/LEMP

Part 2: Prepare Server for One Domain with Cloudflare SSL


  1. A LEMP server
  2. A domain name with DNS on Cloudflare
  3. An SSL certificate
  4. Website with Database (For this case WordPress demo web)

In the segment above, we discussed in detail regarding the preparation of the LEMP server. This segment will help in installing a WordPress website using Ansible.

Following is the high level structure for the site deployment configuration. Note that the format can be defined and configured as per the needs.


Here, the certs directory will contain the SSL certificate and Key file. The sites-config folder will contain NGINX configuration. The sites will contain the files and folder structure of the website. The vars folder will contain the site variables.

The hosts.yml file will contain the host information and deploy-site.yml will contain the steps/tasks for the site deployment.

The deploy-site.yml file extends the functionalities covered in the blog posts.

Tasks from deploy-site.yml

The following block will copy the certificate and key file from the certs folder to /etc/nginx/ssl folder. Here {{site_url}} is a dynamic parameter and it's values are replaced on the fly from vars/main.yml

### {{site_url}} SSL Copy
    - name: copy {{site_url}} SSL files
        src: ./certs/{{site_url}}.csr
        dest: /etc/nginx/ssl/{{site_url}}.csr
      become: yes

    - name: copy {{site_url}} SSL key files
        src: ./certs/{{site_url}}.key
        dest: /etc/nginx/ssl/{{site_url}}.key
      become: yes

The following code block will copy the NGINX configuration file for the website and will be stored in /etc/nginx/sites-available

### {{site_url}} Nginx configuration
    - name: copy the nginx config file
        src: ./site-config/{{site_url}}.cfg
        dest: /etc/nginx/sites-available/{{site_url}}.cfg
      become: yes

The following code block will create symbolic link for the URL from sites-available to sites-enabled.

    - name: create symlink
        src: /etc/nginx/sites-available/{{site_url}}.cfg
        dest: /etc/nginx/sites-enabled/{{site_url}}.cfg
        state: link
      become: yes

The following code block will create a website folder on /var/www/ location

###  Create a directory for the website
    - file:
        path: /var/www/{{site_url}}
        state: directory
        mode: 0755

The following code block will copy the files from sites folder to /var/www/ using rsync. Here the delete yes will delete the files at destination location if the files are deleted from the source location.

##{{site_url}} Website Copy
    - name: copy the content of the website
        src: ./sites/
        dest: /var/www/{{site_url}}
        delete: yes

The following code block will create a database.

###{{site_url}} Database Creation
    - name: Create a {{site_url}} database
        name: "{{site_db}}"
        state: present

The following code block will create a user and assign the DB privileges.

    - name: "Create a new user for {{site_url}}"
        name: "{{site_db_user}}"
        password: "{{site_db_password}}"
        priv: '{{site_db}}.*:ALL'
        state: present

The following code block will import the database

# ###Import database
    - name: import the database
        name: "{{site_db}}"
        state: import
        target: /var/www/{{site_url}}/{{db_file}}
        login_user: "{{site_db_user}}"
        login_password: "{{site_db_password}}"

The following code block will set the directory permission and set it to www-data with 0755 rights.

###Set Directory permissions
    - name: Set Directory permissions
      file: path=/var/www/ owner=www-data group=www-data mode=0755 state=directory recurse=yes

The following code block will restart NGINX and PHP-FPM

## Restart NginX and PHP-fpm
    - name: restart nginx
        name: nginx
        state: restarted
      become: yes

    - name: restart PHP FPM
        name: php7.2-fpm
        state: restarted
      become: yes

Note: All the scripts discussed here can be accessed from https://github.com/ankyit/ansible-lemp-wordpress

0 0 0
Share on

Alibaba Clouder

2,600 posts | 754 followers

You may also like