MaruOpsInfrastructure and AI notes

About (Korean)Contact (Korean)

Start with Ansible: SSH Access and a First Playbook

Server rack with multiple network cables connected

Reference photo · Server rack with multiple network cables connected · Photo: Kim Scarborough / Source / CC BY-SA 2.0 · Resized for listings; illustrative photo, not the tested environment

Ansible groups repeatable server tasks around an inventory of managed hosts and a YAML playbook describing the work. Prepare SSH access, verify the target Python environment and run a small first playbook. Check the resulting change before using the same pattern across more servers.

In this article, we will deploy the same instruction file on two Linux servers. This is an example of checking on one server first and then expanding to the remaining servers, and you can follow it even if there is only one server.

On this page

Preparation environment and connection conditions

CategoryWhat to prepare
control nodeAn Ubuntu 24.04 LTS PC or server to run Ansible commands. sudo permission for Internet connection and package installation
Administered by1-2 Linux servers with Python 3 compatible with SSH server
access accountReplace ops in the example below with your actual SSH account. Must have public key authentication in place

Ansible is installed on the control node. The basic module for Linux used in this article runs with SSH and Python without installing the Ansible agent on the target server. The scope of Python support varies depending on Ansible version, so if applying to an existing environment, also check the official support table .

1. Install Ansible on the control node

The command below is run on the control node. pipx is a tool that installs Python command line tools into a separate environment, and this example requires only the base modules, so the ansible-core package is sufficient.

sudo apt update
sudo apt install -y pipx openssh-client
pipx install ansible-core
export PATH="$HOME/.local/bin:$PATH"
pipx ensurepath
ansible --version

export causes the command to be found directly in the current terminal, and pipx ensurepath sets the path for future login environments. Check the installed core version and Python path at ansible --version. The installation procedure follows official pipx installation guide.

2. SSH connection and inventory check

Example addresses 192.0.2.11 and 192.0.2.12 are addresses for documents. Change it to the actual server IP and account, and first check the regular SSH connection. The host key fingerprint of the first connection is checked against the value found in the server administrator or console and then approved.

ssh ops@192.0.2.11 "python3 --version"
ssh ops@192.0.2.12 "python3 --version"
mkdir -p ~/ansible-demo
cd ~/ansible-demo

If you have not yet prepared public key authentication, register the control node’s public key with ~/.ssh/authorized_keys in the target account. The private key is stored in the control node. If you are only practicing one, you can skip the second SSH command and the node2 line below.

Save the inventory.ini file in your working folder with the following contents: node1 and node2 are aliases called by Ansible, and lab is the group name that binds the two servers.

[lab]
node1 ansible_host=192.0.2.11
node2 ansible_host=192.0.2.12

[lab:vars]
ansible_user=ops

After checking the target list, check the connection with the ping module.

ansible-inventory -i inventory.ini --graph
ansible lab -i inventory.ini -m ansible.builtin.ping

If SUCCESS and "ping": "pong" appear on each server, move on to the next task. This ping module does not check ICMP communication, but rather checks SSH login and Python execution. If you use a separate private key, add --private-key ~/.ssh/실제_키_파일 to the command.

3. Create a playbook to distribute the file

Save deploy.yml in the same folder. Indentation in YAML uses spaces instead of tabs.

---
- name: Deploy a demo file to Linux servers
  hosts: lab
  gather_facts: false

  tasks:
    - name: Write the same demo file on each server
      ansible.builtin.copy:
        dest: /tmp/maruops-ansible-demo.txt
        content: |
          Managed by Ansible.
        mode: "0644"

hosts: lab selects a group of inventory. gather_facts: false skips collecting system information that is not required for this task. copy module matches the content and permissions of the target file, and mode writes it as a string to clarify the interpretation of permission values.

This example aligns the contents of /tmp/maruops-ansible-demo.txt with the above sentence. If there is an existing file with the same name, it may be overwritten, so make sure it is a path for practice purposes. Files in /tmp can be erased by reboot or cleanup policy, so it is not suitable for storing persistent settings files.

4. Check on one device and run all

First, check the grammar and execution target, and preview the changes to be made on one node1. Also run the command below from the ~/ansible-demo folder.

ansible-playbook -i inventory.ini deploy.yml --syntax-check
ansible-playbook -i inventory.ini deploy.yml --list-hosts
ansible-playbook -i inventory.ini deploy.yml --limit node1 --check --diff

--check predicts changes, and --diff shows differences in file content. The copy task in this article supports both features, but not all modules and playbooks are pre-executed in the same way. Even if the grammar check passes, it does not guarantee access or execution rights.

If the expected target and content are correct, apply it to one machine and run the same playbook to the entire group.

ansible-playbook -i inventory.ini deploy.yml --limit node1
ansible-playbook -i inventory.ini deploy.yml
ansible lab -i inventory.ini -m ansible.builtin.command \
  -a "cat /tmp/maruops-ansible-demo.txt"

The last command checks whether Managed by Ansible. is saved on each server. The command module for verification can also report read commands as CHANGED by default. Whether the file distribution has changed again is determined by the results of the next playbook execution.

Try running the same playbook one more time.

ansible-playbook -i inventory.ini deploy.yml

If the file contents and permissions are the same, changed=0, unreachable=0, and failed=0 are displayed in PLAY RECAP for each server. The property of not making any further changes if the desired state is maintained even after repeating the same operation is called idempotency. Executing arbitrary shell commands does not automatically produce this property.

Where to check when execution is blocked

symptomsWhere to check
UNREACHABLE / Permission deniedCheck general SSH connection, IP, port, account, and key. If your SSH port is not 22, add ansible_port=실제포트 to the appropriate host line.
Python execution failedCheck the Python version and support range of the target server. If the paths are different, specify ansible_python_interpreter=/실제/python3/경로 in the host line.
File write permission errorNote the file owner and path for this example: Tasks that require administrator privileges, such as /etc, require become: true and sudo privileges of the target account. If sudo requires a password, add -K to the run command.

At first, it is enough to manage only two files: inventory and playbook. Once you become familiar with distributing the guide file, you can change it to distributing the settings file that actually repeats the copy operation, check it on one device, and expand the scope of application.

Confirmed by: Official document confirmed on October 22, 2026. We verified SSH connection, file deployment, change preview, and rerun on three Ubuntu 24.04 containers (1 control node, 2 management nodes), Python 3.12.3, and ansible-core 2.21.5. No changes were made to the settings or services of the operating server.

reference document