Cubis Engineers

Server baseline playbook

Apply a small, repeatable Linux baseline with packages, an operations group, time settings, and a running service.

Cloud & infrastructureIntermediateUpdated Aug 13, 2026ansibleplaybookpackagessystemdhandlers

A baseline playbook describes the settings every managed server should keep. Start small, use built-in modules, and make each task understandable without reading a shell script.

Create the playbook

playbooks/server-baseline.yml
---
- name: Apply the Linux server baseline
  hosts: all
  become: true
  serial: 2

  tasks:
    - name: Install common operations packages
      ansible.builtin.package:
        name: "{{ common_packages }}"
        state: present

    - name: Create the operations group
      ansible.builtin.group:
        name: "{{ operations_group }}"
        state: present

    - name: Set the system timezone
      community.general.timezone:
        name: "{{ timezone }}"

    - name: Install Nginx
      ansible.builtin.package:
        name: nginx
        state: present

    - name: Write the Nginx health page
      ansible.builtin.template:
        src: health.html.j2
        dest: /usr/share/nginx/html/health.html
        owner: root
        group: root
        mode: '0644'
      notify: Reload Nginx

    - name: Start Nginx at boot and now
      ansible.builtin.service:
        name: nginx
        enabled: true
        state: started

  handlers:
    - name: Reload Nginx
      ansible.builtin.service:
        name: nginx
        state: reloaded

Install the community.general collection before using its timezone module:

Terminal
ansible-galaxy collection install community.general

Commit a requirements.yml file when the project uses collections so CI and other operators install the same dependencies.

Add the template

templates/health.html.j2
<!doctype html>
<html lang="en">
  <head><meta charset="utf-8"><title>healthy</title></head>
  <body>
    <p>healthy</p>
    <p>host: {{ inventory_hostname }}</p>
  </body>
</html>

The template task notifies the handler only when the rendered file changes. The handler then reloads Nginx once at the end of the play instead of restarting it after every task.

Handle family differences with variables

The generic package and service modules work across common Linux families, but package or service names sometimes differ. Put those differences in variables rather than duplicating the playbook.

group_vars/debian.yml
---
firewall_package: ufw
ssh_service: ssh
group_vars/redhat.yml
---
firewall_package: firewalld
ssh_service: sshd

Group hosts by family in inventory or include a variable file based on gathered facts. Test the exact versions your team supports; a shared family name does not guarantee identical repositories or defaults.

Check idempotence

Run the playbook twice against a disposable server. The first run should make the expected changes. The second should report no changes unless external state drifted.

Terminal
ansible-playbook playbooks/server-baseline.yml --limit web-01
ansible-playbook playbooks/server-baseline.yml --limit web-01

Do not automate an SSH lockout

Manage SSH configuration in a separate, carefully tested play. Validate the candidate configuration with sshd -t, keep an existing session open, use a small serial value, and confirm a new connection before moving to the next host.

On this page