Problema
Mantener configuraciones de usuario (bashrc, scripts auxiliares y archivos sensibles) sincronizadas entre varios equipos Linux es una tarea repetitiva. Cada máquina tiene pequeñas variaciones: algunos hosts requieren paquetes extra, otros necesitan secretos que no deben compartirse. Copiar manualmente los archivos o ejecutar git clone && ./setup.sh genera divergencias, errores de permisos y pérdida de tiempo, sobre todo cuando el número de hosts crece de 1 a 5 o más.
Causa
- Gestión ad‑hoc de archivos – Sin un control de versiones central, cada host evoluciona de forma independiente.
- Falta de parametrización – Los scripts se asumen idénticos para todos los nodos, lo que impide añadir excepciones por host o por grupo.
- Ausencia de secreto seguro – Guardar claves o tokens en texto plano dentro del repositorio compromete la seguridad y desalienta el versionado.
- Entorno heterogéneo – Un NAS sin gestor de paquetes necesita una ruta distinta para los scripts, mientras que los servidores con apt pueden instalar dependencias automáticamente.
Solución
Utilizar Ansible como motor de orquestación permite describir qué debe existir en cada máquina y cómo adaptarlo a sus particularidades. La clave está en estructurar el proyecto con:
- Inventario grupal que refleje las funciones (laptop, workstation, server, nas).
- Variables de grupo y de host para diferenciar dotfiles comunes, extensiones opcionales y secretos.
- Roles que encapsulen tareas reutilizables:
dotfiles,scripts,secrets. - Ansible Vault para almacenar claves que solo se despliegan en los hosts autorizados.
Paso a paso esencial
-
Crear la raíz del proyecto
mkdir -p ansible/{inventories,roles/{dotfiles,tasks,scripts,secrets},group_vars,host_vars} cd ansible -
Definir el inventario (
inventories/hosts.ini)[laptops] laptop1 ansible_host=192.168.1.10 [workstations] desktop1 ansible_host=192.168.1.20 [servers] home_server ansible_host=192.168.1.30 vps ansible_host=203.0.113.5 [nas] nas01 ansible_host=192.168.1.40 [all:vars] ansible_user=admin ansible_python_interpreter=/usr/bin/python3Los nombres de los grupos son arbitrarios; la convención
site.ymlpara el playbook principal proviene de la tradición de Ansible de considerar el “sitio” completo como un único despliegue. -
Variables comunes (
group_vars/all.yml)dotfiles_repo: "[email protected]:miusuario/dotfiles.git" scripts_repo: "[email protected]:miusuario/scripts.git" -
Variables específicas (
group_vars/nas.yml)extra_dotfiles: - "nas_extra.sh" install_packages: false # NAS sin aptHost‑level override (
host_vars/laptop1.yml)secret_vault_path: "vault/laptop_secrets.yml" -
Playbook principal (
site.yml)- hosts: all become: true roles: - dotfiles - scripts - secrets -
Rol
dotfiles
Estructura:roles/dotfiles/tasks/main.yml- name: Clonar repositorio de dotfiles git: repo: "{{ dotfiles_repo }}" dest: "/home/{{ ansible_user }}/.dotfiles" version: master force: yes - name: Crear enlace simbólico de .bashrc file: src: "/home/{{ ansible_user }}/.dotfiles/bashrc" dest: "/home/{{ ansible_user }}/.bashrc" state: link force: yes - name: Aplicar extensiones opcionales when: extra_dotfiles is defined loop: "{{ extra_dotfiles }}" file: src: "/home/{{ ansible_user }}/.dotfiles/{{ item }}" dest: "/home/{{ ansible_user }}/{{ item }}" state: link force: yes -
Rol
scripts(similar, clona y enlaza)- name: Clonar repositorio de scripts git: repo: "{{ scripts_repo }}" dest: "/opt/scripts" version: master force: yes - name: Añadir scripts al PATH lineinfile: path: "/home/{{ ansible_user }}/.bashrc" regexp: '^export PATH=' line: 'export PATH=$PATH:/opt/scripts' state: present -
Rol
secrets- name: Decrypt vault file (only on hosts that define it) when: secret_vault_path is defined include_vars: file: "{{ secret_vault_path }}" name: secret_vars - name: Deploy private script when: secret_vars.private_script is defined copy: content: "{{ secret_vars.private_script }}" dest: "/usr/local/bin/private.sh" mode: '0755' -
Crear el vault (ejemplo para laptop)
ansible-vault create vault/laptop_secrets.ymlDentro, definir:
private_script: | #!/bin/bash echo "Información sensible" -
Ejecutar
ansible-playbook -i inventories/hosts.ini site.yml --ask-vault-pass
Cuándo aplicar esta solución
- Múltiples máquinas con configuración similar – Cuando al menos dos hosts comparten la mayor parte del entorno de usuario.
- Necesidad de variaciones controladas – Si algunos equipos requieren archivos extra o paquetes opcionales.
- Gestión de secretos – Cuando al menos un host necesita credenciales que no deben quedar en texto plano.
- Entorno Debian/Ubuntu – Los módulos
aptygitfuncionan sin ajustes; para sistemas sin gestor de paquetes, basta con desactivar la tarea de instalación (install_packages: false).
No es la mejor opción si solo se administra un único equipo y la sobrecarga de Ansible supera el beneficio percibido.
Código
# Estructura mínima del proyecto
tree -L 3 ansible
ansible
├── inventories
│ └── hosts.ini
├── group_vars
│ ├── all.yml
│ └── nas.yml
├── host_vars
│ └── laptop1.yml
├── roles
│ ├── dotfiles
│ │ └── tasks
│ │ └── main.yml
│ ├── scripts
│ │ └── tasks
│ │ └── main.yml
│ └── secrets
│ └── tasks
│ └── main.yml
└── site.yml
Verificación
- Comprobación de enlaces – En cada host,
ls -l ~/.bashrcdebe apuntar a.dotfiles/bashrc. - Presencia de scripts –
which private.shsolo debe devolver una ruta en los hosts que tienen el vault. - Salida de Ansible – El playbook debe terminar con
ok=... changed=...sin errores. - Re‑ejecución idempotente – Ejecutar de nuevo el playbook no debe modificar archivos ya alineados.
Notas adicionales
- Orden de roles: colocar
dotfilesantes descriptsevita que un script dependa de un enlace que aún no existe. - Cache de git: usar
force: yesgarantiza que cambios en el repositorio se reflejen aunque el commit sea el mismo. - NAS sin apt: la variable
install_packagespermite saltar tareas de instalación; basta con envolver esas tareas enwhen: install_packages. - Mantenimiento – Añadir un nuevo host solo requiere crear su entrada en
inventories/hosts.iniy, si necesita excepciones, un archivo enhost_vars/. El resto del repositorio permanece intacto. - Escalado – Para más de 20 máquinas, considera dividir el inventario en varios archivos (
hosts_laptops.ini,hosts_servers.ini) y usar-imúltiple.
Con esta estructura, un sysadmin puede desplegar dotfiles, scripts y secretos de forma reproducible, mantener diferencias controladas y evitar la tediosa copia manual que suele convertirse en la fuente de inconsistencias.