Script de post‑instalación en Linux: cómo automatizar tu sistema tras formatear

  • Un buen script de post‑instalación en Bash permite automatizar actualización, instalación de programas y ajustes clave tras reinstalar Linux.
  • Cada familia de distribuciones requiere comandos específicos de actualización e instalación, por lo que conviene mantener scripts separados por distro.
  • Para despliegues avanzados o masivos pueden emplearse herramientas como Kickstart, Ansible o incluso enfoques tipo Linux From Scratch.
  • El nivel de complejidad puede ir desde un simple install.sh reutilizable hasta la construcción completa de un sistema Linux personalizado.

Script de post‑instalación en Linux

Si cada vez que reinstalas o formateas tu Linux pierdes una tarde entera dejando el sistema “como a ti te gusta”, necesitas automatizarlo ya. Un buen script de post‑instalación te permite, con un solo comando, actualizar el sistema, instalar tus programas clave, aplicar ajustes de seguridad e incluso dejar afinado tu entorno de escritorio y tus dotfiles sin ir comando a comando.

La idea es muy simple: preparar uno o varios scripts Bash reutilizables que guardes en USB booteable multiboot, en tu repositorio de configuración o en un disco externo, y que puedas lanzar nada más terminar la instalación de tu distro. A partir de ahí, el sistema se pone “en piloto automático” y tú solo vigilas que no haya errores gordos. Vamos a ver cómo hacerlo en diferentes situaciones: desde el típico Ubuntu recién instalado hasta escenarios más avanzados con varias distros, pasando por herramientas de instalación automatizada.

¿Qué es un script de post‑instalación en Linux y para qué sirve?

Un script de post‑instalación no es más que un archivo de texto ejecutable con comandos Bash que realizan de forma secuencial todo lo que harías tú a mano justo después de instalar Linux. En lugar de ir tecleando “sudo apt install esto”, “sudo apt install lo otro”, toquetear el cortafuegos, instalar snaps, etc., lo dejas escrito una vez y lo reutilizas todas las veces que quieras.

Este tipo de script suele encargarse de tareas típicas de puesta a punto: actualizar la distro, instalar programas base (navegador, suite ofimática, herramientas de seguridad, multimedia), activar y configurar el firewall, ajustar algún comportamiento del entorno gráfico (por ejemplo el dock de GNOME) y, si quieres, ejecutar otros scripts que traigan tus dotfiles, configuraciones de Vim, de tu terminal o lo que uses en tu día a día.

Crear un script Bash básico de post‑instalación

Empezamos por el enfoque más directo: un script Bash en tu home que puedas copiar‑pegar en cualquier instalación nueva. Vale para distro‑hoppers, para quien reinstala mucho la misma distro o para tener un guion reproducible cuando montas equipos para otros.

Cómo usar Ventoy para reparar USB
Artículo relacionado:
Pasos para crear un USB booteable multiboot con Ventoy

Primero, crea el archivo del script, por ejemplo install.sh:

touch install.sh

Después hazlo ejecutable, de forma que puedas lanzarlo con ./install.sh. Aquí se suele usar chmod con permisos para todos los usuarios, y a menudo se tira de sudo si el archivo está en un sitio donde hace falta elevar permisos:

sudo chmod a+x install.sh

Ahora edita el archivo con tu editor preferido. Puedes usar Vim, Nano, Emacs, Gedit, Kate o lo que tengas más a mano. Un ejemplo clásico usando Vim sería:

sudo vim install.sh

Al principio del fichero hay dos líneas casi obligatorias. La primera indica qué intérprete debe ejecutar el script (la “shebang”):

#!/bin/bash

La segunda línea suele utilizarse para indicar la codificación, lo cual es útil si vas a usar tildes, eñes o caracteres raros en comentarios o mensajes:

# -*- ENCODING: UTF-8 -*-

Actualizar el sistema según la distribución

Una de las primeras cosas que todo el mundo hace tras instalar Linux es actualizar los paquetes. Ese paso también debe estar en tu script, pero es importante tener en cuenta que cada distro utiliza un gestor de paquetes distinto. Lo normal es que tu script esté pensado para una familia concreta (por ejemplo Debian/Ubuntu) y, si tocas varias, que tengas un script distinto para cada caso.

Para distribuciones del estilo Debian, Ubuntu y derivadas, la secuencia típica puede ser:

sudo apt update && sudo apt -y upgrade

En otras distros la cosa cambia. Por ejemplo, en CentOS y Red Hat “like” se ha utilizado tradicionalmente yum:

sudo yum update

En versiones nuevas de Fedora apareció dnf como evolución de yum, con una sintaxis muy parecida. Así, para Fedora podrías usar:

sudo dnf update

En el caso de openSUSE, el sistema de paquetes se gestiona normalmente con zypper y la actualización general se hace con:

sudo zypper update

En el mundo Arch Linux, Manjaro, Antergos, KaOS y otras basadas en pacman, el equivalente sería:

sudo pacman -Syu

Si además usas herramientas externas como yaourt (hoy en día sustituido por yay y otros ayudantes de AUR), en algunos entornos se veía algo como:

yaourt -Syua

Otras como Gentoo o Slackware tienen sus propios gestores y comandos de actualización. En ese caso, tu script irá adaptado a lo que uses (emerge, slapt‑get, etc.), pero la idea siempre es la misma: dejar la base del sistema al día nada más arrancar la post‑instalación.

Organizar la instalación de programas por categorías

Una vez el sistema está actualizado, llega el momento de instalar todo el software que necesitas. Para que el script no sea un caos, es buena idea clasificar los paquetes en grupos lógicos y dejar los comentarios bien claros. Un esquema típico puede tener categorías como estas:

  • Utilidades: herramientas de sistema, compresores, monitores, etc.
  • Internet: navegadores, clientes de correo, mensajería.
  • Juegos: Steam, Lutris, juegos nativos, etc.
  • DE / Escritorios: entornos gráficos y complementos.
  • Multimedia: reproductores de vídeo y audio, editores de imagen.
  • Productividad: suites ofimáticas, gestores de notas, tareas.
  • Desarrollo: compiladores, IDEs, herramientas de debugging.

En el script, puedes introducir comentarios para estas secciones, de forma que sea fácil localizar cada bloque. Algo así:

# Utilidades
# Desarrollo
# Internet
# Juegos
# DE's y WM's
# Multimedia
# Productividad

Debajo de cada bloque comentado, añades los comandos de instalación adecuados para tu distro. En un sistema basado en Arch Linux, podrías tener algo del estilo:

sudo pacman -S chromium
sudo pacman -S steam
sudo pacman -S gnome-shell gnome-extra

El objetivo es que, cuando ejecutes el script, se instalen de golpe todas las aplicaciones que sueles usar siempre. Si en algún momento quieres cambiar tu selección (añadir o quitar un programa), solo modificas la lista de paquetes de esa categoría y listo.

Ejecutar el script y buenas prácticas

Cuando ya has escrito y guardado tu script install.sh, desde la terminal solo tienes que ir a la carpeta donde lo tengas y lanzarlo. Un patrón cómodo es combinar cambio de directorio y ejecución:

cd /ruta/del/script && ./install.sh

Otra opción muy simple para usuarios de Ubuntu o Debian es invocar directamente bash sobre el archivo:

bash nombre-script.sh

Para que no tengas que estar pendiente de confirmaciones, en muchas órdenes de instalación se usa el parámetro -y (o equivalente) que acepta por defecto la pregunta de “¿Desea continuar?”. De este modo el script puede ejecutarse de principio a fin sin intervención, lo cual es cómodo cuando lo lanzas justo tras instalar el sistema y lo dejas tirando actualizaciones, instalaciones y limpieza mientras haces otra cosa.

Ejemplo práctico de script post‑instalación en Ubuntu

script post‑instalación en Ubuntu

En un escenario típico con Ubuntu 22.04 LTS o posterior, tiene sentido montar un script que combine APT y Snap, porque algunas aplicaciones solo están disponibles como snaps. Un ejemplo de flujo podría ser este: actualizar, instalar paquetes de siempre, limpiar el sistema, instalar snaps clave, activar el cortafuegos y aplicar algún retoque de escritorio.

Un esqueleto sencillo de ese tipo de script podría contener bloques del estilo:

#!/bin/bash

# Actualizar la lista de paquetes
sudo apt update

# Instalar actualizaciones disponibles sin preguntar
sudo apt -y upgrade

# Eliminar paquetes que ya no son necesarios y limpiar
sudo apt -y autoremove
sudo apt autoclean

# Instalar aplicaciones habituales vía APT
sudo apt -y install vlc gimp clamav chkrootkit lynis

# Instalar aplicaciones habituales como Snaps
sudo snap install chromium brave

# Habilitar UFW (cortafuegos del kernel Linux)
sudo ufw enable

# Ajustar el comportamiento del dock de GNOME
gsettings set org.gnome.shell.extensions.dash-to-dock click-action 'minimize'

Con algo tan compacto estás cubriendo actualizaciones, limpieza, software base, seguridad y un pequeño ajuste de usabilidad. Por supuesto, puedes ampliar este script a tu gusto: añadir instalación de herramientas de desarrollo, configuración de backups, scripts que descarguen tus dotfiles desde Git, etc.

Usar herramientas avanzadas: Ansible, Chef y similares

Si trabajas con varias máquinas o tienes que replicar tu entorno en servidores, portátiles y sobremesas, llega un punto en que un script Bash simple se queda corto. Ahí entran herramientas como Ansible, Chef y compañía, que permiten describir el estado deseado de un sistema (paquetes instalados, archivos de config, servicios activos) y aplicarlo de forma reproducible.

Algunos usuarios que están hartos de hacer siempre la misma configuración post‑instalación en Ubuntu recurren a estos sistemas de gestión de configuración o a proyectos ya hechos de post‑instalación específicos. Un ejemplo real es un repositorio dedicado a automatizar la post‑instalación en Ubuntu, donde se combinan scripts Bash con definiciones de paquetes, temas, tipografías y otras preferencias.

Aun así, para muchos casos de escritorio lo más práctico sigue siendo un script Bash sin dependencias externas, sobre todo si buscas algo sencillo de transportar en un USB o que puedas copiar en una máquina recién instalada sin andar montando infraestructura adicional.

Automatización desde el propio instalador: scripts de %post y Kickstart

En el mundo de las distribuciones tipo Red Hat, CentOS, Rocky y derivadas, es muy habitual usar Kickstart para instalaciones desatendidas. Dentro de un fichero Kickstart se puede definir una sección %post que se ejecuta automáticamente al finalizar la instalación del sistema base.

La idea es que, si has definido bien la configuración de red en el propio Kickstart, al entrar en la sección de post‑instalación la red ya estará levantada. Ahí puedes colocar cualquier comando que quieras automatizar: instalación de paquetes adicionales, ajustes, descargas de scripts, cambios en mensajes del sistema, etc.

Como ejemplo sencillo, se podría cambiar el mensaje del día (MOTD) del sistema recién instalado colocando en la sección %post algo equivalente a:

%post
echo "Bienvenido a tu nuevo servidor" > /etc/motd
%end

Este enfoque de script de post‑instalación embebido en el instalador es muy potente para despliegues masivos, ya que todo el proceso es 100 % automático: arranca la instalación, se aplican particiones y paquetes base, se ejecutan scripts de post‑instalación y el sistema queda listo para usarse.

Limitaciones al replicar instalaciones entre distros heterogéneas

Cuando intentas usar un único mecanismo genérico de post‑instalación para distros muy distintas, empiezan los dolores de cabeza. No todas gestionan actualizaciones, dependencias y parches de la misma manera, ni ofrecen las mismas versiones de software en sus repositorios.

Hay que distinguir, por un lado, un script que solo enumera paquetes a instalar y deja que el gestor de la distro resuelva dependencias (como hace apt‑get o herramientas estilo zypper, dnf, urpmi), y por otro, un sistema que pretende manejar también parches de seguridad, backports y versiones nuevas de programas que la distribución original ni siquiera provee.

En una distro como Debian, con miles de paquetes y un ecosistema inmenso, el instalador y herramientas como apt se encargan de resolver dependencias del software que la propia distribución mantiene. En otras, especialmente las comerciales, la filosofía es distinta: te dan el conjunto de paquetes y parches que han decidido soportar y, fuera de ahí, ya entras en un terreno en el que la resolución de dependencias y actualizaciones no está garantizada.

Un ejemplo ilustrativo: en una SuSE 8.2 antigua, si querías actualizar PostgreSQL con apt a una versión más nueva que la original de la distribución, te encontrabas con que apt te pedía primero la versión incluida en los CDs oficiales. Tenías que entrar en YaST, marcar el paquete original para instalación desde el CD correspondiente, y solo después apt aceptaba aplicar la actualización extra desde sus repositorios. Este tipo de particularidades hace que sea difícil tener una herramienta única que funcione sin intervención en todos los casos.

Métodos rápidos para identificar el tipo de archivo en Windows y Linux
Artículo relacionado:
Cómo instalar y configurar el Subsistema de Windows para Linux 2 (WSL2)

Los propios instaladores de muchas distros ya contemplan mecanismos para grabar selecciones de paquetes y reutilizarlas en otras máquinas (por ejemplo, guardar la lista de paquetes en un disquete o medio externo y cargarla en una instalación nueva). En esas situaciones, el instalador vuelve a calcular dependencias, te avisa de lo que falta, puede resolverlo automáticamente o pedirte que aceptes paquetes adicionales, o incluso te muestra la opción arriesgada de “seguir adelante aunque haya posibles inconsistencias”.

Cuando, además, hablamos de granjas de máquinas heterogéneas, con hardware y perfiles de software diferentes, la complejidad se dispara. Para que una utilidad genérica de replicación de post‑instalaciones funcione bien entre distintas distribuciones, o bien tú mismo empaquetas y mantienes todos los binarios, parches y dependencias para cada distro, o necesitas que las propias distribuciones den soporte oficial a ese sistema unificado, cosa poco probable en el contexto comercial actual.

Scripts de post‑instalación en entornos personalizados y LFS

En el extremo opuesto a las distros preempaquetadas están los entornos en los que te construyes tu sistema Linux desde cero, al estilo Linux From Scratch (LFS). Aquí la post‑instalación deja de ser una lista de paquetes y se convierte en una serie muy larga de pasos guiados: compilación de herramientas, creación de particiones, montaje de sistemas de ficheros, ajustes de toolchain, configuración de servicios básicos, etc.

Seguir una guía detallada de LFS o una variante más didáctica te lleva a entender el sistema desde las capas más bajas: compiladores, enlazadores, bibliotecas C, shell, utilidades básicas, hasta llegar al kernel y al arranque con GRUB. Es un proceso que puede alargarse varios días (entre 3 y 5, fácilmente), requiere paciencia y cierto nivel de administración Unix, pero a cambio te da un control total sobre qué se instala y cómo se monta el sistema.

En un caso real basado en Debian como sistema anfitrión dentro de una máquina virtual en VirtualBox, el flujo empieza por crear una VM con un disco de, por ejemplo, 30 GB, instalar Debian 10 como base, afinar los repositorios en /etc/apt/sources.list para poder acceder a los paquetes de compilación (build-essential, linux-headers-amd64, etc.) y después instalar las VBoxLinuxAdditions para tener la integración completa con el host.

Una vez el sistema base está cómodo para trabajar (pantalla completa, dependencias de compilación resueltas), se crea una segunda unidad de disco virtual, también de tamaño fijo, que será donde se levante el nuevo sistema Linux personalizado. Desde Debian se particiona ese segundo disco (por ejemplo /dev/sdb) con herramientas como parted, se define una tabla de particiones de tipo msdos y se crean particiones primarias y lógicas (raíz, swap, /home) siguiendo un esquema similar al del primer disco.

Después se formatean esas particiones con los sistemas de ficheros apropiados, se obtienen sus UUID con utilidades como blkid y se añaden entradas a /etc/fstab del sistema anfitrión para montarlas en puntos como /mnt/lfs, /mnt/lfs/home y para habilitar el swap. A partir de ahí se monta la estructura base, se crean directorios como $LFS/sources y $LFS/tools, se descarga una larga lista de paquetes con wget usando un archivo wget-list y se crea un usuario dedicado, por ejemplo lfs, con su .bash_profile y .bashrc adaptados al entorno de compilación.

El proceso continúa con un checker (un script de comprobación de versiones) que verifica que están presentes todas las herramientas necesarias (Bash, Binutils, GCC, etc.), instalando en muchos casos tanto el paquete principal como su variante de desarrollo (-dev o -devel). También se crean enlaces simbólicos clave, como hacer que /bin/sh apunte a Bash, y se van compilando uno a uno paquetes como binutils, gcc, glibc, tcl, expect, dejagnu, m4, ncurses, bison, bzip2, coreutils, diffutils, file, findutils, gawk, gettext, grep, gzip, make, patch, perl, Python, sed, tar, texinfo, xz y muchos otros, en dos rondas (toolchain temporal y toolchain final dentro del chroot).

A lo largo del camino se suele limpiar símbolos de depuración innecesarios para ahorrar espacio, se eliminan bibliotecas estáticas que no son imprescindibles, se ajusta la cadena de herramientas para que todas las nuevas compilaciones utilicen las bibliotecas recién instaladas, y se entra al entorno chroot preparando /dev, /proc, /sys, /run y creando nodos como /dev/console y /dev/null.

Dentro del chroot se construye el árbol estándar de directorios (/bin, /sbin, /usr, /var, /etc…), se generan enlaces simbólicos provisionales para programas que aún no existen, se inicializan /etc/passwd y /etc/group con un usuario root mínimo, y se preparan archivos de log en /var/log con permisos correctos. También se configuran las locales con herramientas como locale-gen o equivalentes, se selecciona zona horaria con tzselect y se crea un enlace desde /etc/localtime a la zona elegida en /usr/share/zoneinfo.

Otra pieza crítica es la configuración de nsswitch.conf para que la resolución de nombres y otros servicios funcionen bien en red, y la de ld.so.conf junto a ldconfig para registrar las rutas de las nuevas bibliotecas dinámicas. Después, ya con la glibc y los compiladores definitivos, se recompilan paquetes clave para asegurarse de que todo enlaza correctamente.

En paralelo se va configurando el acceso a la red en el nuevo sistema: se crean archivos tipo /etc/sysconfig/ifconfig.eth0 (o el nombre de interfaz que corresponda), se define la IP, la puerta de enlace y la máscara, se genera /etc/resolv.conf con los servidores DNS adecuados y se fijan hostname y /etc/hosts con la información de la máquina.

También se configuran scripts y archivos de arranque como /etc/inittab, /etc/sysconfig/clock, /etc/sysconfig/console y /etc/sysconfig/rc.site, donde se ajustan detalles como el nivel de ejecución por defecto, el manejo de logs del sistema, el idioma de consola, el mapa de teclado y el comportamiento de los servicios en el arranque.

Para la experiencia de shell se personalizan /etc/profile y archivos del tipo ~/.bash_profile y ~/.bashrc, además de /etc/inputrc para la biblioteca Readline (controlando atajos de teclado, comportamiento de la historia de comandos, etc.). Se crea también un /etc/shells con la lista de shells válidas y un /etc/fstab adaptado a las nuevas particiones (raíz, home, swap, /boot si existe).

Más adelante llega el turno de compilar el kernel Linux: se descomprime la versión elegida (por ejemplo un linux-5.x.y), se parte de un archivo de configuración base, se revisan opciones clave como CONFIG_DEVTMPFS y otras relacionadas con udev y el hardware de la VM, se entra en el menú de configuración (make menuconfig) para ajustar el nombre por defecto del host y numerosas características, y finalmente se compila (make, make modules_install) y se instala el kernel y sus componentes en /boot.

El paso final suele ser instalar y configurar GRUB como cargador de arranque. Se ejecutan comandos para que GRUB se escriba en el MBR del disco correcto, se genera un grub.cfg con las entradas para el nuevo kernel y, si procede, para otros sistemas presentes. En este contexto concreto, como la distribución anfitriona Debian se usó solo como “andamio”, al final se retira su disco virtual de la máquina, de modo que el disco que al principio era /dev/sdb pasa a ser /dev/sda y el nuevo sistema Linux arranca de forma autónoma.

Cuando todo está en su sitio, se crean archivos de identificación como /etc/lsb-release y /etc/os-release con el nombre de la nueva distro (por ejemplo “S4viOS”), se ajusta un bashrc agradable para root y futuros usuarios, y se hace un último desmontaje de sistemas de ficheros antes del gran momento: reiniciar la VM, elegir la entrada correspondiente en GRUB y ver cómo el sistema Linux que te has currado desde cero arranca por primera vez.

Cuándo merece la pena ir tan lejos y cuándo basta con un simple script

Cómo usar Ventoy para reparar USB
Artículo relacionado:
Pasos para crear un USB booteable multiboot con Ventoy

Todo este recorrido, desde el script sencillo de APT o pacman hasta el montaje de un sistema LFS con kernel personalizado y GRUB, ilustra bien el rango de posibilidades que tienes en Linux a la hora de automatizar la post‑instalación.

Para el día a día, lo más práctico para casi cualquiera es mantener uno o varios scripts de post‑instalación por distribución, donde actualices, instales y afines lo que usas siempre; si te mueves con muchas máquinas o entornos mixtos, entra en juego Ansible o Kickstart con secciones %post; y si lo que te pide el cuerpo es entender Linux desde las tripas, el camino tipo LFS, aunque largo, te da un nivel de control que ninguna distro comercial ofrece de serie. Comparte la guía para que más usuarios sepan del tema.