Manage Services, Systemd Targets, Cron and at Jobs with Ansible

Use Ansible on Rocky Linux 10 to start and enable systemd units, reload the manager, change default targets, reboot safely, manage cron and at jobs, and verify state with service_facts.

Published

Updated

Read time 17 min read

Reviewed byDeepak Prasad

Ansible systemd_service, cron, at, targets and reboot modules for Rocky Linux 10

Rocky Linux 10 runs services under systemd. Ansible gives you modules for day-two operations: start or enable units, reload configuration, switch the default boot target, reboot and wait, and schedule recurring or one-time jobs. This guide walks through ansible.builtin.systemd_service, ansible.builtin.reboot, ansible.builtin.cron, ansible.posix.at, and ansible.builtin.service_facts on Rocky Linux 10.2 with playbooks you can rerun safely.

It assumes you can run a basic play from your first playbook and use become for root-only changes. Writing custom unit files, full crontab syntax tutorials, and batch queue administration are out of scope—this page focuses on Ansible modules for runtime operations.

Tested on: Rocky Linux 10.2 (Red Quartz); kernel 6.12.0-211.16.1.el10_2.0.1.x86_64; ansible-core 2.16.16; systemd 257.

NOTE
This chapter is part of the GoLinuxCloud Ansible tutorial (RHCE EX294). Follow along from ~/ansible-project, inventory group lab, and playbooks in playbooks/. Use your own host names and paths if yours differ.

I show the reboot module against a remote host on purpose—Ansible will not reboot a connection: local target, which keeps you from accidentally bouncing the control node.

Task Module
Start/stop/restart service ansible.builtin.systemd_service
Enable/disable service at boot ansible.builtin.systemd_service
Reload systemd manager config ansible.builtin.systemd_service with daemon_reload
Reboot host safely ansible.builtin.reboot
Gather service status ansible.builtin.service_facts
Create recurring scheduled job ansible.builtin.cron
Create one-time scheduled job ansible.posix.at
Check default target ansible.builtin.command
Change default target ansible.builtin.command or managed symlink approach

What This Article Covers

  • ansible.builtin.systemd_service for start, stop, restart, reload, enable, disable, mask, and daemon_reload
  • ansible.builtin.service_facts to verify unit state before or after changes
  • Default systemd target checks and changes with systemctl get-default / set-default
  • ansible.builtin.reboot for controlled reboots on remote managed hosts
  • ansible.builtin.cron for user and root crontab entries, special_time, environment variables, and /etc/cron.d files
  • ansible.posix.at for one-time scheduled commands (requires ansible.posix collection)
  • Enabling existing systemd timers with systemd_service (cron vs timer bridge)
  • Common mistakes such as raw systemctl commands, missing enabled: true, or forgetting daemon_reload
Cover here Avoid here
systemd_service service operations Full systemd unit-file tutorial
enable/start/restart/reload Deep systemd dependency theory
daemon_reload Writing complex custom unit files
Default target management Full boot process explanation
reboot module Kernel upgrade strategy
cron module Full crontab syntax guide
at jobs Full batch/job queue administration
service_facts verification Deep facts tutorial

Why Manage Services and Scheduled Jobs with Ansible?

Manual systemctl / crontab -e Ansible modules
Scripts re-run blindly state: started and enabled: true are idempotent
No shared record of cron entries cron name: tracks #Ansible: <name> markers
Reboot-and-wait logic in shell ansible.builtin.reboot polls until SSH returns
Service checks as one-off commands service_facts feeds later when: conditions

Service and schedule tasks appear in almost every baseline play—web stacks, agents, backups, maintenance windows. Backup cron jobs often target paths on volumes you provision with LVM and persistent mounts. Keeping service work in modules (not raw shell) matches idempotency practice and reads clearly in playbook structure. Use FQCN in playbooks: ansible.builtin.systemd_service, not bare systemd—see modules and ansible-doc.


Prerequisites on Rocky Linux

  • Managed host running Rocky Linux 10 with working systemd
  • ansible-core on the control node and Python on the target (default on Rocky)
  • ansible.posix collection installed on the control node for ansible.posix.at
  • become: true (or -b) for service, target, cron, and at tasks
  • For ansible.posix.at: at package installed on the managed host and atd service running

Check and install the collection if needed:

bash
ansible-galaxy collection list ansible.posix

Sample output:

output
# /usr/share/ansible/collections/ansible_collections
Collection    Version
------------- -------
ansible.posix 2.1.0
bash
ansible-galaxy collection install ansible.posix

Sample output:

output
Nothing to do. All requested collections are already installed.

Without ansible.posix, playbooks fail with couldn't resolve module/action 'ansible.posix.at'.

Quick connectivity check:

bash
ansible lab -m ansible.builtin.ping

Sample output:

output
localhost | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

Confirm systemd is available on the target:

bash
ansible lab -m ansible.builtin.command -a "systemctl --version"

Sample output:

output
localhost | CHANGED | rc=0 >>
systemd 257 (257-23.el10_2.1.rocky.0.1-ga8848ef)
+PAM +AUDIT +SELINUX -APPARMOR +IMA +IPE +SMACK +SECCOMP -GCRYPT -GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN -IPTC +KMOD +LIBCRYPTSETUP +LIBCRYPTSETUP_PLUGINS +LIBFDISK +PCRE2 +PWQUALITY +P11KIT -QRENCODE +TPM2 +BZIP2 +LZ4 +XZ +ZLIB +ZSTD +BPF_FRAMEWORK +BTF +XKBCOMMON +UTMP +SYSVINIT +LIBARCHIVE

The first line confirms systemd is present on the managed host.


service vs systemd_service in Ansible

ansible.builtin.service ansible.builtin.systemd_service
Init support Portable across init systems systemd-specific
On Rocky Linux 10 Delegates to systemd Native systemd module
Extra options Basic start/stop/enable daemon_reload, masked, drop-in aware
EX294 / RHEL focus Works, but less explicit Preferred on Rocky/RHEL

On Rocky Linux both modules can start sshd. Output is similar when the unit is already running:

bash
ansible lab -m ansible.builtin.service -a "name=sshd state=started" -b

Sample output:

output
localhost | SUCCESS => {
    "changed": false,
    "name": "sshd",
    "state": "started"
}
bash
ansible lab -m ansible.builtin.systemd_service -a "name=sshd state=started" -b

Sample output:

output
localhost | SUCCESS => {
    "changed": false,
    "name": "sshd",
    "state": "started"
}

Use ansible.builtin.systemd_service when you need daemon_reload, masking, or other systemd-only knobs. Reserve ansible.builtin.service for plays that must stay portable across non-systemd hosts.

Official references: service module, systemd_service module.


Manage Services with systemd_service

ansible.builtin.systemd_service controls systemd units—services, sockets, timers, and more. For unit operations, use at least one of state, enabled, or masked with name. For manager-only operations, daemon_reload: true can run by itself to force systemd to reread unit files.

Typical parameters:

Parameter Purpose
name Unit name (httpd or httpd.service)
state started, stopped, restarted, reloaded
enabled Start unit at boot (true / false)
daemon_reload Run systemctl daemon-reload before other actions
masked Prevent unit from starting (true / false)

Install the package first when the unit does not exist yet—see manage packages on Rocky Linux for ansible.builtin.dnf.


Start, Stop, Restart and Reload Services

state: started ensures the unit is running. state: stopped stops it. state: restarted stops and starts again—use after changes that require a full process restart. state: reloaded asks systemd to reload configuration when the unit supports reload (for example httpd).

Start and enable httpd:

yaml
- name: Ensure httpd is started and enabled
  ansible.builtin.systemd_service:
    name: httpd
    state: started
    enabled: true
bash
ansible-playbook playbooks/start-enable-httpd.yml

Sample output:

output
TASK [Ensure httpd is started and enabled] *************************************
ok: [localhost]

A second run reports ok with changed: false when the service is already active and enabled.

Reload httpd after a configuration change without dropping connections when the unit supports reload:

yaml
- name: Reload httpd configuration
  ansible.builtin.systemd_service:
    name: httpd
    state: reloaded
bash
ansible-playbook playbooks/reload-httpd.yml

Sample output:

output
TASK [Reload httpd configuration] **********************************************
changed: [localhost]

Note that state: reloaded is not a no-op check. The module always requests a reload, and if the service is not running, systemd starts it. Use handlers so reloads happen only after a configuration task reports changed.

For config-file changes in real plays, notify a handler with state: reloaded instead of restarting the service on every task run.


Enable or Disable Services at Boot

enabled: true creates the systemd enablement symlinks so the unit starts at boot according to its [Install] section. enabled: false removes that boot-time enablement without necessarily stopping the running process—pair with state: stopped when you want both.

yaml
- name: Enable chronyd at boot
  ansible.builtin.systemd_service:
    name: chronyd
    enabled: true

- name: Disable and stop legacy agent
  ansible.builtin.systemd_service:
    name: legacy-agent
    enabled: false
    state: stopped

Starting a service now does not enable it at boot unless you set enabled: true in the same task or a separate one.


Reload systemd with daemon_reload

When you copy or template a new unit file, systemd must reload its unit database before systemctl enable or start sees the change.

yaml
- name: Install demo unit file
  ansible.builtin.copy:
    dest: /etc/systemd/system/demo-oneshot.service
    mode: '0644'
    content: |
      [Unit]
      Description=Ansible demo oneshot unit

      [Service]
      Type=oneshot
      ExecStart=/bin/true
      RemainAfterExit=yes

      [Install]
      WantedBy=multi-user.target

- name: Reload systemd manager configuration
  ansible.builtin.systemd_service:
    daemon_reload: true

- name: Enable demo unit
  ansible.builtin.systemd_service:
    name: demo-oneshot.service
    enabled: true
    state: started
bash
ansible-playbook playbooks/daemon-reload-demo.yml

Sample output:

output
TASK [Install demo unit file] **************************************************
changed: [localhost]

TASK [Reload systemd manager configuration] ************************************
ok: [localhost]

TASK [Enable demo unit] ********************************************************
changed: [localhost]

Set daemon_reload: true on the same task as state or enabled when you change a unit file and apply state in one step—the module runs reload before other operations.


Mask and Unmask Services

Masking prevents a unit from being started manually or as a dependency. Use it to hard-block a service without deleting its unit file.

yaml
- name: Mask demo oneshot unit
  ansible.builtin.systemd_service:
    name: demo-oneshot.service
    masked: true

- name: Unmask demo oneshot unit
  ansible.builtin.systemd_service:
    name: demo-oneshot.service
    masked: false
bash
ansible-playbook playbooks/mask-unmask.yml

Sample output:

output
TASK [Mask demo oneshot unit] **************************************************
changed: [localhost]

TASK [Unmask demo oneshot unit] ************************************************
ok: [localhost]

Unmask before you enable or start the unit again.


Verify Service Status with service_facts

ansible.builtin.service_facts collects service state into facts—useful before restart tasks or conditional fixes. Access services with bracket notation when the unit name contains hyphens.

yaml
- name: Collect service state facts
  ansible.builtin.service_facts:

- name: Show httpd service state
  ansible.builtin.debug:
    msg: "httpd state={{ ansible_facts.services['httpd.service'].state }} enabled={{ ansible_facts.services['httpd.service'].status }}"
bash
ansible-playbook playbooks/service-facts.yml

Sample output:

output
TASK [Show httpd service state] ************************************************
ok: [localhost] => {
    "msg": "httpd state=running enabled=enabled"
}

Check whether a unit exists before acting on it:

yaml
- name: Collect service facts
  ansible.builtin.service_facts:

- name: Report when httpd is missing
  ansible.builtin.debug:
    msg: httpd unit not registered
  when: "'httpd.service' not in ansible_facts.services"

See debug playbook techniques for more fact-driven troubleshooting patterns.

Official reference: service_facts module.


Manage Systemd Targets

A systemd target groups units for a boot mode—multi-user.target is the usual server default. Ansible has no dedicated target module; use ansible.builtin.command with systemctl get-default and systemctl set-default, or manage the /etc/systemd/system/default.target symlink with ansible.builtin.file if you prefer declarative symlinks.


Check the Current Default Target

yaml
- name: Read current default target
  ansible.builtin.command: systemctl get-default
  register: default_target
  changed_when: false

- name: Display default target
  ansible.builtin.debug:
    var: default_target.stdout
bash
ansible-playbook playbooks/get-default-target.yml

Sample output:

output
ok: [localhost] => {
    "default_target.stdout": "multi-user.target"
}

Change the Default Systemd Target

yaml
- name: Ensure default target is multi-user.target
  ansible.builtin.command: systemctl set-default multi-user.target
  register: set_target
  changed_when: "'Created symlink' in set_target.stdout or 'Removed' in set_target.stdout"
bash
ansible-playbook playbooks/set-default-target.yml

Sample output:

output
TASK [Ensure default target is multi-user.target] ******************************
ok: [localhost]

When the target is already multi-user.target, the task stays ok with changed: false. Switching from graphical.target reports changed when the symlink updates.

IMPORTANT
Changing the default target affects the next reboot. Validate dependencies and console access before setting rescue.target or other recovery targets on production hosts.

Reboot Managed Hosts Safely

ansible.builtin.reboot reboots the host, waits for it to go down, then polls until Ansible can connect again. Use it on remote managed hosts—not on localhost with connection: local, where Ansible refuses to reboot the control node.

yaml
- name: Reboot application servers and wait
  hosts: app
  become: true
  serial: 1
  tasks:
    - name: Reboot managed host safely
      ansible.builtin.reboot:
        msg: Reboot initiated by Ansible
        connect_timeout: 5
        reboot_timeout: 600
        post_reboot_delay: 30
        test_command: uptime

Useful parameters:

Parameter Purpose
reboot_timeout Maximum seconds to wait for the host to return
post_reboot_delay Extra pause after boot before validation
test_command Command that must succeed before the task finishes
msg Broadcast message to logged-in users before reboot

reboot_timeout applies separately to waiting for the reboot and waiting for test_command to succeed, so a badly stuck reboot task can take longer than the single number suggests.

On a local-connection lab host, Ansible stops with a clear error instead of rebooting your workstation:

output
fatal: [localhost]: FAILED! => {"changed": false, "msg": "Running ansible.builtin.reboot with local connection would reboot the control node."}

Official reference: reboot module.


Manage Cron Jobs with ansible.builtin.cron

ansible.builtin.cron manages per-user crontab entries and cron environment variables. Each managed job needs a unique name—Ansible writes #Ansible: <name> above the line so later runs update the same entry.

Official reference: cron module.


Create User Cron Jobs

yaml
- name: Ensure demo user exists
  ansible.builtin.user:
    name: demo_cron
    state: present
    shell: /bin/bash

- name: Add daily cron job
  ansible.builtin.cron:
    name: ansible demo daily touch
    user: demo_cron
    minute: "30"
    hour: "2"
    job: /usr/bin/touch /tmp/ansible-cron-demo
bash
ansible-playbook playbooks/cron-daily.yml

Sample output:

output
TASK [Add daily cron job] ******************************************************
changed: [localhost]

Verify on the host:

bash
crontab -u demo_cron -l

Sample output:

output
#Ansible: ansible demo daily touch
30 2 * * * /usr/bin/touch /tmp/ansible-cron-demo

Create Root Cron Jobs

Root crontab entries require become: true on the play or task.

yaml
- name: Hourly root maintenance stub
  ansible.builtin.cron:
    name: ansible root hourly stub
    user: root
    minute: "15"
    hour: "*"
    job: /usr/bin/logger -t ansible-demo hourly stub
bash
ansible-playbook playbooks/cron-root.yml

Sample output:

output
TASK [Hourly root maintenance stub] ********************************************
changed: [localhost]

Disable or Remove Cron Jobs

Disable a job while keeping the line in crontab (commented out):

yaml
- name: Disable daily touch job temporarily
  ansible.builtin.cron:
    name: ansible demo daily touch
    user: demo_cron
    minute: "30"
    hour: "2"
    job: /usr/bin/touch /tmp/ansible-cron-demo
    disabled: true

Remove the job entirely:

yaml
- name: Remove demo cron job
  ansible.builtin.cron:
    name: ansible demo daily touch
    user: demo_cron
    state: absent
bash
ansible-playbook playbooks/cron-remove.yml

Sample output:

output
TASK [Remove demo cron job] ****************************************************
changed: [localhost]

After removal, crontab -u demo_cron -l reports no crontab for that user.


Manage Environment Variables in Cron

Cron jobs run with a minimal environment. Set variables explicitly when commands need a full PATH or custom directories.

yaml
- name: Set PATH for root cron jobs
  ansible.builtin.cron:
    name: PATH
    env: true
    user: root
    job: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
bash
crontab -l | head -3

Sample output:

output
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
#Ansible: ansible root hourly stub
15 * * * * /usr/bin/logger -t ansible-demo hourly stub

Use env: true with name set to the variable name (PATH, MAILTO, SHELL, and so on).


Use special_time for @daily and @reboot Jobs

The cron module supports special_time for common schedules such as @daily, @hourly, and @reboot—without spelling out minute and hour fields.

yaml
- name: Run cleanup daily using special_time
  ansible.builtin.cron:
    name: daily cleanup
    user: root
    special_time: daily
    job: /usr/local/sbin/cleanup.sh

If a cron command uses date formatting such as date +%F, write it as date +\%F inside the job string—cron treats % specially unless escaped.


Manage /etc/cron.d Files

Use cron_file when you want Ansible to manage a file under /etc/cron.d instead of a user crontab. When using cron_file, specify user because system cron files include the run-as user field.

yaml
- name: Create cron.d job
  ansible.builtin.cron:
    name: app health check
    cron_file: app-health-check
    user: root
    minute: "*/5"
    job: /usr/bin/logger -t app-health-check ok
IMPORTANT
Use cron_file only for files Ansible manages exclusively. Do not point it at /etc/crontab.

Cron vs systemd timers

Use cron when you need classic crontab entries or user-level schedules. Use systemd timers when the job should integrate with systemd units, dependencies, logging, and boot behavior. This article manages existing timer units with systemd_service; writing custom timer files belongs in a separate systemd unit tutorial.

For RHCE EX294, expect tasks that use ansible.builtin.cron and ansible.posix.at—the exam still tests classic scheduling even though many production stacks prefer systemd timers for new work. Learn both: timers for modern services that ship .timer units, cron and at for exam objectives and legacy crontab workflows.

yaml
- name: Enable and start logrotate timer
  ansible.builtin.systemd_service:
    name: logrotate.timer
    state: started
    enabled: true

Schedule One-Time Jobs with ansible.posix.at

ansible.posix.at schedules a command or script to run once in the future. Jobs land in the a queue. Install the at package and start atd before scheduling—the module does not bootstrap either for you.

yaml
- name: Install at package
  ansible.builtin.dnf:
    name: at
    state: present

- name: Ensure atd is running
  ansible.builtin.systemd_service:
    name: atd
    state: started
    enabled: true

- name: Schedule job in five minutes
  ansible.posix.at:
    command: /usr/bin/logger -t ansible-at-demo one-time job fired
    count: 5
    units: minutes
    unique: true
bash
ansible-playbook playbooks/at-once.yml

Sample output:

output
TASK [Install at package] ******************************************************
ok: [localhost]

TASK [Ensure atd is running] ***************************************************
ok: [localhost]

TASK [Schedule job in five minutes] ********************************************
ok: [localhost]

List pending jobs:

bash
atq

Sample output:

output
1	Thu Jul  9 14:05:00 2026 a root

Set unique: true to avoid duplicate jobs when the play reruns. Official reference: at module.


Verify Services, Targets, Cron and at Jobs

After a play, confirm state on the host:

bash
systemctl is-active httpd

Sample output:

output
active
bash
systemctl is-enabled httpd

Sample output:

output
enabled
bash
systemctl get-default

Sample output:

output
multi-user.target
bash
crontab -l | grep ansible

Sample output:

output
#Ansible: ansible root hourly stub
15 * * * * /usr/bin/logger -t ansible-demo hourly stub
bash
atq

Sample output:

output
1	Thu Jul  9 14:05:00 2026 a root

Inside playbooks, prefer service_facts for services and command with changed_when: false for targets instead of shell-only checks.


Common Examples

Start and enable a service

yaml
- name: Start and enable httpd
  ansible.builtin.systemd_service:
    name: httpd
    state: started
    enabled: true

Reload a service after configuration change

yaml
- name: Deploy httpd config
  ansible.builtin.template:
    src: httpd.conf.j2
    dest: /etc/httpd/conf/httpd.conf
  notify: Reload httpd

handlers:
  - name: Reload httpd
    ansible.builtin.systemd_service:
      name: httpd
      state: reloaded

Reload systemd after adding a unit file

yaml
- name: Install unit and reload systemd
  ansible.builtin.copy:
    dest: /etc/systemd/system/myapp.service
    src: files/myapp.service
  notify: Reload systemd

handlers:
  - name: Reload systemd
    ansible.builtin.systemd_service:
      daemon_reload: true

Check whether a service exists

yaml
- name: Gather service facts
  ansible.builtin.service_facts:

- name: Start httpd only when unit is present
  ansible.builtin.systemd_service:
    name: httpd
    state: started
  when: "'httpd.service' in ansible_facts.services"

Change default target to multi-user.target

yaml
- name: Set multi-user default target
  ansible.builtin.command: systemctl set-default multi-user.target
  register: target_result
  changed_when: "'Created symlink' in target_result.stdout"

Reboot a host and wait for it to return

yaml
- name: Rolling reboot of web tier
  hosts: web
  become: true
  serial: 1
  tasks:
    - name: Reboot and wait
      ansible.builtin.reboot:
        reboot_timeout: 900
        post_reboot_delay: 45
        test_command: systemctl is-system-running

Run against remote inventory hosts over SSH—not connection: local on the control node.

Create a daily cron job

yaml
- name: Daily log rotation reminder
  ansible.builtin.cron:
    name: daily logrotate reminder
    user: root
    minute: "0"
    hour: "3"
    job: /usr/sbin/logrotate /etc/logrotate.conf

Disable or remove a cron job

yaml
# Disable (comment out) without deleting
- name: Pause daily job
  ansible.builtin.cron:
    name: daily logrotate reminder
    user: root
    job: /usr/sbin/logrotate /etc/logrotate.conf
    minute: "0"
    hour: "3"
    disabled: true

# Remove entirely
- name: Remove daily job
  ansible.builtin.cron:
    name: daily logrotate reminder
    user: root
    state: absent

Schedule a one-time at job

yaml
- name: Install at and schedule migration
  ansible.builtin.dnf:
    name: at
    state: present

- name: Ensure atd is running
  ansible.builtin.systemd_service:
    name: atd
    state: started
    enabled: true

- name: Run migration script tonight
  ansible.posix.at:
    command: /usr/local/bin/migrate-db.sh
    count: 2
    units: hours
    unique: true

Common Mistakes

Mistake Fix
Using command: systemctl restart everywhere Use ansible.builtin.systemd_service
Forgetting enabled: true Service starts now but not after reboot
Forgetting daemon_reload after unit changes systemd may not see the updated unit
Managing root cron without become: true Add privilege escalation
Creating duplicate cron jobs manually Use stable name: in cron module
Expecting cron environment to match shell Define required environment variables
Using % unescaped in cron commands Escape % as \% because cron treats % specially
Using at without package/service availability Install at with dnf and ensure atd is running
Missing ansible.posix collection Run ansible-galaxy collection install ansible.posix on the control node
Rebooting without waiting Use ansible.builtin.reboot instead of raw reboot command
Running reboot on local connection Target remote hosts; local connection is blocked by design

Best Practices

Practice Why
Prefer systemd_service on Rocky/RHEL Exposes daemon_reload, masked, and systemd semantics
Pair state with enabled for daemons Survives reboot without a separate enable play
Use handlers for reload/restart Avoids bouncing services when config tasks rerun
Set daemon_reload after unit file tasks systemd picks up new units before enable/start
Use unique cron name values Prevents duplicate #Ansible: entries
Define PATH in cron when needed Cron minimal env breaks scripts that rely on login paths
Install ansible.posix on the control node ansible.posix.at is not bundled with ansible-core alone
Install at package before enabling atd The at module schedules jobs; it does not install packages
Use serial: 1 with reboot on groups Rolling reboots reduce downtime
Verify with service_facts in playbooks Cleaner than ad-hoc systemctl in every play
Move repeated service patterns into roles Reuse enable/start/cron blocks across plays

Summary

On Rocky Linux 10, ansible.builtin.systemd_service owns unit runtime state—start, stop, reload, enable, mask, and daemon_reload. ansible.builtin.service_facts confirms unit state inside plays. Default targets use systemctl get-default and set-default via ansible.builtin.command. ansible.builtin.reboot reboots remote hosts and waits for SSH; it refuses local connection. ansible.builtin.cron manages recurring jobs with stable name keys, special_time, and optional cron_file entries. ansible.posix.at schedules one-time work when the ansible.posix collection is installed and at/atd are available on the host. Keep service restarts in handlers, reload systemd after unit file changes, and define cron environment variables your jobs need.


Frequently Asked Questions

1. Should I use ansible.builtin.service or ansible.builtin.systemd_service on Rocky Linux?

Prefer ansible.builtin.systemd_service on Rocky Linux 10. It exposes systemd-specific options such as daemon_reload and masked. ansible.builtin.service still works because it delegates to systemd, but systemd_service is the clearer choice for RHEL-family hosts.

2. When do I need daemon_reload in a playbook?

Run daemon_reload after you add, remove, or change a unit file under /etc/systemd/system/ or drop-in directories. Without it, systemd may keep using the previous unit definition until the manager reloads its configuration.

3. Can ansible.builtin.reboot run against localhost with connection local?

No. The reboot module refuses local connection because it would reboot the control node. Use it on remote managed hosts over SSH, or isolate reboot plays to inventory groups that are not your Ansible control machine.

4. Why must cron jobs use a stable name parameter?

The cron module tags each managed entry with a comment like #Ansible: . Ansible uses that name to find the same job on later runs. Changing name creates a duplicate entry instead of updating the original job.

5. Do I need the at package for ansible.posix.at?

Yes on the managed host: install the at package and ensure atd is running. On the control node, install the ansible.posix collection with ansible-galaxy collection install ansible.posix—the at module is not part of ansible-core.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …