⚠ Vigencia por confirmar. Documento previo al estándar de documentación, migrado sin reescribir su contenido técnico. Revisar contra el código antes de usarlo como fuente de verdad.
Guía de Configuración CI/CD con Runners Self-Hosted¶
Esta es la guía genérica y reutilizable para configurar automatización CI/CD en cualquier proyecto usando GitHub Actions y runners self-hosted en servidores privados.
Arquitectura General¶
graph TB
subgraph GitHub["GitHub"]
Repo["Repository"]
Actions["GitHub Actions"]
GHCR["GHCR Registry"]
Secrets["Secrets"]
end
subgraph Staging["Servidor Staging"]
RunnerS["Runner Self-Hosted<br/>(staging label)"]
DockerS["Docker Engine"]
AppS["Aplicación"]
end
subgraph Production["Servidor Producción"]
RunnerP["Runner Self-Hosted<br/>(production label)"]
DockerP["Docker Engine"]
AppP["Aplicación"]
end
Repo -->|Push| Actions
Actions -->|Build Image| GHCR
Actions -->|Dispatch staging| RunnerS
RunnerS -->|Pull Image| GHCR
RunnerS -->|Deploy| DockerS
DockerS -->|Runs| AppS
Actions -->|Dispatch production| RunnerP
RunnerP -->|Pull Image| GHCR
RunnerP -->|Deploy| DockerP
DockerP -->|Runs| AppP
Secrets -.->|Auth| GHCR
Secrets -.->|SSH Keys| RunnerS
Secrets -.->|SSH Keys| RunnerP
Flujo de Despliegue¶
graph LR
A["Push a rama"] -->|build| B["Build imagen<br/>en GitHub"]
B -->|push| C["Publica a GHCR"]
subgraph "Staging (Automático)"
D["Runner staging<br/>ejecuta deploy.sh"]
end
subgraph "Production (Manual)"
E["Run workflow<br/>en GitHub"]
F["Runner production<br/>ejecuta deploy.sh"]
end
C -->|push a staging| D
C -->|workflow_dispatch<br/>en main| E
E -->|click Approve| F
style D fill:#90EE90
style F fill:#FFB6C6
Paso 1: Configurar Runner Self-Hosted¶
1.1 En el Servidor Staging¶
# 1. Crear directorio de trabajo
mkdir -p /opt/github-runner-staging
cd /opt/github-runner-staging
# 2. Descargar runner (verificar versión más reciente)
curl -o actions-runner-linux-x64.tar.gz -L \
https://github.com/actions/runner/releases/download/v2.317.0/actions-runner-linux-x64-2.317.0.tar.gz
tar xzf ./actions-runner-linux-x64.tar.gz
rm actions-runner-linux-x64.tar.gz
# 3. En GitHub: Settings → Actions → Runners → New self-hosted runner
# Copia el token (válido 1 hora)
# 4. Ejecutar configuración
./config.sh --url https://github.com/YOUR_ORG/YOUR_REPO --token <TOKEN>
# Respuestas:
# - name: staging
# - work folder: _work (default OK)
# - labels: staging ⚠️ IMPORTANTE: debe coincidir con runs-on
# - default (default OK)
1.2 En el Servidor Producción¶
Repite los pasos anteriores con:
- Directorio: /opt/github-runner-production
- Name: production
- Labels: production (o el label que uses en tu workflow)
1.3 Instalar como Servicio Systemd¶
cd /opt/github-runner-staging (o production)
# Instalar
sudo ./svc.sh install
# Iniciar
sudo systemctl start actions-runner
sudo systemctl enable actions-runner
# Verificar
sudo systemctl status actions-runner
sudo journalctl -u actions-runner -f # Ver logs en tiempo real
Verificación en GitHub: Ve a: Settings → Actions → Runners Debes ver ambos runners en verde ("Idle")
Paso 2: Configurar SSH para Git¶
El runner necesita poder hacer git fetch del repositorio sin contraseña.
2.1 Generar SSH Keys¶
⚠️ IMPORTANTE: Las SSH keys deben estar en el HOME del user del runner, NO en /opt/github-runner/.ssh/
Cuando configuras el runner con ./config.sh, systemd lo ejecuta como un user específico (ej: _svc_actions_staging). SSH busca las keys en ~/.ssh/ de ese user.
# Verificar home del user
sudo -u _svc_actions_staging echo ~
# Output: /home/_svc_actions_staging (o similar)
# Generar SSH key en el home correcto
sudo mkdir -p /home/_svc_actions_staging/.ssh
sudo ssh-keygen -t rsa -f /home/_svc_actions_staging/.ssh/id_rsa -N "" -C "staging-runner"
# Permisos correctos (CRÍTICO)
sudo chown -R _svc_actions_staging:_svc_actions_staging /home/_svc_actions_staging/.ssh
sudo chmod 700 /home/_svc_actions_staging/.ssh
sudo chmod 600 /home/_svc_actions_staging/.ssh/id_rsa
2.2 Agregar Public Key en GitHub¶
# Copiar la public key
sudo cat /home/_svc_actions_staging/.ssh/id_rsa.pub
En GitHub:
1. Repo → Settings → Deploy Keys
2. Click "Add deploy key"
3. Title: staging-runner
4. Key: (pega la output anterior)
5. ✅ Allow write access
6. Click "Add key"
2.3 Verificar SSH Access¶
# Test 1: Autenticación SSH
sudo -u _svc_actions_staging ssh -T git@github.com
# Output: "Hi YOUR_ORG/YOUR_REPO! You've successfully authenticated..."
# Test 2: Git clone
sudo -u _svc_actions_staging git clone git@github.com:YOUR_ORG/YOUR_REPO.git /tmp/test
rm -rf /tmp/test
Paso 3: Autenticar Acceso a GHCR (Registry Privado)¶
El runner necesita poder hacer docker login y docker pull de imágenes privadas.
3.1 Crear Personal Access Token (PAT)¶
En GitHub:
1. Settings → Developer settings → Personal access tokens (classic)
2. Click "Generate new token"
3. Nombre: ghcr-runner-pat
4. Permisos mínimos:
- ✅ read:packages (para pull)
- ✅ repo (para acceder al repo)
5. Copy el token (no lo pierdan, no se puede ver después)
3.2 Login a GHCR en el Runner¶
# Opción 1: Login interactivo (NO recomendado para runners)
docker login ghcr.io
# Opción 2: Via stdin (más seguro)
echo "YOUR_PAT_TOKEN" | docker login ghcr.io -u YOUR_GITHUB_USERNAME --password-stdin
# Verificar
docker pull ghcr.io/your-org/your-image:latest
# Logout cuando termines pruebas
docker logout ghcr.io
Para runners: El login se guarda en ~/.docker/config.json del user del runner. Systemd lo ejecuta como ese user, así que el login persiste entre execuciones.
Paso 4: Configurar GitHub Environments (Opcional)¶
Para requerir aprobación manual antes de deployar a producción (solo en GitHub Enterprise).
En GitHub:
1. Repo → Settings → Environments
2. Click "New environment"
3. Nombre: production
4. (Si es Enterprise) Click "Add protection rules"
- ✅ Require reviewers
- ✅ Restrict to selected branches: main
Nota: Plan Team/Pro no tiene "Require reviewers". Usa workflow_dispatch manual como alternativa.
Paso 5: Workflow CI/CD Estándar¶
name: Build & Deploy
on:
push:
branches: [staging, main]
workflow_dispatch:
inputs:
environment:
description: 'Environment to deploy'
required: true
default: 'staging'
type: choice
options:
- staging
- production
jobs:
build:
runs-on: ubuntu-latest
permissions:
packages: write
steps:
- uses: actions/checkout@v4
- name: Build and push image
run: |
docker build -t ghcr.io/your-org/your-image:tag .
docker push ghcr.io/your-org/your-image:tag
deploy-staging:
needs: build
runs-on: [self-hosted, staging] # Label debe coincidir
if: github.ref == 'refs/heads/staging'
steps:
- uses: actions/checkout@v4
- name: Deploy
run: ./deploy.sh
deploy-prod:
needs: build
runs-on: [self-hosted, production] # Label debe coincidir
if: github.event_name == 'workflow_dispatch' # SOLO manual
steps:
- uses: actions/checkout@v4
- name: Deploy
run: ./deploy.sh
Checklist de Configuración¶
- [ ] Runner staging instalado y registrado (Settings → Actions → Runners)
- [ ] Runner production instalado y registrado
- [ ] SSH keys generadas en el home correcto del user del runner
- [ ] Deploy Keys agregadas en GitHub para ambos runners
- [ ]
ssh -T git@github.comfunciona como el user del runner - [ ] PAT creado con permisos read:packages y repo
- [ ]
docker login ghcr.iofunciona en ambos servidores - [ ] Workflow
.github/workflows/build-deploy.ymlexiste - [ ] Labels en
runs-oncoinciden con los labels del runner - [ ] Primer push a staging y observar que
deploy-stagingcorre
Troubleshooting¶
Runner no aparece en GitHub¶
# En el servidor
sudo systemctl restart actions-runner
sudo journalctl -u actions-runner -f
# En GitHub: Settings → Actions → Runners
# Si sigue sin aparecer, ejecutar config.sh de nuevo con un token nuevo
SSH no funciona¶
# Verificar home del user
sudo -u _svc_actions_staging echo ~
# Verificar que las keys están en el lugar correcto
sudo ls -la /home/_svc_actions_staging/.ssh/
# Verificar permisos
sudo stat /home/_svc_actions_staging/.ssh/id_rsa
# Debe ser 0600 (-rw-------)
Docker login falla¶
# Verificar que el PAT tiene permisos correctos
# PAT debe tener: read:packages + repo
# Logout e intentar de nuevo
docker logout ghcr.io
echo "YOUR_PAT" | docker login ghcr.io -u USERNAME --password-stdin
# Verificar login funcionó
docker pull ghcr.io/your-org/your-image:test
Notas Importantes¶
- SSH Keys deben estar en el home correcto — No en
/opt/github-runner/.ssh/, sino en~/.ssh/del user que corre el runner - Labels deben coincidir —
runs-on: [self-hosted, staging]necesita un runner CON el labelstaging - PAT tiene expiration — GitHub PATs expiran por defecto. Renovar antes de que expire
- Systemd ejecuta como user específico — Las credenciales de Docker se guardan para ese user, no para root
¿Preguntas o aclaraciones necesarias?