archible provides a complete, declarative desktop provisioning setup for Arch Linux, powered by Ansible.
- Headless Provisioning: Set up systems from scratch, even on non-bootable targets, using systemd-nspawn containers.
- Flexible Execution: Run playbooks from ArchISO, over SSH on a running system, or locally on the target machine.
- Extensible: A rich ecosystem of roles and plugins covers every aspect of system provisioning, from environment variables to dynamic user directory layout.
- Deep Customization: Fine-tune advanced system settings including systemd, udev, ALSA, GSettings, and other system quirks.
- Highly Configurable: Tailor setups with roles, variables and templates in an intuitive namespacing hierarchy.
Full-sized versions are available at ./roles/aa-global/aa-shared/files.
First, clone the repository to your local machine.
git clone https://github.com/example/archibleThis project follows a standard Ansible inventory structure. For an introduction to this concept, see Building Ansible inventories in the official documentation.
The easiest way to get started is by copying and modifying the provided example:
- Host Declaration:
./inventories/example.yml - Group Variables:
./inventories/group_vars/example/*.yml
In order to get started with group variables, explore the following sections:
- Hierarchy and Notations: Explains the project's variable and role organization.
- Roles: Details all available roles and their references.
- Common Questions: Includes quick tips and common hacks.
Note: Many variables come with reasonable defaults, but pay close attention to the most fundamental ones, such as
aa_wipe(global wiping policy) and user-relatedca_*,fa_*variables. They are undefined by default, but not in the case of example inventory.
The provisioning process is divided into four distinct stages, each corresponding to a specific machine state.
Here are some helpful resources from the Ansible documentation for basic exposure:
- Special Variables
- Understanding privilege escalation:
become ansible.builtin.sshconnection- Protecting sensitive data with Ansible vault
The Prelude stage performs disk partitioning and bootstraps the base system. It can be run from an ArchISO or an existing Arch Linux installation that is provisioning another disk.
main:
hosts:
main_prelude:
ansible_connection: local
#
# If running as a regular user, also provide sudo password.
#
ansible_become_password: mypassThe Catalyst stage sets up the base system after it has been bootstrapped. At this point, the root filesystem is mounted , but lacks networking and users. The systemd-nspawn connection is recommended as the system is not yet bootable.
main:
hosts:
main_catalyst:
#
# Use systemd-nspawn connection plugin.
#
ansible_connection: foundation.connection.nspawn
#
# Root is mounted at /mnt.
#
foundation_nspawn_root: /mnt
#
# Note that --resolv-conf is disabled to allow configuring it.
#
foundation_nspawn_args: >-
--quiet --register=no --as-pid2 --pipe
--hostname=mainframe --machine=mainframe
--resolv-conf=off --timezone=off --link-journal=noThe Apex stage is the heart of provisioning for most tasks. It handles everything from bootloader installation to application setup.
By now, the system has network access and a user account. It can be run via systemd-nspawn, SSH, or locally on an existing machine.
main:
hosts:
#
# Example with systemd-nspawn connection plugin.
#
main_apex:
#
# Specify the created previously user.
#
ansible_user: user
ansible_become_password: mypass
ansible_connection: foundation.connection.nspawn
foundation_nspawn_root: "{{ ba_mount_point }}"
#
# Bind the host's resolv.conf to use its network settings.
#
foundation_nspawn_args: >-
--quiet --register=no --as-pid2 --pipe
--hostname={{ ba_hostname }} --machine={{ ba_hostname }}
--resolv-conf=bind-host --timezone=off --link-journal=no
#
# Example using SSH.
#
main_apex_ssh:
ansible_user: user
ansible_become_password: mypass
ansible_host: 192.168.0.100The Extra stage is a namespace for additional tasks that do not directly affect provisioning and are intended for daily use, such as cleanup operations and backups. From a host declaration perspective, it is identical to the Apex stage.
main:
hosts:
main_extra:
ansible_connection: localThe installation environment needs to be prepared first:
- (Optional, ArchISO) Increase Root Filesystem Size:
- Needed only if planning to use complete Ansible community distribution, additional packages and/or heavy backups.
- At boot, press E to edit the boot entry and add
cow_space_size=3Gto the kernel parameters, and boot the system. - Alternatively, after booting, run
mount -o remount,size=3G /run/archiso/cowspace. - See the Arch Wiki for more info.
- Install Ansible and Dependencies:
- Required:
arch-install-scripts ansible-core whois util-linux coreutils gptfdisk. - Optional:
ansibleif the playbook is modified to use community modules. - File System Tools:
btrfs-progs e2fsprogs dosfstools xfsprogs ntfs-3g exfatprogs f2fs-tools.
- Required:
- Transfer the Project:
- Enable the OpenSSH daemon (already enabled for ArchISO) and set the password via
passwd. - Alternatively, use
unison,rclone, or any other solution.
- Enable the OpenSSH daemon (already enabled for ArchISO) and set the password via
Use the command below to run the playbook. It is also possible to skip certain parts of the run with --skip-tags.
ansible-playbook playbook.yml [--skip-tags apex-apps-browsers]That is it! For next steps, consired the following:
- Explore ansible-playbook options.
- Develop new tasks and roles or extend existing ones.
- Find some answers in Common Questions section.
This project uses a lexicographical prefixing system to organize roles. This system follows a few simple rules:
- Two Levels of Directories: Roles are organized into a two-level directory structure.
- Namespace Directories (First Level):
- Format:
xa-name(e.g.,ba-prelude,da-apex-prep). - The two-letter prefix defines a namespace and follows the natural execution flow (e.g.,
ba-runs beforeca-). - The prefix
aa-is reserved for global items shared across all roles.
- Format:
- Role Directories (Second Level)::
- Format:
yx-name(e.g.,bb-partition,dc-bootloader). - Defines a specific role within a namespace.
- A prefix ending in a- (e.g.,
ba-shared) is reserved for shared variables and tasks used by other roles.
- Format:
While this approach adds some initial complexity, it offers several advantages over a more conventional organization:
- Clear Execution Order: Lexicographical sorting clearly shows a role's position in the execution pipeline.
- Efficient Navigation: Shell autocomplete (e.g.,
cd r*/da<TAB>) makes finding roles quick and easy. - Simplified Playbooks: Eliminates the need for boilerplate
include_roleorimport_roleand avoids issues with nested roles tags. - Clean Segregation: A clear separation between shared and role-specific items.
- Scalability: Supports complex projects without needing custom role search paths or Ansible collections.
- Description: A global namespace used throughout the entire playbook by other roles.
- Description: Defines the global namespace variables.
- Description: Handles tasks for the Prelude Stage.
- Description: Defines the shared prelude stage namespace.
- Exports:
ba_headlessba_mount_pointba_hostnameba_coresba_archba_locale
- Description: Partitions the disk using gptfdisk and mkfs and renders the fstab file.
- Tags:
prelude-partition-mainprelude-partition-fstab
- References:
aa_newlineba_mount_point
- Description: Bootstraps the base system with pacstrap.
- Tags:
prelude-bootstrap-depsprelude-bootstrap-mainprelude-bootstrap-firstboot
- References:
ba_hostnameba_localeba_mount_point
- Description: Handles tasks for the Catalyst Stage.
- Description: Defines the shared catalyst stage namespace.
- Description: Configures networking with systemd-networkd and sets DNS servers.
- Tags:
catalyst-network-maincatalyst-network-dnscatalyst-network-wireless
- References:
aa_wipeba_headlessba_wirelessca_dns_ipca_dns_sni
- Description: Sets up the system locale, sudo access, and creates a new user.
- Tags:
catalyst-user-localecatalyst-user-privilegecatalyst-user-skel
- References:
aa_wipeba_localeca_groupsca_nameca_password
- Description: Prepares the base system for package management and booting.
- Description: Defines the shared apex preparation stage namespace.
- Description: Configures pacman, repositories, makepkg, and paru AUR helper.
- Tags:
apex-prep-packaging-mainapex-prep-packaging-mirrorsapex-prep-packaging-buildapex-prep-packaging-addons
- References:
aa_wipeba_headless
- Description: Installs the booster initramfs generator, microcode and kernel, and configures the kernel-install suite along with the systemd-boot bootloader.
- Tags:
apex-prep-bootloader-suiteapex-prep-bootloader-initrdapex-prep-bootloader-ucodeapex-prep-bootloader-kernelapex-prep-bootloader-main
- References:
aa_wipeba_headless
- Description: Configures the systemd init system, journald logging, udev devices, and related functionality.
- Description: Defines the shared apex system stage namespace.
- Description: Configures systemd services, hugepages, sleep behavior, and the virtual console.
- Tags:
apex-system-init-mainapex-system-init-consoleapex-system-init-hugepage
- References:
aa_wipeba_headless
- Description: Configures logind, journald, coredump, and timesyncd.
- Tags:
apex-system-core-sessionapex-system-core-logapex-system-core-dumpapex-system-core-timesync
- References:
aa_wipeba_headless
- Description: Configures TRIM support for SSDs and a udev module blacklist.
- Tags:
apex-system-devices-blacklistapex-system-devices-trim
- References:
aa_newlineaa_relindentaa_wipeba_headless
- Description: Creates essential system directories.
- Tags:
apex-system-dirs-mainapex-system-dirs-applications
- References:
aa_wipeea_dir_stubs
- Description: Prepares the user's skeleton directory and environment.
- Description: Defines the shared apex user stage namespace.
- Description: Configures user authorization, pam_env, and login profiles.
- Tags:
apex-user-skel-authapex-user-skel-envapex-user-skel-profile
- References:
aa_wipeca_namefa_dir_binfa_dir_homefa_dir_subs
- Description: Configures XDG user directories and MIME associations.
- Tags:
apex-user-xdg-baseapex-user-xdg-dirsapex-user-xdg-mime
- References:
aa_wipeca_namefa_dir_binfa_dir_cachefa_dir_configfa_dir_homefa_dir_sharefa_dir_statefa_dir_xdgfa_homedirs
- Description: Provisions the complete desktop experience.
- Description: Defines the shared apex desktop stage namespace.
- Description: Configures the ly login manager.
- Tags:
apex-desktop-login-main
- References:
aa_wipeba_headless
- Description: Configures the graphics stack.
- Tags:
apex-desktop-graphics-nvidiaapex-desktop-graphics-intel
- References:
ba_headless
- Description: Configures the Pipewire media framework.
- Tags:
apex-desktop-media-driverapex-desktop-media-frameworkapex-desktop-media-setupapex-desktop-media-cards
- References:
aa_newlineaa_relindentaa_wipeba_headlessca_namefa_dir_configfa_dir_state
- Description: Configures the XDG Desktop Portal.
- Tags:
apex-desktop-portal-main
- References:
aa_wipeba_headlessca_namefa_dir_config
- Description: Configures the look and feel for GTK and Qt applications, including themes, fonts, and icons.
- Tags:
apex-look-fontsapex-look-iconsapex-look-qtapex-desktop-look-gtk
- References:
aa_wipeca_namefa_dir_cachefa_dir_configfa_dir_homefa_dir_subsga_fonts_emojiga_fonts_emoji_packagega_fonts_icon_packagega_fonts_monoga_fonts_mono_packagega_fonts_mono_sz_smallga_fonts_sansga_fonts_sans_packagega_fonts_sans_sz_smallga_fonts_serifga_fonts_serif_packagega_fonts_serif_sz_smallga_icons_launcherga_icons_launcher_packagega_icons_launcher_urlga_icons_mainga_icons_main_packagega_icons_main_url
- Description: Configures the rofi application launcher.
- Tags:
apex-desktop-launcher-main
- References:
aa_wipeca_namefa_dir_cachefa_dir_configga_fonts_mono_sz_bigga_icons_launcherga_presetga_scalega_scalingga_scaling_ethalonga_space_bigga_space_mediumga_space_smallga_space_thin
- Description: Configures an X11 desktop suite with polybar, dunst, picom, and herbstluftwm.
- Tags:
apex-desktop-x11-serverapex-desktop-x11-portalapex-desktop-x11-barapex-desktop-x11-notifyapex-desktop-x11-compositorapex-desktop-x11-wm
- References:
aa_dquoteaa_relindentaa_wipeba_headlessca_namefa_dir_configfa_dir_sharefa_dir_xdgga_fonts_iconga_fonts_icon_off_mediumga_fonts_icon_sz_mediumga_fonts_mono_sz_bigga_fonts_sans_off_mediumga_fonts_sans_sz_mediumga_icons_launcherga_keyboard_layoutga_keyboard_modelga_keyboard_optionsga_keysga_monitorsga_monitors_stackga_mouse_accelga_mouse_speedga_netnsga_parse_bindsga_popup_heightga_popup_iconga_popup_widthga_presetga_scalega_scalingga_scaling_ethalonga_space_bigga_space_mediumga_space_smallga_space_thinga_stackga_uptimega_workspace_prefix
- Description: Configures a Wayland desktop suite with waybar, mako, and Hyprland.
- Tags:
apex-desktop-wayland-seatapex-desktop-wayland-barapex-desktop-wayland-notifyapex-desktop-wayland-portalapex-desktop-wayland-compositor
- References:
aa_relindentaa_wipeba_headlessca_nameea_dir_stubsfa_dir_configfa_dir_homefa_dir_subsfa_dir_xdgga_fonts_iconga_fonts_icon_off_mediumga_fonts_icon_sz_mediumga_fonts_icon_sz_smallga_fonts_mono_off_bigga_fonts_mono_sz_bigga_fonts_sansga_fonts_sans_off_mediumga_fonts_sans_sz_mediumga_icons_launcherga_icons_mainga_keyboard_layoutga_keyboard_modelga_keyboard_optionsga_keysga_monitorsga_monitors_stackga_mouse_accelga_mouse_speedga_netnsga_parse_bindsga_popup_heightga_popup_iconga_popup_widthga_presetga_scalega_scalingga_scaling_ethalonga_space_bigga_space_mediumga_space_smallga_space_thinga_uptimega_workspace_prefix
- Description: Configures the user working environment, including shell, version manager, keyring, and other tools.
- Description: Defines the shared apex environment stage namespace.
- Description: Generates a new GnuPG keyring or restores one from a persistent location. Generated key metadata is
placed at
$GNUPGHOME/unattended-keys.json. - Tags:
apex-env-gpg-servicesapex-env-gpg-layoutapex-env-gpg-mainapex-env-gpg-restoreapex-env-gpg-generateapex-env-gpg-profile
- References:
aa_newlineaa_persistaa_wipeba_headlessca_dns_ipca_namefa_dir_homefa_dir_subsfa_layoutha_gpg_user_layout
- Description: Configures the OpenSSH client.
- Tags:
apex-env-ssh-main
- References:
aa_wipeba_headlessca_namefa_dir_home
- Description: Configures the mise version manager.
- Tags:
apex-vermen-main
- References:
aa_wipeba_archca_namefa_dir_cachefa_dir_configfa_dir_homefa_dir_sharefa_dir_statefa_layout
- Description: Configures the wezterm terminal emulator along with a nushell, atuin, carapace, and zoxide.
- Tags:
apex-env-shell-terminalapex-env-shell-promptapex-env-shell-fetchapex-env-shell-historyapex-env-shell-completionapex-env-shell-navigatorapex-env-shell-main
- References:
aa_newlineaa_relindentaa_wipeca_namefa_dir_cachefa_dir_configfa_dir_homefa_dir_sharefa_dir_subsfa_layoutga_ansiga_fonts_emojiga_fonts_iconga_fonts_monoga_fonts_mono_sz_mediumga_keysga_parse_ansiga_parse_bindsga_presetga_scalega_scalingga_scaling_ethalonga_space_mediumga_space_smallga_space_thin
- Description: Configures a user hub for note-taking and time tracking.
- Tags:
apex-env-hub-restoreapps-env-hub-notesapps-env-hub-drawapps-hub-time
- References:
aa_persistaa_wipeca_namefa_dir_configfa_dir_xdg
- Description: Provisions a secret user vault with gopass.
- Tags:
apex-env-vault-layoutapex-env-vault-mainapex-env-vault-restore
- References:
aa_persistaa_wipeca_namefa_dir_cachefa_dir_configfa_dir_homefa_layoutha_vault_user_layout
- Description: Installs various applications for different purposes.
- Description: Defines the shared apex apps stage namespace.
- Description: Provisions various text editors.
- Tags:
apex-apps-editors-helixapex-apps-editors-microapex-apps-editors-sublimeapex-apps-editors-vscodeapex-apps-editors-zed
- References:
aa_dquoteaa_newlineaa_relindentaa_wipeca_namefa_dir_cachefa_dir_configfa_dir_homefa_dir_sharefa_dir_subsfa_layoutga_fonts_emojiga_fonts_iconga_fonts_monoga_fonts_mono_sz_bigga_fonts_mono_sz_smallga_keysga_parse_bindga_parse_bindsga_scalega_scalingia_vscode_user_layout
- Description: Performs unattended installation and configuration of JetBrains IDEs.
- Tags:
apex-apps-jetbrains-contextapex-apps-jetbrains-javaapex-apps-jetbrains-agentapex-apps-jetbrains-distapex-apps-jetbrains-config
- References:
aa_newlineaa_relindentaa_wipeba_archca_info_shortca_namefa_dir_cachefa_dir_configfa_dir_homefa_dir_sharefa_dir_statefa_layout
- Description: Provisions various Gecko and Chromium-based browsers
- Tags:
apex-apps-browsers-geckoapex-apps-browsers-chromiumapex-apps-browsers-default
- References:
aa_newlineaa_wipeba_headlessca_namefa_layoutia_browsers
- Description: Provisions the vopono network namespace VPN launcher.
- Tags:
apex-apps-vpn-netns
- References:
aa_wipeca_namefa_dir_config
- Description: Provisions various file management applications.
- Tags:
apex-apps-files-managerapex-apps-files-archiveapex-apps-files-usageapex-apps-files-editapex-apps-files-viewapex-apps-files-searchapex-apps-files-watchapex-apps-files-renameapex-apps-files-list
- References:
aa_wipeca_namefa_dir_configfa_dir_homefa_dir_sharefa_dir_statefa_dir_subsfa_layoutga_ansiga_parse_ansiga_preset
- Description: Provisions various code-related utilities.
- Tags:
apex-apps-code-gitapex-apps-code-diffapex-apps-code-locapex-apps-code-buildapex-apps-code-runtimeapex-apps-code-containerapex-apps-code-databaseapex-apps-code-llmapex-apps-code-api
- References:
aa_wipeca_namefa_dir_binfa_dir_cachefa_dir_configfa_dir_homefa_dir_sharefa_layout
- Description: Provisions miscellaneous applications for system, data pipelines, media, and other uses.
- Tags:
apex-apps-misc-integrationapex-apps-misc-mediaapex-apps-misc-systemapex-apps-misc-dataapex-apps-misc-networkapex-apps-misc-mailapex-apps-misc-fun
- References:
aa_wipeba_headlessca_namefa_dir_configfa_dir_sharefa_layoutia_spotify_launcher_user_layoutia_tutanota_user_layout
- Description: Sets up complete developer environments for various languages, including project templates and build configurations.
- Description: Defines the shared apex dev stage namespace.
- Description: Provisions a container development environment.
- Tags:
apex-dev-containers-runtimeapex-dev-containers-dockerapex-dev-containers-k8s
- References:
aa_wipeba_headlessca_namefa_dir_cachefa_dir_configfa_dir_homefa_dir_sharefa_dir_subsfa_dir_xdgfa_layout
- Description: Provisions a C++ developer environment based on CMake, vcpkg, Conan, Meson, and Bazel.
- Tags:
apex-dev-cpp-mainapex-dev-cpp-cmakeapex-dev-cpp-vcpkgapex-dev-cpp-conanapex-dev-cpp-mesonapex-dev-cpp-bazel
- References:
aa_relindentaa_wipeca_info_fullca_info_mailca_namefa_dir_binfa_dir_cachefa_dir_homefa_dir_sharefa_dir_statefa_dir_subsfa_dir_xdgfa_layoutja_editorconfigja_gitignoreja_readme
- Description: Provisions a Rust developer environment.
- Tags:
apex-dev-rust-fixapex-dev-rust-main
- References:
aa_wipeba_coresca_info_fullca_info_mailca_namefa_dir_homefa_dir_statefa_dir_subsfa_dir_xdgfa_layoutja_editorconfigja_gitignoreja_readme
- Description: Provisions a Java (Gradle/Maven) developer environment.
- Tags:
apex-dev-java-mainapex-dev-java-contextapex-dev-java-mavenapex-dev-java-gradle
- References:
aa_wipeba_coresca_info_fullca_info_mailca_namefa_dir_homefa_dir_statefa_dir_subsfa_dir_xdgfa_layoutja_editorconfigja_gitignoreja_readme
- Description: Provisions a .NET developer environment.
- Tags:
apex-dev-dotnet-layoutapex-dev-dotnet-mainapex-dev-dotnet-template
- References:
aa_relindentaa_wipeca_info_fullca_namefa_dir_cachefa_dir_homefa_dir_sharefa_dir_statefa_dir_subsfa_dir_xdgfa_layoutja_editorconfigja_gitignoreja_readme
- Description: Provisions a Go developer environment.
- Tags:
apex-dev-go-contextapex-dev-go-mainapex-dev-go-template
- References:
aa_wipeca_namefa_dir_cachefa_dir_homefa_dir_sharefa_dir_subsfa_dir_xdgfa_layoutja_editorconfigja_gitignoreja_readme
- Description: Provisions a Python developer environment.
- Tags:
apex-dev-python-mainapex-dev-python-uvapex-dev-python-ipythonapex-dev-python-ruffapex-dev-python-mypyapex-dev-python-blackapex-dev-python-pdmapex-dev-python-poetryapex-dev-python-template
- References:
aa_wipeca_info_fullca_info_mailca_namefa_dir_cachefa_dir_configfa_dir_homefa_dir_sharefa_dir_statefa_dir_subsfa_dir_xdgfa_layoutja_editorconfigja_gitignoreja_readme
- Description: Provisions a JavaScript developer environment.
- Tags:
apex-dev-javascript-contextapex-dev-javascript-nodeapex-dev-javascript-denoapex-dev-javascript-bun
- References:
aa_wipeca_info_fullca_info_mailca_namefa_dir_cachefa_dir_configfa_dir_homefa_dir_sharefa_dir_statefa_dir_subsfa_dir_xdgfa_layoutja_editorconfigja_gitignoreja_readme
- Description: Handles tasks for the Extra Stage.
- Description: Defines the shared extra stage namespace.
- Description: Performs cleanup operations for various user applications and system caches.
- Tags:
extra-cleanup-trimextra-cleanup-packagingextra-cleanup-cacheextra-cleanup-trashextra-cleanup-homeextra-cleanup-geckoextra-cleanup-chromiumextra-cleanup-spotify
- References:
aa_wipeba_headlessca_namefa_dir_cachefa_dir_homefa_dir_sharefa_layoutia_browsersia_spotify_launcher_user_layoutia_tutanota_user_layoutia_vscode_user_layoutza_chromium
- Description: Performs backups of user application data and critical files to a persistent storage location.
- Tags:
extra-backup-gpgextra-backup-hubextra-backup-vault
- References:
aa_newlineaa_persistfa_dir_homefa_dir_xdgfa_layoutha_gpg_user_layoutha_vault_user_layout
This connection plugin allows Ansible to connect to the control node via a systemd-nspawn container.
The connection is stateless and does not boot the underlying system. Think of it as an advanced chroot.
Variables:
foundation_nspawn_root: Path to the root directory to start container from. (Default:/mnt)foundation_nspawn_exe: Path to the systemd-nspawn executable binary. (Default:/usr/bin/systemd-nspawn)foundation_nspawn_args: Additional arguments to pass to the systemd-nspawn. No need to manually specify (Default:--quiet --as-pid2 --pipe)
Note: The
--directoryand--userflags are managed by the plugin automatically and should not be specified infoundation_nspawn_args.
This action plugin provides a convenient, declarative interface for batched file management.
It was created to enforce strict filesystem rules on a managed node without the verbose boilerplate created by built-in modules (it may took up to 100+ lines for simple repetitive operations).
The plugin first performs optional wiping operations (with optimizations to avoid redundant wipes), then creates all necessary directories, and finally performs the main file operations (copy, template, link, etc.).
Parameters:
base: Base path for all relative targets.wipe: Default wipe policy for all targets. Eitherneveroralways.create:- For directory targets, create the provided directory.
- For file targets, create an empty file if no other file operation is specified.
perms: Default permissions for targets in the formatdmod:fmod:owner:group.dmod,fmod: A numerical chmod mode for directory and file targets, respectively.owner,group: The user and group names.
Variable targets:
dir: An absolute or relative path (starting with./) to a directory target.file: An absolute or relative path (starting with./) to a file target.copy: The path of a file on the managed node to copy to the target.content: A string to write to the file target.template: A template to render to the file target.link: The path of a filesystem object to link to the target.url: A remote URL to download to the file target.wipe,create: Overrides the global wipe and create parameters for this target.perms: Overrides the global permissions. Format:mod[:owner[:group]].
Examples:
- name: Example batched file management
foundation.files.install:
#
# Specify defaults first.
#
wipe: always
perms: 755:644:user:user
base: /home/user/docs/example
vars:
targets:
#
# Wipe and then touch /home/user/docs/example/empty with 644:user:user.
#
- file: ./empty
create: true
#
# Ensure that directory /home/user/docs/example/empty exists with 755:user:user.
#
- dir: ./directory
wipe: never
create: true
#
# Wipe and then write /home/user/docs/hello with 755:user:user.
#
- file: /home/user/docs/hello
perms: "755"
content: |
Hello, World!This action plugin downloads and extracts a remote archive to a specified destination, with an optional existence guard.
It is backed by bsdtar, which can handle tar (with many compression formats), zip, 7-zip, and others.
Note: File ownership is not strictly verified. If the download is skipped because the
createscondition is met, no other changes are made.
Parameters:
url: A link to the remote archive to download.dest: The destination directory. Note that the directory is not created automatically.perms: A permission expression in the form ofowner:group, which specifies the owner and group of all extracted files.strip: An optional number of leading path components to strip from the archive.creates: A list of relative (./) or absolute paths. The existence of these paths is tested before downloading to prevent re-downloading, and after the download to ensure the required files were created.
Examples:
- name: Download and extract bat
foundation.files.net_archive:
url: https://github.com/sharkdp/bat/releases/download/v0.25.0/bat-v0.25.0-x86_64-unknown-linux-gnu.tar.gz
dest: /tmp
perms: "owner:group"
strip: 1
creates:
- ./batThis action plugin transfers persistence files from the control node to the managed node, either by piping to a shell command or by unpacking an archive.
Persistence items are represented as a two-component directory (key/value) on the control node. The plugin first
checks for the existence of all requested persistent items and skips the action if any are missing. This allows for
effective fallbacks without using ignore_errors: true.
Parameters:
base: The absolute base directory for all operations, unless overridden.persist: The persistence directory on the control node.
Variable shells:
key: The two-component key of the persistence item.dir: Optional working directory for the command. If not specified, it falls back to thebaseparameter.cmd: The shell command that receives the raw binary stream of the persistence item via its standard input.
Variable archives:
key: The two-component key of the persistence item.dir: Optional working directory for the command. If not specified, it falls back to thebaseparameter.perms: A permission declaration in the form ofowner:group.
Examples:
- name: Restore files from persistence
foundation.persist.from:
base: /opt
persist: "{{ ansible_config_file | ansible.builtin.dirname }}/persist"
vars:
shells:
- key: mykey/shell
dir: ./directory
cmd: echo Hello!
archives:
- key: mykey/archive
dir: /tmp
perms: "owner:group"
register: _persist_from
- name: Create default files if restoration was skipped
when: _persist_from is skipped
ansible.builtin.debug:
msg: Persistence item not found; creating default files instead.This action plugin transfers files from the managed node to the control node, saving them as persistence items. It can capture output from shell commands or create archives from specified paths.
Note: The plugin requires the persistence directory to already exist on the control node. It uses the permissions of this directory for any newly created persistence items. This allows to avoid permissions issues when running ansible with superuser rights.
Parameters:
base: The absolute base directory for all operations, unless overridden.persist: The persistence directory on the control node.
Variable shells:
key: The two-component key for the persistence item.dir: Optional working directory for the command. If not specified, it falls back to thebaseparameter.cmd: The shell command that outputs a raw binary stream to be saved.
Variable archives:
key: Two component key of persist item.dir: Optional working directory for the command. If not specified, it falls back to thebaseparameter.include: A list of filesystem patterns to include (matched withfind). Use['*']to include all files.
Examples:
- name: Save file to persistance
foundation.persist.to:
base: /opt
persist: "{{ ansible_config_file | ansible.builtin.dirname }}/persist"
vars:
shells:
- key: some/path
cmd: echo Hello
archives:
- key: mykey/archive
dir: /tmp
include:
- qwertyThis action plugin provides convenient access to the metadata of GnuPG keys previously generated by the ha-apex-env/hb-gpg role.
It reads the custom unattended-keys.json file inside the user's GNUPGHOME. It accesses required key information,
consisting of the key fingerprint and keygrip.
Parameters:
home: The user's home directory.layout: The user directory layout, eitherxdgordot.
Variable keys:
- A list of key names to fetch. To fetch all keys, use
['all'].
Examples:
- name: Get GPG key info
foundation.persist.gpg:
home: /home/user
layout: dot
vars:
keys:
- mykey
register: _persist_gpg
#
# Results are placed into the `gpg` dictionary, for easy iteration and filters integration.
#
- name: Print key information
ansible.builtin.debug:
#
# Available fields: `fingerprint`, `keygrip`.
#
msg: Fingerprint for mykey is {{ _persist_gpg.gpg.mykey.fingerprint }}This action plugin manipulates the GUID Partition Table (GPT) of a disk.
It is backed by gptfdisk, and provides a concise, declarative syntax for managing the entire partitioning routine, including formatting new filesystems and optionally mounting them.
The plugin offers advanced validation, supports check mode, and integrates seamlessly with orchestration and bootstrap roles by returning structured metadata about all configured partitions.
Note: The exact set of prerequisites varies by layout and may require additional packages to be installed. These typically include userspace tools specific to the selected filesystem, such as btrfs-progs or xfsprogs.
Parameters:
disk: The absolute path to the block device to partition. It may also be provided as a symbolic link (e.g.,/dev/disk/by-id/ata-SSD).base: The absolute path that serves as the root for all mount points.
Variable layout:
name: The label assigned to the partition and its filesystem (when applicable).table: A block describing partition table details.type: The partition type, given as an alias (efi,root), a short gptfdisk type code (ef00,8300), or an explicit GUID expression (C12A7328-F81F-11D2-BA4B-00A0C93EC93B). Refer to the source code for a complete mapping.size: The size of the partition. Supports numeric units (100G,250MB), a percentage of the disk (10%), or a specialautovalue that fills the remaining free space.
fs: An optional block describing filesystem creation.type: The filesystem name to create.exec: An optional template string that replaces the default command used for formatting. The following variables are supported:$PART- the quoted path to the partition block device$NAME- the quotednameparameter$FS- thetypeparameter$OPTS- a set of recommended options based on the filesystem type
mount: An optional block describing the partition mounting.path: The absolute path to mount the filesystem to. Processed as provided, but prefixed with thebaseparameter during the actual mount operation. This allows to work with canonical paths (e.g.,/boot) without hard-coding a prefix. Must benonefor swap partitions.mode: The optional access mode and ownership of the mount point, in the formatmod:owner:group. Default:755:root:root.opts: An optional template string that forms mount options. Supports the$OPTSvariable, which expands to recommended options based onfs.type.exec: An optional template string overriding the default mount command. Supports the following variables:$SRC: the quoted path to the partition block device$DST: the quoted destination directory, composed ofbaseandpath.$OPTS: the evaluated mount options (eitheroptstemplate or default fallback).
Examples:
#
# This will create:
# - PARTLABEL=MY-ESP, SIZE=(1000 ^ 3 bytes)
# - PARTLABEL=MY-ROOT, SIZE=(remaining free space)
#
# This will format:
# - mkfs.vfat -F 32 -n "MY-ESP" /dev/sd?1
# - mkfs.ext4 -F -L "MY-ROOT" -t ext4 -O fast_commit,...,extra_isize /dev/sd?2
#
# This will mount:
# - mount -o defaults,strictatime /dev/sd?2 /mnt
# - mkdir -p /mnt/efi
# - mount -o async,noatime,...,fmask=0023,dmask=0022 /dev/sd?1 /mnt/efi
#
# Note that the order of partition during the mount stage is automatically
# adjusted to ensure a correct sequence, even when nested mount points exist.
#
- name: Partition the disk
foundation.storage.partition:
disk: /dev/disk/by-id/ata-VendorC
base: /mnt
vars:
layout:
- name: MY-ESP
table:
type: efi
size: 1G
fs:
type: vfat
mount:
path: /efi
- name: MY-ROOT
table:
type: root
size: auto
fs:
type: ext4
exec: mkfs.ext4 -F -L $NAME -O $OPTS,extra_isize $PART
mount:
path: /
opts: defaults,strictatime
#
# Please note that metadata paths are not influenced by the `base` parameter.
#
register: _my_disk_layout
#
# `submission` metadata contains information about each partition.
#
# - MY-ESP is using fat
# - MY-ROOT is using ext4
#
- name: Print the metadata of partitions
ansible.builtin.debug:
msg: "{{ item.name }} is using {{ item.fs_name }}"
loop: "{{ _my_disk_layout.submission }}"
#
# `fstab` metadata contains prepared fstab fields. Only partitions that define
# both `mount` and `fs` blocks are included.
#
# - PARTUUID=... /
# - PARTUUID=... /efi
#
- name: Print the fstab fields
ansible.builtin.debug:
msg: "{{ item.fs_vfstype }} {{ item.fs_spec }} {{ item.fs_file }}"
loop: "{{ _my_disk_layout.fstab }}"This action plugin modifies the global system environment via the /etc/environment file.
It is recommended to only set the most essential system-wide variables here, such as those required for early loading
by drivers and system services. For user-specific variables, use foundation.user.env.
Environment:
- A key-value mapping of environment variables to set. Values must be strings.
Examples:
- name: Set a global system variable
foundation.system.env:
environment:
SYSTEM_VAR: "YES"This action plugin modifies kernel command-line parameters and handles the proper regeneration of the initramfs.
The system must be set up with the da-apex-prep/dc-bootloader role for this to work. The plugin
intelligently merges merges list items (i.e. blacklist=one,two), and replaces flags and options
(i.e. quiet, one=two).
Parameters:
headless: A boolean flag. Iftrue, the generated initramfs image will be platform-neutral to avoid boot issues. The initramfs will be properly regenerated with hardware-specific modules upon the next system update on the booted machine.
Environment:
- A key-value mapping of kernel parameters to set.
Examples:
- name: Add kernel parameters
foundation.system.kernel:
headless: true
environment:
#
# Flags must be set to true.
#
quiet: true
#
# Options must be strings.
#
loglevel: "3"
#
# Lists must be a list of strings. Options are allowed to be upgraded to list.
#
module_blacklist:
- "pcspkr"
- "floppy"This action plugin manipulates system packages, transparently managing packages from official repositories and user repositories like the AUR.
In order to use this plugin, da-apex-prep/db-packaging role must have been run first.
Parameters:
sync: Optional flag to sync repositories before performing any operations.force: Optional flag to forcefully install packages even if they already exist. This can be useful for re-triggering post-install hooks.
Variables remove:
- A list of packages to remove. This will never fail, allowing optionally removing conflicting packages first.
Variable install:
- A list of packages to install.
Examples:
- name: Install git version of waybar
foundation.system.services:
headless: false
vars:
remove:
- waybar
install:
- waybar-gitThis action plugin manipulates systemd services in a declarative way.
Unlike the built-in ansible.builtin.systemd_service module, it works on non-bootable machines and implies logical
state changes (e.g., masking a service also stops it). The plugin operates in the user context when run without
become, and in the system context otherwise.
Parameters:
headless: A boolean flag indicating if the system is currently running.
Variable units:
- A list of systemd units to manage.
Variable state:
reload: Reloads the systemd daemon.start: Restarts the specified units.stop: Stops the specified units.enable: Enables the units and restarts them immediately.disable: Disables the units and stops them immediately.mask: Masks the units and stops them immediately.unmask: Unmasks the units and restarts them immediately.
Examples:
- name: Mask unnecessary services
foundation.system.services:
headless: false
vars:
units:
- avahi-daemon.service
state:
mask: trueThis action plugin stops specified services and process groups to ensure a consistent state before applying configuration changes.
Parameters:
headless: A boolean flag. Iftrue, this action does nothing, as a non-bootable machine has no running services.
Variable services:
- A list of services to stop.
Variable processes:
- A list of process names to terminate.
Examples:
- name: Ensure firefox is not running
foundation.system.stop:
headless: "{{ ba_headless }}"
vars:
processes:
- firefoxThis action plugin manages a user's environment via pam_env.conf file, with support for sections and variable
substitution.
It manually parses the file content, preserving unknown entries, inline/top comments and replaces absolute paths with
placeholders like @{HOME}. Substitution is performed greedily, even for non-path variables, such as
--config-home=/home/user/someapp.
Parameters:
section: The name of the section to place the variables under.home: The user's home directory, which will be replaced with the@{HOME}placeholder.subs: A dictionary of additional substitutions (e.g., XDG base directories). These have the highest priority and are placed in the very first!importantsection. along with thehomepath for all variables. Practically recommended to be a list of XDG_* base directories.
Environment:
- A key-value mapping of environment variables to set.
Examples:
#
# pam_env would contain:
# !important section:
# - XDG_CONFIG_HOME = @{HOME}/.config
# apps section:
# - SOMEAPP_HOME = ${XDG_CONFIG_HOME}/someapp
#
- name: Set someapp home directory
foundation.user.env:
section: apps
home: /home/user
subs:
XDG_CONFIG_HOME: /home/user/.config
environment:
SOMEAPP_HOME: "/home/myuser/.config/someapp"This is a set of action and lookup plugins for managing user data layouts. The action plugin identifies the desired layout, wipes old directories, and creates necessary symbolic links. The lookup plugin simply resolves the correct path without performing filesystem operations.
The user layout determines the preferred stage paths for user applications. The plugin supports two possible options:
- XDG Layout:
- The modern, standardized, clean separation of config, cache, data, and state.
- Still not supported by many different applications.
- DOT Layout:
- The historical style that is the only option for many applications.
- Can mimic its style for XDG compliance applications via symbolic links.
Parameters:
wipe: Whether to wipe all possible directories (both resolved and foreign). Eitherneveroralways.layout: The desired layout choice. Eitherdotorxdg.
Variables:
default: The path the tool uses by default.wipe: Optional override forwipeparameter.dot,xdg: Optional layout-specific value. Can be a path string or a dictionary{ link: /path/to/link }to create a symlink (fromlinktodefault).
Examples:
#
# This would wipe:
# - /home/user/myapp
# - /home/user/.myapp
# - /home/user/.local/share/myapp
#
# This would link:
# - /home/user/.myapp => /home/user/myapp
#
# This would resolve as:
# - /home/user/myapp
#
- name: Resolve myapp user layout
foundation.user.layout:
wipe: true
layout: dot
vars:
data:
default: /home/user/myapp
dot:
link: /home/user/.myapp
xdg: /home/user/.local/share/myapp
register: _myapp_ulayout
#
# Manually handle XDG layout case.
#
- name: Apply XDG layout to myapp
when: fa_layout == "xdg"
ansible.builtin.debug:
#
# Value is resolved as /home/user/myapp, because of the link.
# Otherwise it would be /home/user/xdg or /home/user/default.
#
msg: Going to manually set {{ _myapp_layout.data }}
#
# Lookup Plugin Example.
#
- name: Config rendering example
vars:
_ulayout:
default: /home/user/myapp
dot: /home/user/.myapp
xdg: /home/user/.local/share/myapp
ansible.builtin.debug:
#
# Resolved as /home/user/xdg
#
msg: "Resolved as {{ lookup('foundation.user.layout', 'dot', _ulayout) }}"This action plugin manipulates user-specific packages via the mise version manager.
The ha-apex-env/hd-vermen role must have been run first.
Parameters:
wipe: Whether to not only uninstall a package but also wipe its installation directory entirely. Eitherneveroralways.home: The user's home directory.
Variable install:
- A list of packages to install, in the format tool@version. Supports the complete mise tool identifier (e.g.,
aqua:golangci/golangci-lint@latest).
Variable remove:
- A list of packages to remove.
Examples:
- name: Install java package for user
foundation.user.packages:
wipe: false
home: /home/user
vars:
install:
- java@latestA collection of roles from previous development iterations, prior to the current plugin system.
They are left for archival purposes only and are not used anymore. They are documented in the
arguments_specs file within the
collection.
An experimental Jinja extension that introduces syntax intended to solve complex Jinja indentation problems.
This module is deprecated as it raises more questions than it answers and may be replaced in the future. Proper
reframing is required, perhaps with a new Mako template engine.
Currently, is supports the following features:
- Source Rules:
- A template must use tabs.
- The indentation of the rendered file is controlled via the
setindentstatement.
- Whitespaces: Each line is stripped of whitespaces.
- Newline Symbols:
- Each newline symbol is stripped unless the line ends with the
+symbol. - Extra newlines can be added with the
newlinestatement.
- Each newline symbol is stripped unless the line ends with the
- Indentation Control: Each indentation requires an appropriate
indentstatement.
Parameters:
- Same as for
ansible.builtin.template.
Examples:
{% setindent spaces 8 %}
{% macro foo() %}
{% indent %}
function() {+
{% indent %}
return [1, 2, 3];+
{% endindent %}
}
{% endindent %}
{% endmacro %}
{+
{% indent %}
"root: "true",+
"values": [+
{% indent %}{{ foo() }}{% endindent %},+
],+
{% endindent %}
}+
{% newline 3 %}How do I get a list of all possible variables to tune?
Due to the project's role hierarchy, you can use these one-liners:
# List all default variables across all roles
find roles -wholename '*/defaults/main.yml' | xargs bat
# Find which variables a specific role references
rg "[a-z]{2}_[a-z_]+" -oN roles/ga-apex-desktop/gh-x11/tasks/main.ymlWhat is the code style?
The project inherits common and widely adopted Ansible conventions and practices:
- Python: 4-space indentation, 120-character line-limit.
- YAML: 2-space indentation, 120-character line limit,
.ymlextension,_prefix for local variables. - Inline Jinja (.yml): 2-space indentation, single-quotes for strings (unless at the start of a line).
- Single-quotes for strings, except at the beginning of the line
- Template Files (.j2): 4-space indentation, 120-character line limit, double-quotes for strings.
What is the point of overcomplicating user directories instead of using connection context and/or facts?
The main reason for this is to create a static, reproducible environment that behaves consistently under different circumstances. No code changes should be applied when running a specific role through container, chroot, or root-only SSH connection.
Moreover, relying on the environmental variables often results in an inconsistent source of truth. Specifically,
pam_env, /etc/environment, and .bash_profile may not clearly reflect pending changes and may require multiple
machine reboots.
What kind of the roles can be run on SSH, localhost, maybe other Linux distributions?
Once the system basics are set up (packaging, kernel, etc.), most apex-* roles are somewhat platform-neutral and can
be run standalone. However, some roles have dependencies (e.g., the vault requires a GnuPG keyring).
It is a good idea to check the role implementation and its relationship with different roles and components in the Roles section. Even very complex roles can be tuned with tags.
While building a fully resilient, cross-distribution playbook is beyond the scope of this project, many of the techniques and configurations can be adapted for other systems. Some of the tasks require almost no change in order to be run on musl or even ARM-based systems. This is not only about all-in-one solution, but also configuration quirks: nothing stops from using the same technique of unattended GnuPG generation in shell scripts, PyInfra, or standalone utility.
Huge shout-out to:
- The
Ansibleteam and its community - The
Jinja2template engine ansible-lintfor keeping the code consistent- The
AnsibleExtension for VSCode uvandrufffor an amazing Python tooling experienceruffPython code formatter and linter- All the authors of documentation, manual pages, mailing lists, and Stack Exchange answers that made this possible.
Praise open 👾 source! Thanks for using this project and your attention to this matter! I use Arch BTW!
This project is licensed under the MIT License. You are free to use, modify, and distribute this software, but please provide attribution.


