Despliegue con Git en un servidor local (Red Local)

 Introducción

Ahora que entiendes que Git vive en el host y no en el contenedor, te explicaré cómo hacer el despliegue completo en un servidor local (una computadora en tu red local que has configurado como servidor). Este es un escenario muy común en entornos de desarrollo, pruebas o incluso producción pequeña.


Escenario: Servidor local en la red

Arquitectura del entorno

text
Tu red local (192.168.1.0/24)
├── Tu computadora de desarrollo (192.168.1.100)
│   ├── VS Code
│   ├── Git (cliente)
│   └── Código fuente
│
├── Servidor local (192.168.1.200)  ← ¡Este es tu "servidor de producción"!
│   ├── Sistema: Ubuntu Server 22.04
│   ├── Git (instalado en el host)
│   ├── Docker y Docker Compose
│   ├── Repositorio del proyecto (/home/usuario/mi-app)
│   └── Contenedores Docker (PHP, Nginx, MySQL)
│
└── Usuarios en la red
    └── Acceden a: http://192.168.1.200:8000

Paso 1: Configurar el servidor local

1.1 Instalar requisitos en el servidor

Conéctate a tu servidor local por SSH:

bash
ssh usuario@192.168.1.200

Instalar Git, Docker y Docker Compose:

bash
# Actualizar sistema
sudo apt update && sudo apt upgrade -y

# Instalar Git
sudo apt install git -y

# Instalar Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

# Agregar usuario al grupo docker
sudo usermod -aG docker $USER

# Instalar Docker Compose (plugin)
sudo apt install docker-compose-plugin -y

# Verificar instalaciones
git --version
docker --version
docker compose version

Importante: Reinicia la sesión SSH para que los cambios de grupo surtan efecto.

1.2 Crear estructura de directorios en el servidor

bash
# Crear directorio para la aplicación
mkdir -p /home/usuario/mi-app
cd /home/usuario/mi-app

Paso 2: Configurar el repositorio remoto (Git)

Opción A: Usar GitHub como repositorio central (Recomendado)

1. En tu computadora de desarrollo, crea el repositorio en GitHub:

  • Ve a GitHub.com y crea un nuevo repositorio (ej: mi-app-laravel)

  • Copia la URL del repositorio (HTTPS o SSH)

2. En el servidor local, clona el repositorio:

bash
# En el servidor (192.168.1.200)
cd /home/usuario
git clone https://github.com/tu-usuario/mi-app-laravel.git mi-app
cd mi-app

3. Configurar deploy key (opcional, para mayor seguridad):

bash
# Generar SSH key en el servidor
ssh-keygen -t ed25519 -C "servidor-local@mi-app"

# Mostrar la clave pública
cat ~/.ssh/id_ed25519.pub

# Copiar la clave y agregarla en GitHub:
# Settings → Deploy keys → Add deploy key

Opción B: Repositorio local en el servidor (Sin GitHub)

Si no quieres usar GitHub, puedes tener un repositorio central en el servidor mismo:

1. Crear un repositorio bare (central) en el servidor:

bash
# En el servidor
mkdir -p /home/usuario/repositorios/mi-app.git
cd /home/usuario/repositorios/mi-app.git
git init --bare

2. Desde tu computadora de desarrollo, agregar el remoto:

bash
# En tu computadora local
git remote add servidor ssh://usuario@192.168.1.200/home/usuario/repositorios/mi-app.git
git push servidor main

3. En el servidor, clonar desde el repositorio bare:

bash
# En el servidor
cd /home/usuario
git clone /home/usuario/repositorios/mi-app.git mi-app
cd mi-app

Paso 3: Configurar los archivos de Docker

3.1 Crear Dockerfile (en tu computadora de desarrollo y subirlo a Git)

docker/php/Dockerfile:

dockerfile
FROM php:8.2-fpm

# Instalar dependencias
RUN apt-get update -y && apt-get install -y \
    openssl zip unzip git curl libpng-dev libonig-dev libxml2-dev libzip-dev \
    && docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip

# Instalar Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

WORKDIR /var/www/html

# Copiar código (esto se sobrescribirá con el volumen)
COPY . .

# Instalar dependencias
RUN composer install --no-dev --optimize-autoloader --no-interaction

# Crear usuario www-data como propietario
RUN chown -R www-data:www-data /var/www/html \
    && chmod -R 755 /var/www/html/storage /var/www/html/bootstrap/cache

EXPOSE 9000
CMD ["php-fpm"]

3.2 Crear docker-compose.yml (para producción)

docker-compose.prod.yml:

yaml
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: docker/php/Dockerfile
    container_name: mi_app_php
    restart: unless-stopped
    volumes:
      - ./:/var/www/html
    networks:
      - app-network

  nginx:
    image: nginx:alpine
    container_name: mi_app_nginx
    restart: unless-stopped
    ports:
      - "8000:80"
    volumes:
      - ./:/var/www/html
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - app
    networks:
      - app-network

  db:
    image: mysql:8.0
    container_name: mi_app_db
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: ${DB_DATABASE:-laravel}
      MYSQL_USER: ${DB_USERNAME:-laravel}
      MYSQL_PASSWORD: ${DB_PASSWORD:-secret}
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-rootsecret}
    ports:
      - "3306:3306"
    volumes:
      - db_data:/var/lib/mysql
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

volumes:
  db_data:

3.3 Configurar Nginx (docker/nginx/default.conf)

nginx
server {
    listen 80;
    server_name localhost;
    root /var/www/html/public;

    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

3.4 Subir configuración a Git

Desde tu computadora de desarrollo:

bash
git add .
git commit -m "feat: add Docker configuration for production"
git push origin main

Paso 4: Desplegar en el servidor local

4.1 Clonar y preparar el entorno

En el servidor (192.168.1.200):

bash
# Si ya clonaste el repositorio
cd /home/usuario/mi-app

# O clonarlo si no lo has hecho
git clone https://github.com/tu-usuario/mi-app-laravel.git mi-app
cd mi-app

# Copiar .env.example a .env y configurarlo
cp .env.example .env

# Editar .env con las configuraciones para producción local
nano .env

Configuración típica para producción local:

env
APP_NAME=MiApp
APP_ENV=production
APP_DEBUG=false
APP_URL=http://192.168.1.200:8000

DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laravel
DB_PASSWORD=secret

# Cache en producción
CACHE_DRIVER=redis
SESSION_DRIVER=redis

4.2 Construir y levantar los contenedores

bash
# Construir la imagen de la aplicación
docker compose -f docker-compose.prod.yml build

# Levantar los servicios
docker compose -f docker-compose.prod.yml up -d

# Verificar que estén corriendo
docker compose -f docker-compose.prod.yml ps

Salida esperada:

text
NAME               IMAGE               COMMAND                  SERVICE   STATUS         PORTS
mi_app_php         mi-app-app          "docker-php-entrypoi…"   app       Up 2 minutes   9000/tcp
mi_app_nginx       nginx:alpine        "/docker-entrypoint.…"   nginx     Up 2 minutes   0.0.0.0:8000->80/tcp
mi_app_db          mysql:8.0           "docker-entrypoint.s…"   db        Up 2 minutes   0.0.0.0:3306->3306/tcp

4.3 Ejecutar migraciones y configuración inicial

bash
# Generar clave de aplicación
docker compose -f docker-compose.prod.yml exec app php artisan key:generate

# Ejecutar migraciones
docker compose -f docker-compose.prod.yml exec app php artisan migrate --force

# Optimizar para producción
docker compose -f docker-compose.prod.yml exec app php artisan config:cache
docker compose -f docker-compose.prod.yml exec app php artisan route:cache
docker compose -f docker-compose.prod.yml exec app php artisan view:cache

4.4 Acceder a la aplicación

Desde cualquier computadora en tu red local:

text
http://192.168.1.200:8000

Paso 5: Flujo de trabajo de actualización

5.1 Hacer cambios en desarrollo (tu computadora)

bash
# En tu computadora local
git add .
git commit -m "feat: add new feature"
git push origin main

5.2 Actualizar el servidor local

En el servidor (192.168.1.200):

bash
cd /home/usuario/mi-app

# 1. Descargar los últimos cambios
git pull origin main

# 2. Actualizar dependencias (si composer.json cambió)
docker compose -f docker-compose.prod.yml exec app composer install --no-dev --optimize-autoloader

# 3. Ejecutar migraciones (si hay nuevas)
docker compose -f docker-compose.prod.yml exec app php artisan migrate --force

# 4. Limpiar y regenerar caché
docker compose -f docker-compose.prod.yml exec app php artisan config:clear
docker compose -f docker-compose.prod.yml exec app php artisan config:cache
docker compose -f docker-compose.prod.yml exec app php artisan route:cache
docker compose -f docker-compose.prod.yml exec app php artisan view:cache

# 5. Reiniciar servicios (opcional)
docker compose -f docker-compose.prod.yml restart

echo "¡Actualización completada!"

5.3 Automatizar con un script (recomendado)

Crea un archivo deploy.sh en el servidor:

bash
#!/bin/bash
# deploy.sh - Script de despliegue automático

set -e  # Detener en caso de error

echo "🚀 Iniciando despliegue..."

# 1. Actualizar código
echo "📦 Actualizando código desde Git..."
git pull origin main

# 2. Actualizar dependencias
echo "📦 Actualizando dependencias..."
docker compose -f docker-compose.prod.yml exec app composer install --no-dev --optimize-autoloader

# 3. Ejecutar migraciones
echo "🗄️ Ejecutando migraciones..."
docker compose -f docker-compose.prod.yml exec app php artisan migrate --force

# 4. Optimizar caché
echo "⚡ Optimizando caché..."
docker compose -f docker-compose.prod.yml exec app php artisan config:cache
docker compose -f docker-compose.prod.yml exec app php artisan route:cache
docker compose -f docker-compose.prod.yml exec app php artisan view:cache

# 5. Reiniciar servicios
echo "🔄 Reiniciando servicios..."
docker compose -f docker-compose.prod.yml restart

echo "✅ ¡Despliegue completado exitosamente!"

Hacer ejecutable el script:

bash
chmod +x deploy.sh

Ejecutar:

bash
./deploy.sh

Paso 6: Automatización con Webhooks (Opcional)

Para que el servidor se actualice automáticamente cuando hagas git push:

6.1 Crear un endpoint webhook en el servidor

Crea un archivo webhook.php en el servidor:

php
<?php
// webhook.php - Endpoint para GitHub webhooks
// Ubicación: /home/usuario/mi-app/webhook.php

// Verificar secreto (opcional)
$secret = 'tu-secreto-webhook';
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_HUB_SIGNATURE_256'] ?? '';

// Validar firma
if ($secret && $signature) {
    $computed = 'sha256=' . hash_hmac('sha256', $payload, $secret);
    if (!hash_equals($computed, $signature)) {
        http_response_code(401);
        die('Firma inválida');
    }
}

// Ejecutar script de despliegue
$output = shell_exec('cd /home/usuario/mi-app && ./deploy.sh 2>&1');
echo "Despliegue ejecutado:\n" . $output;

6.2 Configurar webhook en GitHub

  1. Ve a Settings → Webhooks en tu repositorio de GitHub

  2. Add webhook

  3. Payload URL: http://192.168.1.200:8000/webhook.php

  4. Content type: application/json

  5. Secret: El que configuraste en el script

  6. Events: Just the push event

6.3 Consideraciones de seguridad

El webhook debe estar protegido (no accesible desde internet). Para un servidor local es aceptable, pero en producción:

  • Usa autenticación por IP (solo acepta peticiones de GitHub)

  • Usa HTTPS

  • Usa el secreto de verificación


Paso 7: Resolución de problemas comunes

⚠️ Error: "Permission denied" al hacer git pull

Causa: El usuario no tiene permisos en el directorio.

Solución:

bash
# En el servidor
sudo chown -R $USER:$USER /home/usuario/mi-app
chmod -R 755 /home/usuario/mi-app

⚠️ Error: "Could not connect to database"

Causa: El contenedor de MySQL no está listo.

Solución: Esperar unos segundos o usar healthcheck:

yaml
# En docker-compose.prod.yml
db:
  healthcheck:
    test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
    interval: 10s
    timeout: 5s
    retries: 5

⚠️ Error: "No such file or directory" al ejecutar deploy.sh

Causa: El script no tiene permisos de ejecución.

Solución:

bash
chmod +x deploy.sh

⚠️ El contenedor no ve los archivos actualizados

Causa: El volumen no está montado correctamente.

Solución:

yaml
# Asegurarse de que el volumen monta el directorio correcto
volumes:
  - ./:/var/www/html  # Monta todo el directorio actual

c


Conclusión

¡Y así es como despliegas tu aplicación Laravel con Docker usando Git en un servidor local!

Puntos clave:

  1. Git está en el host (servidor local) - No en el contenedor

  2. El código se sincroniza - Los archivos del host se montan en el contenedor

  3. Actualización simple - git pull + docker compose es todo lo que necesitas

  4. Automatización - Un script deploy.sh hace todo el trabajo

  5. Sin FTP - Todo se maneja con Git

La belleza de este enfoque es que funciona igual en un servidor local, en la nube (AWS, DigitalOcean) o en cualquier otro lugar. El flujo es el mismo: host con Git → contenedores con Docker.

¡Felicidades! 🎉 Ahora tienes un pipeline completo de despliegue para tu entorno local


Comentarios

Entradas más populares de este blog

11-intro

12. Exposición sobre los comandos usados hasta el momento

13. Cambiar nombre de la rama Master a Main