A set of tasks that must be run in the just created machines, before anything else will happen.
Go to file
Andrea Dell'Amico b86cdd81c7
Fix another typo in the hostname definition in /etc/hosts.
2026-04-13 18:22:25 +02:00
defaults system-storage-manager does not exist anymore. 2026-04-08 19:08:18 +02:00
files First commit. Some tasks from basic-system-setup have been moved here. 2026-02-23 10:47:58 +01:00
handlers First commit. Some tasks from basic-system-setup have been moved here. 2026-02-23 10:47:58 +01:00
meta First commit. Some tasks from basic-system-setup have been moved here. 2026-02-23 10:47:58 +01:00
tasks Fix another typo in the hostname definition in /etc/hosts. 2026-04-13 18:22:25 +02:00
templates First commit. Some tasks from basic-system-setup have been moved here. 2026-02-23 10:47:58 +01:00
tests First commit. Some tasks from basic-system-setup have been moved here. 2026-02-23 10:47:58 +01:00
vars Initial commit 2026-02-21 12:31:20 +01:00
.ansible-lint First commit. Some tasks from basic-system-setup have been moved here. 2026-02-23 10:47:58 +01:00
.gitignore Exclude some directories. 2026-02-23 10:40:37 +01:00
LICENSE Initial commit 2026-02-21 12:31:20 +01:00
README.md Add a task that manages held packages on deb systems. 2026-02-26 15:46:29 +01:00

README.md

os-bootstrap

An Ansible role that performs early-stage OS bootstrapping for Linux servers. It runs before any role dependencies (rsyslog, firewall, NTP, etc.) and establishes the baseline system state: locale, timezone, hostname, package prerequisites, network configuration, disk management, sysctl tuning, and PKI infrastructure.

This role is designed to be the first dependency of higher-level roles such as basic-system-setup.

Requirements

  • Ansible >= 2.9
  • Python 3 on target hosts (this role installs additional Python packages needed by subsequent Ansible tasks)

Ansible Collections Required

ansible-galaxy collection install ansible.posix
ansible-galaxy collection install community.general

Supported Platforms

  • Ubuntu 20.04 (Focal), 22.04 (Jammy), 24.04 (Noble)
  • Debian 11 (Bullseye), 12 (Bookworm)
  • RHEL / CentOS Stream / Rocky Linux / AlmaLinux 8, 9, 10

Task Descriptions

Tasks run in the following order:

Task File Description Condition
apt-hold.yml Sets or releases dpkg hold on a list of packages (Debian/Ubuntu only) apt_hold_packages non-empty
http_client_proxy.yml Configures system-wide HTTP/HTTPS proxy environment variables enable_env_proxy
ansible-python3-pkgs.yml Installs Python 3 packages required by Ansible modules always
hostname.yml Sets the system hostname from inventory explicitly_set_hostname
locale.yml Generates and configures system locales always
timezone.yml Sets the system timezone always
etchosts-customizations.yml Adds custom entries to /etc/hosts always
network-interfaces.yml Configures additional network interfaces via Netplan (Ubuntu) ubuntu_configure_additional_interfaces
additional_disks.yml Partitions, formats, and mounts additional disks additional_disks
swap_device.yml Configures a swap device swap_device
external_repos_el.yml Installs EPEL and optional elrepo repositories RedHat family only
basic_setup_el.yml EL/RedHat package install, SELinux configuration, service management RedHat family only
deb_general.yml Debian/Ubuntu package install, apt proxy, unattended upgrades, service cleanup Debian family only
sysctl.yml Writes custom kernel parameters to /etc/sysctl.d/ always
grub_cmdline_parameters.yml Appends parameters to GRUB_CMDLINE_LINUX configure_grub_cmdline_parameters
pki_dir.yml Creates the PKI directory hierarchy always
self_signed_certificate.yml Generates a self-signed certificate with mkcert letsencrypt_acme_install is defined and true
trusted_ca.yml Installs Lets Encrypt and custom CA certificates into the system trust store always
certificate_from_private_ca.yml Requests a certificate from a private CA (mkcert) mkcert_create_certificate and no Lets Encrypt

Sub-tasks for deb_general.yml

Task File Description Condition
apt_proxy.yml Configures APT proxy in /etc/apt/apt.conf.d/02proxy use_apt_proxy
dist_upgrade.yml Performs a full distribution upgrade dist_upgrade
packages_deb.yml Installs common and additional packages always
remove_unneeded_pkgs.yml Removes unwanted packages (exim, snapd, lxd, etc.) always
pubkeys.yml Manages root SSH authorized keys manage_root_ssh_keys
unattended_upgrades.yml Configures unattended-upgrades for automatic security updates always
disable_services.yml Disables unwanted services disable_some_not_needed_services

Role Variables

Package Hold Management (Debian/Ubuntu only)

Prevents APT from upgrading (or removing) specific packages by placing them on dpkg hold. Any package in the list that is not known to dpkg is skipped with a warning rather than causing the play to fail.

# List of packages to manage. Empty list → tasks are a no-op.
apt_hold_packages: []

# true  → put packages on hold (default)
# false → release hold (dpkg selection reset to 'install')
apt_hold_set: true

Example — hold the running kernel packages:

apt_hold_packages:
  - linux-image-generic
  - linux-headers-generic
apt_hold_set: true

Example — release the hold later:

apt_hold_packages:
  - linux-image-generic
  - linux-headers-generic
apt_hold_set: false

Timezone and Locale

timezone: Europe/Rome

default_locale_lang: en_US.UTF-8
default_deb_locale_messages: C.UTF-8
default_el_locale_messages: en_US.UTF-8

locales_list:
  - { name: "{{ default_locale_lang }}" }
  - { name: en_US.UTF-8 }
  - { name: en_US }
  - { name: it_IT.UTF-8 }
  - { name: it_IT }

Hostname and /etc/hosts

explicitly_set_hostname: true
domain_name: "{{ ansible_domain }}"

# Inline block of hosts entries, e.g.:
# "192.168.1.10 host1.example.com host1"
custom_etc_hosts_entries: ""
custom_etc_hosts_entries_adjunct: ""

Network Interfaces (Ubuntu / Netplan)

ubuntu_configure_additional_interfaces: false
ubuntu_configure_additional_int_dhcp_overrides: true
ubuntu_configure_additional_ints_list: []
# Example:
# ubuntu_configure_additional_ints_list:
#   - name: eth1
#     dhcp4: true

disable_ipv6: false
ipv6_sysctl_value: 1
ipv6_sysctl_file: /etc/sysctl.d/10-ipv6-disable.conf

Sysctl

sysctl_custom_file: /etc/sysctl.d/90-custom-values.conf
sysctl_opts_reload: true
sysctl_custom_file_state: present

# Only name and value are mandatory
sysctl_custom_options: []
# - name: 'net.nf_conntrack_max'
#   value: '32768'
#   sysctlfile: '{{ sysctl_custom_file }}'
#   sysctl_reload: '{{ sysctl_opts_reload }}'
#   sysctlfile_state: '{{ sysctl_custom_file_state }}'

GRUB

configure_grub_cmdline_parameters: false
grub_cmdline_additional_parameters: ""
# Example: "intel_iommu=on quiet"

Additional Disks

additional_disks: false
disks_and_mountpoints_list: []
# - mountpoint: '/data'
#   device: 'xvda3'
#   fstype: 'xfs'
#   opts: 'noatime'
#   state: 'mounted'
#   create_filesystem: true

Swap Device

swap_device: false
swap_device_name: /dev/vdxxxxx

HTTP Client Proxy

enable_env_proxy: false
env_proxy_http_host: localhost
env_proxy_http_port: "3128"
env_proxy_http_protocol: http
env_proxy_https_protocol: "{{ env_proxy_http_protocol }}"
env_proxy_http_url: "{{ env_proxy_http_protocol }}://{{ env_proxy_http_host }}:{{ env_proxy_http_port }}"
env_proxy_https_url: "{{ env_proxy_http_url }}"
env_proxy_use_authentication: false
env_proxy_username: ""
env_proxy_password: ""
no_proxy_targets:
  - ::1
  - 127.0.0.1
  - localhost

Python 3 Packages for Ansible

ansible_python3_debs:
  - python3-lxml

ansible_python3_el:
  - python3-lxml

PKI Directory and Certificates

pki_dir: /etc/pki
pki_subdirs:
  - certs
  - keys

pki_install_a_custom_ca: false

# Self-signed certificate paths (used when letsencrypt is not available)
self_signed_cert: "{{ pki_dir }}/selfsigned/cert"
self_signed_fullchain: "{{ pki_dir }}/selfsigned/fullchain"
self_signed_key: "{{ pki_dir }}/selfsigned/privkey"
self_signed_subject: /CN={{ ansible_fqdn }} self signed

# Certificate from private CA (mkcert)
mkcert_create_certificate: false
mkcert_cert_name: "{{ ansible_fqdn }}.pem"
mkcert_cert_dest_path: "{{ pki_dir }}/certs"
mkcert_key_name: "{{ ansible_fqdn }}-key.pem"
mkcert_key_dest_path: "{{ pki_dir }}/keys"
mkcert_dsn_and_ip_list: "{{ ansible_fqdn }} {% for ip in ansible_all_ipv4_addresses %}{{ ip }} {% endfor %}"
mkcert_ca_host: localhost

Trusted CA Certificates

trusted_ca_el_anchors_path: /etc/pki/ca-trust/source/anchors
trusted_ca_deb_path: /usr/local/share/ca-certificates

# Let's Encrypt intermediate CAs (normally already trusted by the OS)
trusted_ca_letsencrypt_install: false
trusted_ca_letsencrypt_ca_certificates_url: https://letsencrypt.org/certs

# Additional custom CAs to install
trusted_ca_additional_ca_files: []
# - { ca_url: 'https://example.com/foo-ca.pem', ca: 'foo-ca.pem', name: 'foo-ca' }

EL/RedHat — External Repositories

centos_install_epel: true
centos_epel_repo_url: epel-release
centos_install_release_scl: false

rh_install_elrepo: false

EL/RedHat — Basic Setup

centos_pkg_state: latest
centos_packages_cleanup: true

# SELinux
selinux_policy_type: targeted
selinux_policy_state: enforcing
centos_selinux_daemons_dump_core: false

# Services
centos_disable_avahi: true
centos_remove_avahi: false
centos_disable_networkmanager: false
centos_remove_networkmanager: false
centos_services_to_be_disabled:
  - acpid

# DNS (optional, for static resolver configuration)
centos_set_dns_servers: false
dns1: 208.67.220.220
dns2: 208.67.222.222

# Root SSH keys
manage_root_ssh_keys: true

Debian/Ubuntu — Package Management

use_apt_proxy: false
apt_proxy_url: http://localhost:3128

dist_upgrade: false

pkg_state: present
common_packages:
  - acl
  - curl
  - wget
  - htop
  - vim-tiny
  - psmisc
  - tcpdump
  - lsof
  - strace
  - rsync
  - unzip
  - tree
  - bash-completion
  - sudo
  - less
  # ... see defaults/main.yml for the full list

# Additional packages (define in your playbook or group_vars)
# additional_packages:
#   - pkg1
#   - pkg2

Debian/Ubuntu — Package Cleanup

cleanup_base_packages: true
base_packages_to_remove:
  - ppp
  - at
  - snapd

cleanup_exim_email_server: true
disable_apport_service: true
ubuntu_remove_lxd: true

Debian/Ubuntu — Unattended Upgrades

unatt_allowed_origins:
  - ${distro_id}:${distro_codename}-security
unatt_autofix: "true"
unatt_minimalsteps: "false"
unatt_install_on_shutdown: "false"
unatt_email_on_error: "false"
unatt_autoremove: "true"
unatt_autoreboot: "false"
unatt_autoreboot_time: now

Debian/Ubuntu — Service Management

disable_some_not_needed_services: false
services_to_be_disabled:
  - rpcbind
  - atd
  - acpid

Dependencies

None. This role is intentionally dependency-free so it can run before any other role.

Example Playbook

Normally you do not invoke os-bootstrap directly — it is pulled in automatically as a dependency. If you need to run it standalone:

---
- hosts: servers
  become: true
  roles:
    - role: adellam.os_bootstrap
      vars:
        timezone: Europe/Rome
        explicitly_set_hostname: true
        disable_ipv6: false
        sysctl_custom_options:
          - name: net.nf_conntrack_max
            value: "65536"

With proxy and extra disk

---
- hosts: servers
  become: true
  roles:
    - role: adellam.os_bootstrap
      vars:
        enable_env_proxy: true
        env_proxy_http_host: proxy.example.com
        env_proxy_http_port: "3128"
        additional_disks: true
        disks_and_mountpoints_list:
          - mountpoint: /data
            device: sdb1
            fstype: xfs
            opts: noatime
            state: mounted
            create_filesystem: true

EL with custom SELinux and EPEL

---
- hosts: el_servers
  become: true
  roles:
    - role: adellam.os_bootstrap
      vars:
        centos_install_epel: true
        selinux_policy_state: enforcing
        selinux_policy_type: targeted
        centos_pkg_state: latest

Testing

source ~/ansible/ansible6/bin/activate
ansible-lint

Basic test playbook is in tests/test.yml.

License

EUPL-1.2

Author Information

Andrea DellAmico andrea.dellamico@isti.cnr.it

ISTI-CNR, Pisa, Italy