Post

Project: Ansible LEMP Stack Deployment

Project: Ansible LEMP Stack Deployment

Automated deployment of a full LEMP stack on RHEL 10 using Ansible roles. This is the first project in my infrastructure-as-code series — the goal is to go from a bare RHEL server to a fully running Nginx + MariaDB + PHP-FPM stack with a single command.

GitHub: biroue10/ansible-lemp-stack


Stack

ComponentPackageService
Web servernginxnginx
Databasemariadb-servermariadb
PHP runtimephp + php-fpmphp-fpm

Project structure

1
2
3
4
5
6
7
8
9
10
11
ansible-lemp-stack/
├── inventory/
│   └── hosts
├── roles/
│   ├── nginx/
│   │   └── tasks/main.yml
│   ├── mariadb/
│   │   └── tasks/main.yml
│   └── php/
│       └── tasks/main.yml
└── playbook.yml

Ansible roles keep each component isolated — nginx, mariadb and php each live in their own directory with their own tasks. The main playbook just assembles them:

1
2
3
4
5
6
7
- name: Deploy LEMP Stack
  hosts: lemp
  become: true
  roles:
    - nginx
    - mariadb
    - php

Key concept: idempotence

Running the playbook twice produces the same result as running it once. On the first run, Ansible installs and starts the services (changed=3). On every subsequent run, it checks the desired state against the current state and does nothing if they already match (changed=0).

1
2
3
4
5
6
7
# First run
PLAY RECAP
localhost : ok=7  changed=3  unreachable=0  failed=0

# Second run — nothing changed
PLAY RECAP
localhost : ok=7  changed=0  unreachable=0  failed=0

This is the foundation of infrastructure-as-code: the playbook describes what the system should look like, not what steps to execute.


Deploy in one command

1
2
3
git clone https://github.com/biroue10/ansible-lemp-stack.git
cd ansible-lemp-stack
ansible-playbook -i inventory/hosts playbook.yml --ask-become-pass

What’s next

This is the v1 skeleton. Upcoming iterations will add:

  • Variables — configurable ports, package versions, database names
  • Handlers — restart nginx only when the config actually changes
  • Templates (Jinja2) — generate nginx.conf from variables
  • Vault — encrypt database passwords at rest
This post is licensed under CC BY 4.0 by the author.