CI/CD en su propio VPS
Un servidor propio para las compilaciones compensa cuando los minutos en la nube no alcanzan, las compilaciones son lentas o hace falta acceso a recursos internos. Pero el runner ejecuta cualquier código del repositorio, así que un error en su configuración abre el acceso al servidor de producción y a todos los secretos. Veamos cómo funcionan los runners de GitLab y GitHub, cuántos recursos necesitan, cómo desplegar por SSH y dónde guardar las claves.
CI/CD es la compilación, la comprobación y el despliegue automáticos del código tras cada cambio. La definición del pipeline está en el repositorio (.gitlab-ci.yml o los archivos de .github/workflows/), y quien la ejecuta es el runner: un programa que recoge los trabajos de GitLab o GitHub y los lanza. El runner puede ser el del servicio o uno instalado en su propio VPS.
Runners propios: GitLab y GitHub
GitLab Runner se instala desde el repositorio oficial (instrucciones de instalación) y se registra en un proyecto o grupo con un token de los ajustes de CI/CD. La decisión principal al registrarlo es el ejecutor (executor). shell lanza los comandos directamente en el sistema del servidor; docker, en un contenedor nuevo para cada trabajo. Según la documentación de GitLab, con shell no hay un entorno limpio para cada compilación y los trabajos ven todo el sistema de archivos, incluidos el token del runner y la caché de otros proyectos. Para la mayoría de los proyectos el ejecutor adecuado es docker.
Un runner propio de GitHub Actions se añade en los ajustes del repositorio o de la organización (documentación de GitHub). Usarlo es gratis: solo paga el servidor. El runner se conecta por sí mismo a GitHub por HTTPS en el puerto 443, así que no hace falta abrir puertos de entrada en el servidor. Si el runner no se actualiza en 30 días, GitHub deja de asignarle trabajos (referencia de runners propios).
Una limitación importante para los proyectos abiertos. GitHub indica que los runners propios casi nunca deben usarse con repositorios públicos: cualquiera puede abrir un pull request y ejecutar su código en el servidor. Para el código público, quédese con los runners en la nube y conecte su servidor a repositorios privados.
Por qué la compilación y la producción deben ir en servidores distintos
- Carga. Compilar el frontend o el código ocupa todos los núcleos durante minutos. Si el runner está en el servidor de la web, mientras tanto la web responde más despacio.
- Seguridad. El runner ejecuta código de cualquier rama, incluida la que envía un contratista externo. Según la documentación de GitLab, el ejecutor shell supone un riesgo alto para el servidor y la red, y un contenedor en modo privileged da al trabajo acceso root completo al servidor. Si en ese mismo servidor funciona la web con la base de datos de clientes, cualquier trabajo tiene acceso a ella.
- Espacio en disco. Las imágenes de Docker, la caché de dependencias y los artefactos ocupan decenas de gigabytes. Cuando el disco se llena, no se detiene solo la compilación, sino también la web.
Un esquema para un equipo pequeño: un VPS para el runner, otro aparte para la web en producción y, si hace falta, un tercero para el entorno de pruebas (de él trata el artículo VPS para desarrolladores: entornos de pruebas). El runner solo lleva al servidor de producción el resultado ya compilado.
Cuántos recursos necesita la compilación
Una buena referencia son los runners en la nube de GitHub. Según su referencia, el runner Linux estándar para repositorios privados tiene 2 núcleos, 8 GB de memoria y 14 GB de SSD, y para los públicos, 4 núcleos y 16 GB. Si sus compilaciones caben en esos recursos, un VPS equivalente lo hará igual de bien, y con un disco rápido y la caché conservada, normalmente más rápido.
- Núcleos. Una compilación con compilación paralela y pruebas usa todos los núcleos. Si las compilaciones van una tras otra, 4 núcleos acortan la cola de forma notable frente a dos.
- Memoria. Compilar el frontend, compilar Java o Go y ejecutar pruebas con una base de datos en un contenedor requieren de 4 a 8 GB por trabajo. El número de trabajos simultáneos se fija con el parámetro
concurrenten elconfig.tomlde GitLab Runner (descripción de los parámetros); multiplique la memoria de un trabajo por ese número. - Disco. Instalar dependencias supone miles de archivos pequeños, y en NVMe va más rápido. Contrate a partir de 50 GB y limpie la caché de Docker de forma programada:
docker builder pruneydocker image prune. Por qué importa el disco se analiza en el artículo NVMe o SSD: ¿hay diferencia para una web? - Virtualización. El ejecutor docker necesita KVM: en OpenVZ, Docker funciona con limitaciones o no funciona en absoluto (KVM u OpenVZ: cuál elegir).
Según el catálogo de accsly.io a noviembre de 2026, un plan VPS con 2 núcleos y 4 GB de memoria cuesta en la mediana del mercado unos 21 USD (unos 18,5 EUR) al mes, y una cuarta parte de esos planes cuesta menos de 12,5 USD. Con 4 núcleos y 8 GB, unos 40 USD (unos 36 EUR), y una cuarta parte, menos de 22 USD. Con 8 núcleos y 16 GB, unos 80 USD. Los importes están convertidos de rublos al tipo del 7 de noviembre de 2026 y redondeados. Los planes adecuados están en las selecciones VPS para runners de CI/CD y VPS para GitLab, si además instala el propio GitLab.
Un servidor de compilación por horas
El runner solo hace falta mientras hay compilaciones, normalmente unas horas por jornada laboral. Según el catálogo de accsly.io a noviembre de 2026, 173 de los 2 248 proveedores con planes VPS ofrecen pago por horas, 298 tienen una API para crear y eliminar servidores y 96, ambas cosas. Un plan con 4 núcleos y 8 GB en los proveedores con pago por horas cuesta en la mediana del mercado unos 51 USD al mes, alrededor de 0,07 USD por hora. Un servidor que funciona 3 horas en cada uno de los 22 días laborables costará unos 4,6 USD en lugar del precio mensual.
Para que esto funcione sin intervención manual, el servidor se crea mediante la API antes de la compilación y se elimina después. GitLab Runner puede escalar así con complementos para proveedores en la nube, y GitHub recomienda runners efímeros (ephemeral), que aceptan un solo trabajo y se eliminan. Una ventaja añadida para la seguridad: el siguiente trabajo no ve rastros del anterior. Cuándo sale más a cuenta el pago por horas que el mensual se analiza en el artículo Pago por horas: cuándo sale a cuenta; los planes, en las selecciones VPS con pago por horas y VPS con API.
Despliegue por SSH con una clave restringida
La forma más sencilla de llevar la compilación al servidor de producción es SSH: rsync de los archivos o un comando que descarga la nueva versión y reinicia el servicio. Para que la clave de CI no se convierta en la llave de todo el servidor:
- Un usuario aparte. Cree un usuario
deploysin sudo y con permisos solo sobre el directorio de la web. Si hay que reiniciar un servicio, permita en sudoers un único comando concreto. - Una clave aparte. Genere un par de claves solo para CI y no lo use en ningún otro sitio.
- Restricciones en authorized_keys. Delante de la clave añada
restrict, que prohíbe el reenvío de puertos y la terminal, yfrom="dirección del runner", para que la clave solo funcione desde el servidor de compilación. El parámetrocommand="…"permite a la clave ejecutar únicamente un script de despliegue (man authorized_keys). - Verificación del servidor. Guarde la huella de la clave del servidor de producción en una variable de CI y escríbala en
known_hostsantes de conectarse. No desactive la verificación conStrictHostKeyChecking=no: así el runner se conectaría a cualquier servidor que se hiciera pasar por el suyo.
Para que el sitio no se interrumpa durante el despliegue, la nueva versión se arranca junto a la anterior y el tráfico se cambia cuando supera la comprobación de estado; la versión previa se conserva para revertir. Cómo montarlo en un solo servidor se explica en Actualizar un servidor sin tiempo de inactividad.
La protección básica del propio servidor de producción (acceso solo con clave, firewall, fail2ban) se describe en el artículo Primeros 10 pasos tras comprar un VPS: seguridad. Los registros de accesos con la clave de despliegue ayudarán a averiguar qué ha pasado si algo sale mal; dónde buscarlos se explica en el artículo Registros y auditoría de accesos en el servidor.
Dónde guardar los secretos
- Fuera del repositorio. Las contraseñas de la base de datos, los tokens de API y las claves de despliegue se guardan en variables de CI. En GitLab una variable puede ser protegida, y entonces solo la ven los pipelines de ramas y etiquetas protegidas, y enmascarada, y entonces su valor se sustituye por
[MASKED]en el registro del trabajo. Un valor enmascarado debe ser una sola línea sin espacios de al menos 8 caracteres (documentación de GitLab). - Un valor por secreto. GitHub advierte que un secreto en formato JSON o YAML se oculta peor en los registros; guarde cada valor por separado y no imprima secretos en el registro de la compilación.
- Secretos distintos para el entorno de pruebas y para producción. La clave de despliegue al servidor de producción solo debe estar disponible desde una rama protegida, no desde cualquier rama de un desarrollador.
- Tokens de corta duración en lugar de permanentes. Para servicios en la nube, GitHub recomienda OpenID Connect: el trabajo recibe un token durante la compilación y no hace falta guardar una clave permanente.
- Rotación. Cambie las claves cuando deje el equipo alguien con acceso a los ajustes de CI y tras cualquier filtración de un registro de compilación.
El archivo .env con la configuración de producción se crea en el servidor una sola vez, con permisos 600, y el despliegue no lo sobrescribe. Así, ni siquiera una copia completa del repositorio contiene las contraseñas de la base de datos de producción.