Bare-metal serveriai su AMD EPYC™ 9355P procesoriumi jau pasiekiami mūsų UK lokacijoje. Norėdami užsisakyti, spauskite čia.

How to Automate Server Configuration on Ubuntu 26.04 with Ansible

  • Publikuota 2026 Spalio 30

Configuring a single server manually may only take a few minutes, but those minutes add up when you're provisioning multiple VPS instances. Repeating the same commands on every server also increases the risk of configuration differences that can become difficult to troubleshoot later.

Ansible is an automation platform that reduces repetitive server administration by executing tasks over SSH from a control machine. Using inventories and playbooks, you can define your server configuration once and apply it consistently across your infrastructure.

In this tutorial, you'll use Ansible to automate a baseline setup on a BaCloud VPS running Ubuntu 26.04 and verify that the playbook applies the configuration as expected.

Prerequisites

Before you begin, make sure you have the following:

  • A control machine running Ubuntu. You'll install Ansible on this machine and use it to run commands and playbooks.

  • A BaCloud VPS running Ubuntu Server 26.04 LTS. This will be the managed server that Ansible configures.

  • A user account on the managed BaCloud VPS with sudo privileges.

  • The public IP address or hostname of your BaCloud VPS.

Choose Ubuntu 26.04 VPS hosting in Lithuania, the Netherlands, the USA, or the UK. Fast NVMe storage delivers excellent performance, low latency, and reliable speed for your projects. Perfect for websites, applications, testing environments, and business infrastructure.
Get Ubuntu 26.04 VPS

Step 1: Install Ansible on the Control Node

Before you can automate your server, install Ansible on the control node. This is the computer or virtual machine from which you'll connect to and manage your Ubuntu 26.04 VPS.

First, update the package index:

sudo apt update

Once the package index has been updated, install Ansible:

sudo apt install ansible -y

After the installation completes, verify that Ansible is installed successfully:

ansible --version

You should see output similar to the following:

img-1793363201-6ae48d0180827.webp

This confirms that Ansible is installed and ready to use.

Unlike the managed server, the control node is where you install Ansible and run commands and playbooks. The managed BaCloud VPS doesn't need Ansible installed because the control node connects over SSH to execute tasks.

Step 2: Prepare SSH Access to the Managed Server

Ansible uses SSH to connect to the managed server and execute tasks. Configure key-based authentication from the control node to your BaCloud VPS before proceeding.

First, check whether an SSH key pair already exists on the control node:

ls -l ~/.ssh/

Look for a matching private and public key pair, such as id_ed25519 and id_ed25519.pub. If no suitable key exists, generate one:

ssh-keygen -t ed25519

Accept the default file location or specify another location if you want to keep the key separate from existing credentials. Set a passphrase if appropriate for your environment.

Next, install the public key on the managed server:

ssh-copy-id -i ~/.ssh/id_ed25519.pub username@VPS_IP

The -i option specifies the public key to install. This example assumes your key pair uses the default id_ed25519 filename. If you use a different filename, replace the path with the location of your public key.

On the first connection, SSH may ask you to verify the managed server's host-key fingerprint. Verify the fingerprint against a trusted source before accepting it. Type yes only after confirming that it matches.

The command may then prompt for the managed server user's password to install the public key. Enter the password when prompted.

Replace username with the SSH user on your BaCloud VPS and VPS_IP with its public IP address. The command adds the public key to that user's ~/.ssh/authorized_keys file. If ssh-copy-id is unavailable on the control node, the public key can be installed manually.

Test key-based authentication using the corresponding private key:

ssh -i ~/.ssh/id_ed25519 -o IdentitiesOnly=yes username@VPS_IP 

The -i option specifies the private key to use, while IdentitiesOnly=yes prevents SSH from trying other identities offered by the SSH agent. If you use a different key filename, update the path accordingly.

Once the connection succeeds, you should be logged in to the managed VPS without entering the account password. Exit the remote session:

exit

Important: Run these commands on the control node, not inside the managed VPS's shell. This step does not require changing the managed server's SSH daemon configuration. Keep the private key on the control node; install only the public key on the managed server.

Step 3: Create the Ansible Project and Inventory

Create a directory for the Ansible project and move into it:

mkdir ansible-server-setup && cd ansible-server-setup

Create an inventory file to specify the server Ansible will manage:

nano inventory.ini

Add the following configuration, replacing the VPS_IP address and username with those of your BaCloud VPS:

[managed]
VPS_IP ansible_user=username ansible_ssh_private_key_file=~/.ssh/id_ed25519

The inventory identifies the managed server by its IP address or hostname. The ansible_user variable specifies the SSH account Ansible will use to connect, while ansible_ssh_private_key_file specifies the private key used for authentication. The [managed] line defines a group, allowing you to target this server by its group name in commands and playbooks.

The private-key path must match the key used in Step 2. If you used a different filename, update the path accordingly.

Save and close the file.

Step 4: Verify Ansible Connectivity

Before creating the setup playbook, verify that Ansible can connect to the managed server and execute modules over SSH.

Run the following command from the project directory on the control node:

ansible managed -i inventory.ini -m ansible.builtin.ping

A successful result looks similar to this:

img-1793363202-6ae48d0209f91.webp

The ping module does not send an ICMP ping. It checks whether Ansible can connect to the managed server and execute a module, which normally requires a working SSH connection and Python on the remote host.

If the command fails, check the server address and SSH username in inventory.ini, confirm that SSH key-based login works, and ensure Python 3 is available on the managed server.

Step 5: Write the Server Setup Playbook

Once connectivity is confirmed, create the playbook file in the project directory:

nano setup.yml

This is where you'll define the tasks Ansible should execute on the managed server. 

Add the following configuration:

---- name: Configure the managed server  hosts: managed  become: true  become_exe: /usr/bin/sudo.ws
  tasks:    - name: Update APT package cache      ansible.builtin.apt:        update_cache: true        cache_valid_time: 3600    - name: Install baseline packages      ansible.builtin.apt:        name:          - curl          - unzip          - vim        state: present

Save and close the file.

Understanding the playbook

The playbook defines the configuration Ansible should apply to the managed server.

  • hosts: managed targets the managed inventory group defined in inventory.ini. Ansible applies the tasks to the servers listed in that group.

  • become: true enables privilege escalation, allowing Ansible to perform administrative tasks that require root permissions.

  • become_exe: /usr/bin/sudo.ws specifies the executable Ansible should use for privilege escalation. 

  • ansible.builtin.apt uses Ubuntu's APT package manager to manage software packages.

  • update_cache: true refreshes the APT package index, while cache_valid_time: 3600 allows Ansible to skip the refresh if the cache was updated within the last hour.

  • state: present ensures that curl, unzip, and vim are installed. If a package is already installed, Ansible leaves it in place rather than reinstalling it.

This playbook focuses on package management and does not change firewall rules, SSH configuration, user accounts, or reboot settings. It describes the desired state rather than issuing separate installation commands, so Ansible makes changes only when needed.

Step 6: Execute the Playbook

Run the playbook from the project directory on the control node:

ansible-playbook -i inventory.ini setup.yml --ask-become-pass

The --ask-become-pass option prompts you to enter the password required for privilege escalation on the managed server.

Ansible connects to the managed server, executes the defined tasks, and reports the results.

img-1793363202-6ae48d0289fc3.webp

The task results indicate how Ansible handled each operation:

  • ok means the task completed successfully without making changes.

  • changed means the task made a change to the managed server. In this run, Ansible installed the missing packages.

  • failed means a task did not complete successfully. Check the error message before trying again.

The PLAY RECAP summarizes the results:

  • ok=3 — Three tasks completed successfully. This includes gathering facts and the two configuration tasks.

  • changed=1 — One task made changes to the server.

  • unreachable=0 — No hosts were unreachable.

  • failed=0 — No tasks failed.

  • skipped=0 — No tasks were skipped.

  • rescued=0 — No failed tasks required recovery through a rescue block.

  • ignored=0 — No task failures were ignored.

You can also preview supported tasks without applying their changes by running the playbook in check mode:

ansible-playbook -i inventory.ini setup.yml --check --ask-become-pass

Check mode helps you review a playbook before applying it, but it doesn't guarantee an exact prediction of every change. Some tasks depend on earlier results or cannot fully simulate their actions.

Step 7: Test Idempotency

One of Ansible's key features is idempotency. This means you can run the same playbook multiple times, and Ansible will only make changes when needed to bring the server into the desired state. For example, if the required packages are already installed, Ansible won't install them again.

In Step 6, the Install baseline packages task reported changed, and the PLAY RECAP showed changed=1 because Ansible installed the missing packages.

Run the same playbook again from the project directory on the control node:

ansible-playbook -i inventory.ini setup.yml --ask-become-pass

You should see output similar to the following:

img-1793363203-6ae48d03223d4.webp

In our test, all three tasks reported ok, and the recap showed changed=0. This means Ansible completed the tasks without making any changes during the second run. The packages were already installed, and the APT package cache did not need refreshing.

Compare this result with the first run in Step 6, where changed=1 indicated that a task had made changes. The difference demonstrates idempotency: Ansible checks the server's current state and avoids repeating changes that are no longer necessary.

The results may differ if the server's state changes. For example, if the APT cache needs refreshing, that task may report a change again. Idempotency does not mean every run must report changed=0; it means Ansible makes changes when needed to maintain the desired state.

Conclusion

You've used Ansible to automate a basic server setup on Ubuntu 26.04, from establishing SSH connectivity to installing packages with a playbook. By running the playbook a second time, you've also seen how idempotency helps Ansible avoid unnecessary changes.

You can extend this playbook with additional tasks for user management, service configuration, and application deployment as your server administration needs grow.

For more in-depth tutorials, visit the BaCloud blog for helpful guides.

« Atgal