Ansible Deploy — Automate Server Configuration and Deployments Without Retyping Every Command
> cd .. / HUB_EDITORIALE
Sviluppo di siti web

Ansible Deploy — Automate Server Configuration and Deployments Without Retyping Every Command

[2026-08-02] Author: Ing. Calogero Bono
> share
Zenithby Meteora Web The operating system for your business. Social, clients, bookings and invoices in one platform. Gyms, barbers, professionals. Discover Zenith Free demo · no card

Are you tired of SSHing into your server every time to update nginx, reload PHP-FPM, or copy the latest files? Each time it's a series of copy-paste commands, a potential human error, an hour of work you can't bill. The problem is not the single operation: it's that you repeat it. Repeating manually means wasting time, making mistakes on a server without realizing it, and discovering at 3 AM that a configuration went to production without a backup. There's a different way: write the configuration once, as code, and apply it everywhere, in the exact same way.

Here at Meteora Web, we've used this philosophy since 2017. When a client has three servers or ten, we don't trust memory. We write an Ansible playbook, version it in git, and tell the system: apply this configuration, verifying everything is as expected. In 2026, doing it by hand is not just inefficient: it's a risk.

What is Ansible and why is it called configuration as code?

Ansible is an IT automation engine that describes the desired state of your servers in text files. You don't say "run this command and hope it works." You say "this server must have nginx installed, port 80 open, PHP 8.3 active." Ansible figures out what to do, connection after connection.

The key difference from bash scripts is that Ansible is declarative and idempotent. Declarative: you describe the result, not the steps. Idempotent: running the same playbook a hundred times produces the same result. If the configuration is already correct, Ansible does nothing. If it's wrong, it fixes it. A bash script, on the other hand, runs everything every time: if a package is already installed, it reinstalls it; if a file exists, it overwrites it randomly. With Ansible, the server converges to the declared state. No more configuration drift.

Why is Ansible the right tool for Italian SMEs?

Because it doesn't require an agent installed on your servers. It uses SSH, which you already know. You don't need a dedicated infrastructure: just a computer with Python and SSH to manage all servers. And the learning curve is gentler than alternatives like Puppet or Chef. In practice, Ansible is the fastest way to move from "I configure everything by hand" chaos to professional management.

Here at Meteora Web, we use it to configure hundreds of servers across Italy, from clothing stores to professionals with a catalog portal. The result is always the same: fewer errors, more time for value-added work.

Sponsored Protocol

Take action now: install Ansible on your computer. On Linux or macOS, open a terminal and type sudo apt install ansible (Debian/Ubuntu) or brew install ansible (macOS). On Windows, use WSL2 with an Ubuntu distro. After installation, verify with ansible --version.

How does Ansible work agentless?

Ansible works in push mode. The machine where you run it (the control node) pushes commands to target servers (the nodes) via SSH. You install nothing on the remote server. This is a huge advantage over other solutions: no extra ports to open, no additional services to protect, no agents to update.

On your computer, Ansible runs in Python and uses predefined modules to do almost anything: install packages (apt, yum, dnf), copy files (copy, template), manage services (service, systemd), run commands (command, shell). When you run a playbook, Ansible checks the current state of the server for each task and decides whether to act or if it's already fine.

This choice is also a security matter. With less exposed surface, a server managed with Ansible is easier to maintain and monitor. And for those of us who deal with cybersecurity, knowing there are no agents scattered across client machines is a relief.

The inventory file: where you tell Ansible who to manage

An inventory is a simple INI or YAML file that lists servers and their variables. Here's a minimal inventory hosts.ini example:

[web]
web1.example.com ansible_host=192.168.1.10
web2.example.com ansible_host=192.168.1.11

[db]
db1.example.com ansible_host=192.168.1.20

[web] and [db] are groups. You can run a playbook only on one group, bringing order to chaos. In production, we manage inventories that change dynamically, but a static file is fine to start. Remember to set connection variables, e.g., ansible_user=deploy and ansible_ssh_private_key_file=/path/to/key.

Difference from a shell script: a shell script is a sequence of imperative commands that doesn't know what's already been done. Ansible describes the target state and applies it. It's not a subtlety: it's why you don't break production by running a playbook twice.

Take action now: create a hosts.ini file with your servers. Then test connectivity with ansible all -i hosts.ini -m ping --user your-user. The ping command is not the classic ICMP ping: it uses a Python module to verify SSH works and the server responds.

Sponsored Protocol

How to write an Ansible playbook for deploy?

Playbooks are the heart of Ansible. They are YAML files containing a list of plays. Each play defines which servers to target and which tasks to run. Here's a minimal playbook to configure nginx and a PHP app on a server:

---
- name: Configure web server for my app
  hosts: web
  become: yes
  vars:
    domain: example.com
    php_version: "8.3"

  tasks:
    - name: Update apt cache
      apt:
        update_cache: yes
        cache_valid_time: 3600

    - name: Install nginx and PHP-FPM
      apt:
        name:
          - nginx
          - php{{ php_version }}-fpm
        state: present

    - name: Start and enable nginx
      service:
        name: nginx
        state: started
        enabled: yes

    - name: Copy virtual host configuration
      template:
        src: templates/vhost.conf.j2
        dest: /etc/nginx/sites-available/{{ domain }}
      notify: reload nginx

    - name: Enable virtual host
      file:
        src: /etc/nginx/sites-available/{{ domain }}
        dest: /etc/nginx/sites-enabled/{{ domain }}
        state: link
      notify: reload nginx

    - name: Copy app files
      synchronize:
        src: ./app/
        dest: /var/www/{{ domain }}
        delete: yes

  handlers:
    - name: reload nginx
      service:
        name: nginx
        state: reloaded

What happens here? Each task is explicitly declared. If the virtual host isn't configured yet, it gets created and nginx is reloaded. If the configuration is identical, the playbook does nothing and ends with ok=5 instead of changed=5. That's idempotence: the result is always the desired state, with minimal intervention.

The notify: reload nginx line is a handler. Ansible runs it only if a previous task actually changed something. Nginx is not reloaded every time, only when needed. Another detail that avoids taking the server down for an innocent file change.

Jinja2 templates for dynamic configurations

Ansible integrates Jinja2 to generate dynamic configuration files. The nginx virtual host from the previous playbook can be a template vhost.conf.j2:

server {
    listen 80;
    server_name {{ domain }};
    root /var/www/{{ domain }};
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php{{ php_version }}-fpm.sock;
    }
}

Variables between double braces are replaced with values defined in the playbook. The same template works for multiple sites on the same server, without duplicating files. To add a domain, just change one line in the playbook.

Sponsored Protocol

Take action now: take a test server, create a playbook with at least two tasks: install nginx and create a customized index.html. Run it with ansible-playbook -i hosts.ini playbook.yml and check in a browser. Then run it again and look at the output: tasks will report ok, not changed.

Does Ansible deploy really work for real web applications?

Manual deployment of a web app is the most frequent source of errors we see in projects that come to us. Files uploaded via FTP at odd hours, production code that doesn't match test, dependencies installed randomly. Ansible solves this too, not just system configuration.

Here's how we at Meteora Web set up the deploy of any Laravel application, the framework we use for our clients' proprietary platforms:


- name: Deploy Laravel application
  hosts: web
  become: yes
  vars:
    deploy_dir: /var/www/my-site
    repo_url: git@github.com:client/repo.git

  tasks:
    - name: Navigate to deploy directory
      git:
        repo: "{{ repo_url }}"
        dest: "{{ deploy_dir }}"
        version: main
        update: yes
        force: yes

    - name: Install PHP dependencies with Composer
      composer:
        command: install
        working_dir: "{{ deploy_dir }}"
        no_dev: yes
        arguments: "--optimize-autoloader"

    - name: Run database migrations
      command: php artisan migrate --force
      args:
        chdir: "{{ deploy_dir }}"

    - name: Optimize Laravel cache
      command:
        cmd: php artisan optimize
        chdir: "{{ deploy_dir }}"

    - name: Set correct permissions
      file:
        path: "{{ deploy_dir }}/storage"
        owner: www-data
        group: www-data
        recurse: yes

The git module handles cloning and fetching the repository. The composer module is a wrapper for Composer. Laravel commands are executed only when needed because Ansible checks the command output and acts accordingly.

A good practice is using two directories and a symlink for zero-downtime deploy. The structure is:

/var/www/app/current -> release_20260101_1100
/var/www/app/releases/release_20260101_1100
/var/www/app/releases/release_20251228_0900

Ansible creates a new release, updates the current symlink, and activates the new code. If something goes wrong, the symlink is rolled back to the old release. Your app is back in seconds, without restoring midnight backups.

Sponsored Protocol

Take action now: if you use Laravel, turn it into your personal playbook. Start by cloning the repository to a temporary directory, then move files to production, then run php artisan migrate manually. Once you're confident, automate every step in Ansible.

How much time does an Ansible playbook save compared to manual deploy?

Let's take a concrete example. A Meteora Web client runs a server with nginx, PHP-FPM, a Laravel app, and a cron job for queues. Every week, the developer (or the owner) logs in and runs about 15 commands in precise order: git pull, composer install, php artisan migrate, php artisan cache:clear, PHP-FPM reload, etc. One typo can take the site down for hours. With Ansible, the same process is reduced to one terminal line:

ansible-playbook -i hosts.ini deploy.yml

Time for manual deploy: 20 to 45 minutes, plus verifying the result. Time with Ansible: less than 2 minutes, with automatic status verification of every task. Multiply by 4 deploys a month, that's about 3 hours saved. If a project needs two servers, Ansible updates them in parallel, not sequentially.

And time isn't the only benefit. Human errors disappear. The configuration is documented in the playbook, not in someone's head. When the person who wrote the playbook is on vacation, another team member can understand the system by reading the code. This is worth more than thousands of documentation pages.

Server setup pays off too. By hand, it takes hours to install all packages, manage permissions, configure firewalls. With Ansible, a server is ready in 10 minutes. And to reproduce an identical staging environment, you don't start from zero: you run the same playbook on a new machine.

Take action now: time how long it takes you to do a manual deploy. Write it on a sticky note. Next time you automate, compare the playbook time. The difference is your gain in billable hours.

Ansible Tower, AWX, and alternatives: what do you really need?

When playbooks grow and servers become dozens, you need a centralized interface. Ansible Tower (the commercial version) and AWX (the open-source version) are web panels that add scheduling, user roles, and historical logs. They let you run playbooks from a console without keeping a terminal open.

Sponsored Protocol

Here at Meteora Web, we recommend starting without them. First learn the command line: debugging a playbook error requires understanding what's underneath. A web interface hides stack details, and if you don't understand the log, you won't fix the problem. Add AWX when you have 3+ servers and need a unified view of jobs. An intermediate option is using GitHub Actions or GitLab CI to run Ansible from a pipeline, but that's a broader CI/CD topic, not just Ansible.

For Italian SMEs, a good rule is: get order with playbooks first, then think about additional tools. The value isn't in the dashboard, it's in the quality of playbooks. A well-written playbook can be run from the terminal.

Take action now: if you don't have a CI/CD pipeline yet, don't complicate your life. Run Ansible from your computer with an alias in .bashrc or .zshrc. Memorize the command and you'll become process-oriented.

What to do now

You've read enough. The next manual deploy you do is a missed opportunity. Here are actions to put into practice for your next project:

  • Install Ansible on your computer and create an inventory with at least one test server. You don't need a production server to learn.
  • Write your first playbook that installs nginx and creates an index.html page. Run it twice: the second run must end with ok and no changed.
  • Replicate your last manual deploy in a playbook. It doesn't matter if the playbook isn't perfect initially. Writing steps in a YAML file forces you to understand what you're really doing.
  • Respect the principles that make Ansible powerful: idempotence, handlers, and templates. If a task always forces an update, you're doing Ansible wrong.
  • Read the official documentation when in doubt: Ansible Documentation (EN). It's the authoritative source, not first-page tutorials.

This is also the approach we bring to Meteora Web. We've seen companies use Ansible with 50 servers impeccably and companies with a single server configured sloppily by hand. The difference isn't size, it's process professionalism. To explore how Ansible fits into a complete infrastructure, read our guide on DevOps and CI/CD. And for those who need to defend against errors, our guide on network forensics with Wireshark shows how to monitor traffic when things don't go as expected.

> share
Ing. Calogero Bono

> AUTHOR_EXTRACTED

Ing. Calogero Bono

Ingegnere informatico, fondatore di Meteora Web e Zenith OS. System administrator e progettista di piattaforme, app e CMS proprietari, con esperienza in sviluppo full-stack, marketing digitale ed ecosistema Google.
[ Read Full Dossier ]

> METEORA_WEB // DIGITAL AGENCY

We build the digital presence your business deserves.

Websites, social media, online advertising, e-commerce and high-performance hosting, engineered with method by computer engineers in Sciacca, for all of Italy.

> MW_JOURNAL

> READ_ALL()