Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesThe reliable way to automate a LEMP server is to let Ansible configure an Ubuntu host from a control node, with separate roles for operating-system preparation, NGINX, PHP-FPM and MySQL-compatible database software. This guide builds that arrangement with an inventory, variables, Jinja templates, handlers and validation steps so the same playbook can be safely rerun.
Antihost offers Linux VPS plans with full root access, unlimited traffic on a 1 Gbps port and a 14-day free trial.
What this playbook builds
LEMP means Linux, NGINX, a MySQL-compatible database and PHP. Ubuntu supplies the Linux operating system; NGINX accepts HTTP requests; PHP-FPM executes PHP code; and MySQL or MariaDB stores application data. Ansible does not replace any of those services. It describes their desired state and applies that state repeatedly.
As an Amazon Associate I earn from qualifying purchases.
Free tools Windows power users keep installed
One-click scans. No signup required.
| Layer | Typical Ubuntu components | What the automation controls |
|---|---|---|
| Linux | Ubuntu host, users, packages and firewall policy | Prerequisites, package updates, service accounts and baseline settings |
| Web server | NGINX | Package installation, enabled service, server blocks, document roots and reloads |
| Application runtime | PHP and PHP-FPM | Runtime packages, pool settings, socket path and test page |
| Database | MySQL or MariaDB | Server package, service state, databases, users and privileges |
Package names and PHP-FPM socket paths differ between Ubuntu releases. The playbook therefore keeps release-sensitive values in variables instead of copying package lists from an older tutorial. A DigitalOcean walkthrough published in 2020 targeted Ubuntu 18.04; its defaults should be treated as historical examples, not as instructions for a current release.
Choose the Ansible topology first
Control node
Ansible is an agentless automation tool installed on one machine called the control node. This can be your workstation, a CI runner or a dedicated administration host. The control node stores the inventory, playbooks, roles and encrypted secrets, and opens SSH connections to managed hosts.
#1 Best Overall
- [Ultra-Lightweight Ear Buds Designed for Small Ears] Each earbud weighs only 3.7g and features a compact, ergonomic in-ear design made especially for small ears. Secure, low-profile, and comfortable for workouts, all-day wear or overnight sleeping.The ultra-slim profile sits flush in your ear with no hard edges poking or rubbing, so you can even sleep on your side without discomfort.
- [Immersive Stereo Sound with TOZO OrigX Technology] TOZO OrigX tuning delivers clear vocals, balanced mids, and natural stereo sound for music, podcasts, and videos.
- [Long Battery Life for Daily Use] Get up to 7 hours of playtime on a single charge, with up to 32 hours total using the charging case—ideal for workdays, commuting, and extended listening sessions.
- [Bluetooth 5.3 & Stable Connection] Bluetooth 5.3 provides fast pairing, stable wireless performance, and reduced dropouts as you move around home or office.
- [Deep Bass with Clear Vocals] High-performance drivers produce punchy bass while keeping vocals clean and detailed for everyday listening.
Managed node
The Ubuntu server being configured is the managed node. It does not need Ansible installed. It does need Python so Ansible can run its generated Python code, an account with an interactive POSIX shell, and SSH access from the control node. The account must be able to obtain the privileges required to install packages and manage services, normally through sudo.
One host or separate hosts
A single Ubuntu host is simple and suits a small application or a learning environment. Separate web and database hosts provide isolation and make independent scaling possible, but require more inventory groups, network rules and operational work. The roles below support either arrangement: place the same host in all groups for a single-server deployment, or assign different hosts to the web, PHP and database groups.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallInstall Ansible deliberately
The official installation guidance distinguishes the full ansible package from the smaller ansible-core package. The full package is convenient when you want a broad set of community collections; ansible-core is a smaller foundation when you intend to select collections explicitly. Record the choice in project documentation and do not assume that a version shown in an old Ubuntu 18.04 tutorial applies to your target release.
| Choice | Use it when | Trade-off |
|---|---|---|
ansible |
You want the maintained community package and commonly used collections available together. | Larger installation and more bundled content. |
ansible-core |
You want a minimal control node and are prepared to declare collections yourself. | More explicit dependency management. |
Install the selected package using the current Ansible installation method for your control-node operating system. The official installer documents pipx, which keeps the Ansible command-line tools isolated from the system Python environment. After installation, verify both the executable and its version:
ansible --version
For a reproducible project, create a requirements.yml for collections and a documented Python or package-management procedure for the control node. Install collection dependencies before running the playbook. If you use a maintained NGINX role rather than handwritten tasks, follow that role’s documented platform and collection requirements and pin a tested role version.
Create a maintainable project layout
Separate responsibilities into roles. A practical layout is:
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →lemp-ansible/
├── ansible.cfg
├── inventory/
│ └── hosts.yml
├── group_vars/
│ ├── all.yml
│ └── vault.yml
├── requirements.yml
├── site.yml
└── roles/
├── common/
│ ├── defaults/main.yml
│ └── tasks/main.yml
├── nginx/
│ ├── defaults/main.yml
│ ├── handlers/main.yml
│ ├── tasks/main.yml
│ └── templates/site.conf.j2
├── php/
│ ├── defaults/main.yml
│ ├── handlers/main.yml
│ ├── tasks/main.yml
│ └── templates/php.ini.j2
└── database/
├── defaults/main.yml
├── tasks/main.yml
└── templates/my.cnf.j2
Defaults provide safe, overridable values. Group variables describe a host group, while encrypted variables hold passwords. Templates render configuration files, and handlers restart or reload services only when a task reports a change. This structure follows Ansible’s documented pattern of roles, templates, group variables and handlers and is easier to test than one large task file.
Define inventory, SSH and variables
Start with an inventory that names the connection address and groups. The example below uses one server in every group; replace the address, user and key path with your values.
all:
children:
web:
hosts:
app1:
ansible_host: 203.0.113.20
php:
hosts:
app1:
database:
hosts:
app1:
Put connection defaults in group_vars/all.yml or in the inventory:
Rank #2
- 【IP68 Waterproof Multifunctinal Smart Watch】Torjalph fitness activity tracker smart watch is IP68 waterproof technology ,you can wear it when swimming,handwashing,it's rain-proof which means that you perform multiple exercise modes outside without worrying about sweat or rain corroding your fitness smart watch,and this watch equipped with a 230 mAh large capacity battery magnetic charging ,and this sports activity tracker watch can be used 5-7 days If fully charged.
- 【Larger 1.83'' Full Screen Watch & Multi-dial Selection】1.83 inch full HD color touch screen smart watch for men / women provides excellent 240*284 resolution image quality ,This Bluetooth smart watch offers a better touch experience and remarkable immersion and ,more than 200 watch faces for you to choose from App"Da Fit ", you can change the dial style according to your mood to wear keeping the world on your wrist
- 【Accurate Activity Tracker Smart Watch 】sports watch activity tracker uses sweat and dust resistant breathable silicone strap, our Fitness Tracker Smart Watch for Android iOS Phones supports 12 sports modes,walking,running,bicycle,swimming,badminton,basketball,football,rope skipping. This bluetooth calls smart watch for men women is lightweight and stylish, bringing more convenient and relaxing sports experience while also providing more protection.
- 【Your 24 Hour Personal Daily Health Monitor】Fitness watch for women men full touch screen IP67 waterproof bluetooth smartwatch for Android iOS Phones with heart rate blood pressure monitor multifunction Andriod smart watch with steps counter sleep monitor sports fitness watch, monitoring blood oxygen saturation allows you to understand your physical condition in real time to track fitness progress ,to know your health status in time.
- 【More Functions Bluetooth Call Smart Watch】This fitness activity smart watch full screen for iPhone and Android phones includes a bunch of new features including:The bluetooth smart watch can hang up the phone, receive and display WhatsAPP /Text /Facebook /Twitter /Messenger reminder.Support sedentary reminder, alarm clock, stopwatch, timer,weather information,music control, remote camera control compatible with Android 5.0 and above or iOS 9.0 and above smartphones.
ansible_user: deploy
ansible_become: true
ansible_python_interpreter: /usr/bin/python3
If the SSH account uses a non-default key, pass it with --private-key or configure it in ansible.cfg. Test the connection before changing packages:
ansible all -i inventory/hosts.yml -m ansible.builtin.ping
ansible all -i inventory/hosts.yml -m ansible.builtin.setup -a 'filter=ansible_python*'
A successful ping confirms SSH and module execution. The facts command confirms that Python is usable. Fix DNS, firewall rules, SSH keys, sudo policy or Python installation before proceeding.
Keep release-sensitive values in variables. For example:
nginx_server_name: example.test
web_root: /var/www/example
php_packages:
- php-fpm
- php-mysql
- php-cli
database_packages:
- mysql-server
database_service: mysql
php_fpm_service: php8.3-fpm
php_fpm_socket: /run/php/php8.3-fpm.sock
app_database: exampledb
app_database_user: exampleapp
The PHP service, socket and package names above are examples only. Check which names exist in the Ubuntu release you selected and override them in variables. Do not silently carry an 8.3 value to a release that ships another PHP version.
Prepare the operating system role
The common role should do only host-wide preparation. Keep application configuration in the service roles.
- name: Install baseline packages
ansible.builtin.apt:
name:
- ca-certificates
- curl
- python3
- python3-pymysql
state: present
update_cache: true
cache_valid_time: 3600
- name: Ensure the web root exists
ansible.builtin.file:
path: '{{ web_root }}'
state: directory
owner: www-data
group: www-data
mode: '0755'
Some Ubuntu repositories call the database driver package python3-pymysql. The exact package name is important because Ansible’s MySQL modules need a Python PyMySQL library on the managed node. If your target release uses a different package name, set that value in the common role and verify it before database tasks run.
Install and configure NGINX
Package and service tasks
The NGINX role installs the package, enables it at boot and starts it. Keep the service name in a variable if a distribution-specific package changes it.
- name: Install NGINX
ansible.builtin.apt:
name: nginx
state: present
update_cache: true
- name: Enable and start NGINX
ansible.builtin.service:
name: nginx
enabled: true
state: started
Render a server block
Use a template rather than a long inline command. This server block serves static files and forwards PHP requests to the selected PHP-FPM Unix socket:
server {
listen 80;
listen [::]:80;
server_name {{ nginx_server_name }};
root {{ web_root }};
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ .php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:{{ php_fpm_socket }};
}
location ~ /.ht {
deny all;
}
}
Deploy the file and enable it with a symlink. Remove the distribution’s default site only if you have confirmed that no other application needs it.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Rank #3
- [Low Noise High Fidelity] New bee guitar cord with 22AWG oxygen-free-copper center conductors provides enhanced signal transfer, the PVC insulation material and the high-density stranded copper double shield provide noise rejection for quiet operation
- [Ideal for Choice] New bee 1/4 straight and right angle metal connectors, ensures a secure connection in any instrument, pedal, or amp jack. L shape great fro on stage guitar stands
- [Wide Compatible] Suitable for mixer, power amplifier, microphone, effector, decoder, equalizer, electoronic organ, electric guitar, electric bass and other electronic equipments. Widely used in the installation of smart home, karaoke speaker, professional stage audio, etc
- [Easy Disassemble] Making incidental repairs quick and easy! Very nice, flexible cable
- [Dont Hesite to Order] 24 hours customer services and professional technology team are standing by
- name: Render the application server block
ansible.builtin.template:
src: site.conf.j2
dest: /etc/nginx/sites-available/example
owner: root
group: root
mode: '0644'
notify: Validate and reload NGINX
- name: Enable the application server block
ansible.builtin.file:
src: /etc/nginx/sites-available/example
dest: /etc/nginx/sites-enabled/example
state: link
notify: Validate and reload NGINX
Validate before reloading
Never reload NGINX with an unvalidated template. The handler can run the syntax check and reload only when the check succeeds:
- name: Validate and reload NGINX
listen: Validate and reload NGINX
block:
- name: Check NGINX configuration
ansible.builtin.command: nginx -t
changed_when: false
- name: Reload NGINX
ansible.builtin.service:
name: nginx
state: reloaded
A maintained NGINX role is an alternative to handwritten tasks. It can reduce boilerplate and provides documented platform support, collection requirements and lint/test practices. Handwritten roles give you tighter control over file names and policy. Choose one approach per project and pin external role or collection versions.
Install PHP and PHP-FPM
LEMP requires PHP-FPM because NGINX does not execute PHP itself. Select packages available in the target Ubuntu repository, then expose the chosen service and socket as variables. A common baseline includes the FPM runtime, command-line PHP and the MySQL driver:
- name: Install PHP packages
ansible.builtin.apt:
name: '{{ php_packages }}'
state: present
update_cache: true
- name: Enable and start PHP-FPM
ansible.builtin.service:
name: '{{ php_fpm_service }}'
enabled: true
state: started
Do not hard-code a PHP version in the role when the same code must support multiple Ubuntu releases. Derive the values from inventory, or maintain separate variable files for each supported release.
Render runtime settings
Keep pool and runtime settings in a template. Typical settings include the execution user, group, listen socket, memory limit and upload limit. The exact file path differs by PHP version, so make it a variable:
php_fpm_config_path: /etc/php/8.3/fpm/pool.d/www.conf
php_ini_path: /etc/php/8.3/fpm/php.ini
- name: Render PHP settings
ansible.builtin.template:
src: php.ini.j2
dest: '{{ php_ini_path }}'
owner: root
group: root
mode: '0644'
notify: Restart PHP-FPM
Use a handler for restarts, not a restart task that runs on every playbook execution:
- name: Restart PHP-FPM
ansible.builtin.service:
name: '{{ php_fpm_service }}'
state: restarted
Test the complete request path
Create a temporary test file only on a non-production host, or use an application health endpoint:
- name: Install a PHP test page
ansible.builtin.copy:
content: '<?php echo "LEMP OKn";'
dest: '{{ web_root }}/health.php'
owner: www-data
group: www-data
mode: '0644'
Request the page through NGINX with a browser or curl. A downloaded PHP source file indicates that the PHP location or FPM socket is wrong. A 502 response usually means the FPM service is stopped, the socket path does not exist, or its permissions prevent NGINX from connecting.
Recommended Free Tools
Install and secure MySQL-compatible database software
Choose MySQL or MariaDB per release policy
Ubuntu repositories may offer MySQL, MariaDB or both. Select the implementation that matches your application’s compatibility requirements, repository policy and operational standards. The Ansible database modules can manage either when the corresponding Python driver and server behavior are supported. Do not mix package names and service names from different implementations without checking the target release.
Rank #4
- Digital Family Calendar: BSIMB digital calendar wall touch screen puts an end to your chaotic life. Designed for busy families, the digital calendar keeps you organised and in step with your family. Assign different colours and tasks to each family member to make family scheduling easier and more intuitive.
- Easy Setup and Auto-Sync: Plug in, connect to WLAN and auto-syn your electric calendar in minutes. Digital wall calendar compatible with Google calendar, iCloud calendar, Outlook calendar, Cozi calendar and Yahoo calendar. Add events directly on the device or through the ecalendar app. Electronic calendar stays current whether you're at work or traveling—no manual updates needed.
- All-In-One Home Assistant: Smart calendar builds habits and encourage independence with personalised chores. Display daily meal plan to reduce the daily ‘What's for dinner?’ stress. To-do lists keep track of everything you want. Turning chores into valuable achievements with stars and rewards, making life easier and more enjoyable.
- Manage Anytime Anywhere: BSIMB electronic calendar for family stay connected with the powerful ecalendar app. Access and manage your digital wall calendar anytime, anywhere with free features like stars & rewards, photo screensaver, chore charts, meals plans, to-do lists etc. Electronic planner paid features like Magic Import tailor advanced tools for users with special needs.
- Stay in Touch with Loved Ones: Share photos or videos from your phone to electronic wall calendar with app, instantly update your life moment, and keep in touch with your family, especially those who are far away; you can also surprise your smart calendar digital wall by sending him a wish card; it's also great for keeping tech-illiterate elders in touch with the youngsters.
| Decision | Questions to answer |
|---|---|
| Repository availability | Which server and client packages are present in the selected Ubuntu release and enabled repositories? |
| Application compatibility | Does the application require MySQL-specific behavior, MariaDB features or a particular major version? |
| Operations | Which service name, backup process, authentication policy and upgrade path does your team support? |
| Ansible support | Can the selected database modules authenticate with the installed server and PyMySQL library? |
Install the server and driver
- name: Install database packages
ansible.builtin.apt:
name: '{{ database_packages }}'
state: present
update_cache: true
- name: Enable and start the database
ansible.builtin.service:
name: '{{ database_service }}'
enabled: true
state: started
Install the Python 3 PyMySQL package before invoking database modules. The managed node, not only the control node, needs that library.
Create a database and least-privilege account
Store the application password in an encrypted variable file. Do not commit a plaintext password to inventory or a role default.
- name: Create application database
community.mysql.mysql_db:
name: '{{ app_database }}'
state: present
login_unix_socket: '{{ database_socket }}'
- name: Create application database user
community.mysql.mysql_user:
name: '{{ app_database_user }}'
password: '{{ app_database_password }}'
priv: '{{ app_database }}.*:ALL'
host: localhost
state: present
login_unix_socket: '{{ database_socket }}'
no_log: true
The module collection and authentication parameters must match the selected server. On some installations, socket authentication for the local administrative account is the simplest initial method; on others, a dedicated administrative login is required. Test the connection method on a disposable host before applying it to production.
Apply security changes carefully
Automate only security actions you understand for the chosen implementation: remove anonymous accounts, remove test databases, restrict remote administrative access and enforce a deliberate bind address. A blanket hardening task copied from another distribution can break replication, local sockets or an existing application. Keep destructive cleanup tasks explicit and review them before the first run.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Assemble the site playbook
The top-level playbook assigns roles to groups. This example runs all roles on one host while retaining group separation for future growth:
---
- name: Prepare Ubuntu hosts
hosts: all
become: true
roles:
- common
- name: Configure web tier
hosts: web
become: true
roles:
- php
- nginx
- name: Configure database tier
hosts: database
become: true
roles:
- database
If PHP must be installed before NGINX renders a socket path, keep the role order shown. For separate hosts, define the database host address and application credentials in group variables, then permit only the web tier to reach the database port through the host firewall or cloud network policy.
Run safely and verify every layer
- Check syntax: run
ansible-playbook site.yml --syntax-check -i inventory/hosts.yml. - Inspect the plan: use
--check --diffwhere modules support check mode. Review package removals, template changes and privilege operations. - Apply to a disposable host: run
ansible-playbook site.yml -i inventory/hosts.ymlwith the SSH key and become settings required by your environment. - Confirm service state: use Ansible service or command modules to verify NGINX, PHP-FPM and the selected database service are enabled and running.
- Validate NGINX: execute
nginx -tand request the configured server name. - Validate PHP: request the health endpoint through NGINX and confirm that PHP executes rather than being downloaded.
- Validate the database path: connect with the application account, create a test table in a temporary database if appropriate, and confirm that PHP can authenticate using the same credentials.
- Rerun the playbook: a second run should report no changes except intentionally volatile facts. Unexpected changes indicate a non-idempotent task, unstable template input or a service modifying a file that Ansible owns.
Lint YAML and roles before merging changes. The maintained NGINX role documentation treats linting and automated testing as part of role maintenance; adopt the same discipline for local roles. Test against each Ubuntu release you claim to support rather than assuming package names and defaults are interchangeable.
Common failures and targeted fixes
SSH works manually but Ansible cannot connect
Check the inventory hostname, SSH user, key path and sudo policy. Run Ansible with increased verbosity only while troubleshooting, and verify that the configured user has an interactive shell. A server that permits SFTP but not a normal shell will not satisfy Ansible’s managed-node requirement.
Python interpreter errors
Install Python 3 on the managed node or set ansible_python_interpreter to the correct path. Do not install Ansible on every server merely to solve an interpreter error; the managed node needs Python, not the Ansible package.
Best Value
Package not found
Confirm the Ubuntu release and enabled repositories. Replace historical package names with the names available on that release, then store those names in variables. Avoid adding an unrelated third-party repository just to preserve an old tutorial’s version unless your patching and trust policy allows it.
NGINX reload fails
Read the output of nginx -t. Common causes are a typo in a template, a duplicate listen directive, an invalid server name, a missing include or a PHP socket path copied from another PHP version. Because validation is in the handler, a failed check should leave the running configuration in place.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →PHP returns 502 Bad Gateway
Check that the FPM service name is correct, the service is running, the socket file exists and NGINX has permission to access it. Compare the rendered socket path with the actual path under /run/php. If you changed PHP versions, update the package, service, configuration and socket variables together.
Database modules fail to import or authenticate
Install the Python 3 PyMySQL package on the managed node, use the correct collection module, and select a login method supported by the server. A local Unix socket can work when TCP credentials do not. Keep passwords encrypted and mark password-bearing tasks with no_log: true.
Every run reports changes
Look for shell commands without an appropriate changed_when, templates that embed timestamps, permissions changed by a service, or tasks that restart services unconditionally. Replace commands with Ansible modules where possible and notify handlers only from tasks that actually changed files.
Production considerations
Use separate variables for development, staging and production. Keep production database passwords in Ansible Vault or an approved secret manager, restrict SSH access, and define firewall rules that expose HTTP and HTTPS publicly but keep database access private. Add TLS termination, backups, log rotation, monitoring and a tested restore procedure before calling the host production.
For a multi-host deployment, use inventory groups such as web, php and database. Set the database host’s private address in variables and grant the application account access only from the web network. The same roles can then be reused without duplicating task logic.
Pin Ansible collections and maintained roles after testing. Review Ubuntu release support, PHP package availability and database upgrade notes during every operating-system upgrade. Keep templates small, document why a non-default setting exists, and run the playbook in check mode against a staging host before production changes.
Quick Recap
What success looks like
- The control node can reach every managed host over SSH.
- Python runs on each managed host without installing Ansible there.
- NGINX is enabled, serves the intended server block and passes
nginx -t. - PHP-FPM is enabled, its socket matches the NGINX template and a PHP request returns the expected response.
- The selected MySQL-compatible server is running, the PyMySQL dependency is present, and the application database and least-privilege user exist.
- A second playbook run is effectively idempotent.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




