From ede7d7442c269420d56d1cf11cc4563fe670ef6f Mon Sep 17 00:00:00 2001 From: Anghelo Flores Date: Mon, 16 Feb 2026 15:18:04 -0500 Subject: [PATCH] feat: add docker --- .env.prod.example | 8 +- .github/workflows/deploy.yml | 87 +++++ .gitignore | 1 + back/.env.example | 2 +- back/Dockerfile | 11 +- .../database/seeders/RolePermissionSeeder.php | 0 docker-compose.prod.yml | 12 +- install-dev.md | 213 +++++++++++ install-prod.md | 362 ++++++++++++++++++ 9 files changed, 680 insertions(+), 16 deletions(-) create mode 100644 .github/workflows/deploy.yml delete mode 100644 back/database/seeders/RolePermissionSeeder.php create mode 100644 install-dev.md create mode 100644 install-prod.md diff --git a/.env.prod.example b/.env.prod.example index 6056a13..22e3f70 100644 --- a/.env.prod.example +++ b/.env.prod.example @@ -10,8 +10,8 @@ APP_KEY= APP_DEBUG=false APP_URL=https://tu-dominio.com -# Puerto donde se expone la app (default: 80) -APP_PORT=80 +# Puerto interno donde Docker escucha (el nginx del host hace proxy a este puerto) +APP_PORT=8080 # --- Base de datos --- DB_CONNECTION=mysql @@ -52,6 +52,10 @@ LOG_CHANNEL=stack LOG_STACK=single LOG_LEVEL=error +# --- Docker / Registry --- +GITHUB_REPO=tu-usuario/tu-repo +IMAGE_TAG=latest + # --- Misc --- BCRYPT_ROUNDS=12 FILESYSTEM_DISK=local diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..7fb09c2 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,87 @@ +name: Build & Deploy + +on: + workflow_dispatch: + inputs: + deploy: + description: 'Desplegar en el VPS despues de construir' + required: true + default: true + type: boolean + +env: + REGISTRY: ghcr.io + BACKEND_IMAGE: ghcr.io/${{ github.repository }}/backend + FRONTEND_IMAGE: ghcr.io/${{ github.repository }}/frontend + +jobs: + build: + name: Build & Push Images + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build & push backend + uses: docker/build-push-action@v5 + with: + context: ./back + push: true + tags: | + ${{ env.BACKEND_IMAGE }}:latest + ${{ env.BACKEND_IMAGE }}:${{ github.sha }} + + - name: Build & push frontend + uses: docker/build-push-action@v5 + with: + context: ./front + push: true + build-args: | + VITE_API_URL=/api + tags: | + ${{ env.FRONTEND_IMAGE }}:latest + ${{ env.FRONTEND_IMAGE }}:${{ github.sha }} + + deploy: + name: Deploy to VPS + needs: build + if: ${{ inputs.deploy }} + runs-on: ubuntu-latest + + steps: + - name: Deploy via SSH + uses: appleboy/ssh-action@v1 + with: + host: ${{ secrets.VPS_HOST }} + username: ${{ secrets.VPS_USER }} + key: ${{ secrets.VPS_SSH_KEY }} + port: ${{ secrets.VPS_PORT || 22 }} + script: | + cd ${{ secrets.VPS_PROJECT_PATH }} + + # Login al registry + echo ${{ secrets.CR_PAT }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin + + # Descargar imagenes nuevas + docker pull ${{ env.BACKEND_IMAGE }}:latest + docker pull ${{ env.FRONTEND_IMAGE }}:latest + + # Reiniciar servicios + docker compose --env-file .env.prod -f docker-compose.prod.yml up -d --force-recreate backend frontend nginx + + # Ejecutar migraciones si hay pendientes + docker exec admision_prod_backend php artisan migrate --force + + # Limpiar imagenes viejas + docker image prune -f diff --git a/.gitignore b/.gitignore index d5673e3..41ad741 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ public_html/hot storage/*.key .env +.env.prod Homestead.yaml Homestead.json /.vagrant diff --git a/back/.env.example b/back/.env.example index c51cacb..fdfaea6 100644 --- a/back/.env.example +++ b/back/.env.example @@ -1,6 +1,6 @@ APP_NAME=Laravel APP_ENV=local -APP_KEY=base64:wEHrK9znRe1M/jdkBIM7b+TihyUtHygp8hKeqE57eB4= +APP_KEY= APP_DEBUG=true APP_URL=http://localhost:8000 diff --git a/back/Dockerfile b/back/Dockerfile index 80ce693..699c987 100644 --- a/back/Dockerfile +++ b/back/Dockerfile @@ -62,11 +62,14 @@ COPY --from=vendor /app/vendor ./vendor RUN chown -R www-data:www-data storage bootstrap/cache \ && chmod -R 775 storage bootstrap/cache -# Laravel optimizations -RUN php artisan config:cache \ - && php artisan route:cache \ - && php artisan view:cache +# View cache (no depende de .env, se puede hacer en build) +RUN php artisan view:cache + +# Entrypoint: cachear config y rutas con las variables reales de .env.prod +RUN printf '#!/bin/sh\nphp artisan config:cache\nphp artisan route:cache\nexec "$@"\n' > /usr/local/bin/entrypoint.sh \ + && chmod +x /usr/local/bin/entrypoint.sh EXPOSE 9000 +ENTRYPOINT ["entrypoint.sh"] CMD ["php-fpm"] diff --git a/back/database/seeders/RolePermissionSeeder.php b/back/database/seeders/RolePermissionSeeder.php deleted file mode 100644 index e69de29..0000000 diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 13bd4fa..19f8ebb 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -4,7 +4,7 @@ services: container_name: admision_prod_nginx restart: unless-stopped ports: - - "${APP_PORT:-80}:80" + - "127.0.0.1:${APP_PORT:-8080}:80" volumes: - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro - backend_storage:/var/www/html/storage/app/public:ro @@ -15,9 +15,7 @@ services: - admision_net backend: - build: - context: ./back - dockerfile: Dockerfile + image: ghcr.io/${GITHUB_REPO:-usuario/repo}/backend:${IMAGE_TAG:-latest} container_name: admision_prod_backend restart: unless-stopped env_file: @@ -31,11 +29,7 @@ services: - admision_net frontend: - build: - context: ./front - dockerfile: Dockerfile - args: - VITE_API_URL: ${VITE_API_URL:-/api} + image: ghcr.io/${GITHUB_REPO:-usuario/repo}/frontend:${IMAGE_TAG:-latest} container_name: admision_prod_frontend restart: unless-stopped networks: diff --git a/install-dev.md b/install-dev.md new file mode 100644 index 0000000..46ad093 --- /dev/null +++ b/install-dev.md @@ -0,0 +1,213 @@ +# Instalacion Local (Desarrollo) - Direccion de Admision 2026 + +## Requisitos previos + +| Herramienta | Version minima | Uso | +|---|---|---| +| Laragon | - | PHP + Composer (servidor local) | +| PHP | 8.4+ | Backend Laravel (Thread Safe, VS17 x64) | +| Composer | 2.x | Dependencias PHP | +| Node.js | 18+ | Frontend Vue | +| npm | 9+ | Dependencias JS | +| Docker | 20+ | Solo para MySQL | +| Docker Compose | 2+ | Solo para MySQL | + +### Descargar PHP 8.4 para Laragon + +Si Laragon no trae PHP 8.4, descargarlo manualmente: + +1. Descargar desde https://windows.php.net/download/ el archivo **VS17 x64 Thread Safe** (zip) +2. Extraer en `C:\laragon\bin\php\php-8.4.xx-Win32-vs17-x64\` +3. En Laragon: clic derecho > **PHP** > seleccionar la version 8.4 +4. Instalar Visual C++ 2022 si no lo tienes: https://aka.ms/vs/17/release/vc_redist.x64.exe +5. Agregar al PATH: clic derecho en Laragon > **Tools** > **Path** > **Add Laragon to Path** + +--- + +## Paso 1: Clonar el repositorio + +```bash +git clone direccion_de_admision_2026 +cd direccion_de_admision_2026 +``` + +--- + +## Paso 2: Levantar MySQL con Docker + +```bash +docker compose up -d +``` + +Esto levanta un contenedor MySQL 8.0 con: +- **Host:** 127.0.0.1 +- **Puerto:** 3306 +- **Usuario:** root +- **Password:** root +- **Base de datos:** admision_2026 + +Verificar que funcione: + +```bash +docker exec admision_2026_db mysql -uroot -proot -e "SELECT 'MySQL OK'" +``` + +--- + +## Paso 3: Importar la base de datos + +```bash +docker exec -i admision_2026_db mysql -uroot -proot admision_2026 < admision_2026_estructura.sql +``` + +Verificar tablas importadas: + +```bash +docker exec admision_2026_db mysql -uroot -proot admision_2026 -e "SHOW TABLES;" +``` + +Debe mostrar ~31 tablas (users, postulantes, areas, cursos, examenes, procesos_admision, etc.) + +> **Nota:** El dump ya incluye los registros en la tabla `migrations`, por lo que no es necesario ejecutar `php artisan migrate`. + +--- + +## Paso 4: Configurar el Backend (Laravel) + +```bash +cd back +``` + +### 4.1 Instalar dependencias + +```bash +composer install +``` + +### 4.2 Configurar variables de entorno + +```bash +copy .env.example .env +``` + +Editar `back/.env` y configurar: + +``` +DB_PASSWORD=root +``` + +### 4.3 Generar clave de aplicacion + +```bash +php artisan key:generate +``` + +### 4.4 Crear enlace de storage (para imagenes) + +```bash +php artisan storage:link +``` + +### 4.5 Ejecutar seeders + +```bash +php artisan db:seed --class=RoleSeeder +``` + +Esto crea los roles: `usuario`, `administrador`, `superadmin`. + +> **Nota:** Solo ejecutar `php artisan migrate` si en el futuro se agregan nuevas migraciones al proyecto. + +### 4.6 Asignar rol a tu usuario + +Si ya tienes un usuario creado (por registro o por el dump SQL), asignarle un rol desde tinker: + +```bash +php artisan tinker +``` + +```php +$user = \App\Models\User::where('email', 'tu@email.com')->first(); +$user->assignRole('superadmin'); +exit +``` + +> **Importante:** Sin un rol asignado, el login devuelve 200 pero no redirige al dashboard. + +--- + +## Paso 5: Configurar el Frontend (Vue 3) + +```bash +cd front +``` + +### 5.1 Instalar dependencias + +```bash +npm install +``` + +### 5.2 Configurar variables de entorno + +```bash +copy .env.example .env +``` + +El archivo debe contener: + +``` +VITE_API_URL=http://127.0.0.1:8000/api +``` + +--- + +## Levantar el proyecto + +Necesitas **3 cosas corriendo** al mismo tiempo: + +### 1. MySQL (Docker) - si no esta corriendo + +```bash +docker compose up -d +``` + +### 2. Backend (Terminal 1) + +```bash +cd back +php artisan serve +``` + +Estara disponible en: **http://localhost:8000** + +### 3. Frontend (Terminal 2) + +```bash +cd front +npm run dev +``` + +Estara disponible en: **http://localhost:5173** + +--- + +## Stack tecnologico + +| Capa | Tecnologia | +|---|---| +| Backend | Laravel 12 + PHP 8.4 | +| Frontend | Vue 3 + Vite 7 + Ant Design Vue + Pinia | +| Base de datos | MySQL 8.0 (Docker) | +| Autenticacion | Laravel Sanctum (Bearer tokens) | +| Permisos | Spatie Laravel Permission | +| QR | SimpleSoftwareIO QRCode | + +--- + +## Notas importantes + +- **No modificar** `composer.lock` a menos que todos los del equipo acuerden (requiere PHP 8.4+) +- El dump SQL (`admision_2026_estructura.sql`) incluye la estructura de todas las tablas + registros en `migrations`. No es necesario ejecutar `php artisan migrate` a menos que se hayan agregado nuevas migraciones al proyecto. +- Si el puerto 3306 esta ocupado (por otro MySQL local), detener ese servicio primero o cambiar el puerto en `docker-compose.yml` +- Los archivos `.env` no se suben al repositorio (estan en `.gitignore`) diff --git a/install-prod.md b/install-prod.md new file mode 100644 index 0000000..f25bba8 --- /dev/null +++ b/install-prod.md @@ -0,0 +1,362 @@ +# Deploy en Produccion (Docker + CI/CD) - Direccion de Admision 2026 + +Todo el proyecto se dockeriza y se despliega mediante **GitHub Actions**. Las imagenes se construyen en GitHub y se suben a GitHub Container Registry (GHCR). El VPS solo descarga y ejecuta. + +## Resumen rapido — ¿Que hago yo? + +### Setup inicial (solo una vez, ~25 min) + +| Paso | Que haces | Donde | Tiempo | +|---|---|---|---| +| 1 | Pegar 6 secrets en GitHub | Navegador (GitHub Settings) | 5 min | +| 2 | Conectar al VPS, clonar repo, crear `.env.prod` y editar valores | Terminal SSH | 10 min | +| 3 | Copiar/pegar 2 comandos para MySQL + importar dump | Terminal SSH | 2 min | +| 4 | Clic en "Run workflow" en GitHub Actions y esperar | Navegador (GitHub Actions) | 3 min | +| 5 | Copiar/pegar 2 comandos (storage link + seeders) | Terminal SSH | 1 min | +| 6 | Copiar/pegar server block de nginx + correr certbot | Terminal SSH | 5 min | + +### Deploys del dia a dia + +| Tu haces | Que pasa | +|---|---| +| `git push` a main | Nada (hasta que tu decidas) | +| Clic en **"Run workflow"** en GitHub Actions | Build + Push al registry + Deploy al VPS (~3 min) | + +**No tocas terminal, no compilas, no haces SSH. Solo un clic desde el navegador.** + +--- + +## Arquitectura de deploy + +``` + Tu PC GitHub GHCR VPS + | | | | + | git push | | | + | ──────────► [main branch] | | + | | | | + | [GitHub Actions] | | + | (trigger manual) | | + | | | | + | Build backend ──────► ghcr.io/.../backend | + | Build frontend ──────► ghcr.io/.../frontend | + | | | | + | SSH al VPS ────────────────────────────► | + | | | docker pull | + | | | docker up | + | | | App lista | +``` + +## Arquitectura de contenedores + +``` + Puerto 80/443 + | + [nginx del host] ← SSL/Certbot + | + proxy_pass + | + 127.0.0.1:8080 + | + [nginx container] (reverse proxy) + / \ + /api/* / \ /* + / \ + [backend] [frontend] + PHP-FPM nginx+SPA + :9000 :80 + | + [mysql] + :3306 +``` + +--- + +## De donde sale cada credencial + +Hay 3 lugares donde se ponen credenciales. Esta tabla explica cada una y como obtenerla: + +### A. Archivo `.env.prod` (en el VPS) + +| Credencial | Como la obtienes | +|---|---| +| `APP_KEY` | Ejecutas `php -r "echo 'base64:'.base64_encode(random_bytes(32)).PHP_EOL;"` en tu PC y copias el resultado | +| `DB_PASSWORD` | La inventas tu. Para generar una segura: `openssl rand -base64 32` | +| `APP_URL` | Tu dominio (ej: `https://admision.tu-universidad.edu.pe`) | +| `SESSION_DOMAIN` | Tu dominio sin https (ej: `admision.tu-universidad.edu.pe`) | +| `SANCTUM_STATEFUL_DOMAINS` | Igual que SESSION_DOMAIN | +| `GITHUB_REPO` | Tu usuario/repo de GitHub (ej: `juanperez/direccion_de_admision_2026`) | +| `MAIL_PASSWORD` | Te lo da tu proveedor SMTP (Gmail, Mailgun, etc.) | + +### B. GitHub Secrets (en el navegador) + +| Secret | Como lo obtienes | +|---|---| +| `VPS_HOST` | La IP de tu droplet, la ves en el panel de DigitalOcean | +| `VPS_USER` | Normalmente `root` | +| `VPS_SSH_KEY` | El contenido de la clave privada SSH (ver paso 1 abajo) | +| `VPS_PORT` | Normalmente `22` | +| `VPS_PROJECT_PATH` | La ruta donde clonas el repo (ej: `/root/direccion_de_admision_2026`) | +| `CR_PAT` | Token que generas en GitHub > Settings > Developer settings > Tokens | + +### C. Se generan automaticamente (no los tocas) + +| Credencial | Descripcion | +|---|---| +| `GITHUB_TOKEN` | GitHub Actions lo crea solo en cada ejecucion | +| `MYSQL_ROOT_PASSWORD` | Docker lo lee de `DB_PASSWORD` del `.env.prod` | + +--- + +## Setup inicial (solo la primera vez) + +### 1. Crear clave SSH en el VPS y configurar secrets en GitHub + +GitHub Actions necesita conectarse al VPS por SSH. Si actualmente entras con contraseña, necesitas crear una clave SSH. + +**En el VPS** (conectate con tu contraseña como siempre): + +```bash +# Generar clave SSH para GitHub Actions +ssh-keygen -t ed25519 -C "github-actions-deploy" -f ~/.ssh/github_actions -N "" + +# Agregar la clave publica a las claves autorizadas +cat ~/.ssh/github_actions.pub >> ~/.ssh/authorized_keys + +# Mostrar la clave PRIVADA (la que copias a GitHub) +cat ~/.ssh/github_actions +``` + +> Copia TODO el contenido que muestra el ultimo comando (desde `-----BEGIN` hasta `-----END`). Esa es la clave que pegas en GitHub como secret `VPS_SSH_KEY`. + +**En GitHub** (navegador): + +1. Ir a tu repo > **Settings** > **Secrets and variables** > **Actions** +2. Clic en **"New repository secret"** y agregar uno por uno: + +| Secret | Que pegar | +|---|---| +| `VPS_HOST` | La IP de tu droplet (ej: `164.92.xxx.xxx`) | +| `VPS_USER` | `root` | +| `VPS_SSH_KEY` | El contenido completo de `~/.ssh/github_actions` (clave privada) | +| `VPS_PORT` | `22` | +| `VPS_PROJECT_PATH` | `/root/direccion_de_admision_2026` | +| `CR_PAT` | El token generado en el siguiente paso | + +### Generar el CR_PAT (Personal Access Token) + +1. Ir a GitHub > tu foto (esquina superior derecha) > **Settings** +2. Ir a **Developer settings** > **Personal access tokens** > **Tokens (classic)** +3. Clic en **"Generate new token (classic)"** +4. Nombre: `deploy-admision` | Expiracion: 90 dias o sin expiracion +5. Marcar permisos: `read:packages`, `write:packages` +6. Clic en **"Generate token"** +7. Copiar el token y guardarlo como secret `CR_PAT` en el repo + +--- + +### 2. Preparar el VPS + +**En el VPS** (por SSH): + +```bash +# Instalar Docker si no lo tienes +curl -fsSL https://get.docker.com | sh + +# Clonar el repo +git clone direccion_de_admision_2026 +cd direccion_de_admision_2026 + +# Configurar variables de entorno +cp .env.prod.example .env.prod +``` + +Editar `.env.prod`: + +```bash +nano .env.prod +``` + +Cambiar estos valores **obligatoriamente**: + +``` +APP_KEY= # Pegar la clave generada (ver abajo) +APP_URL=https://tu-dominio.com +DB_PASSWORD= # Pegar la password generada (ver abajo) +SESSION_DOMAIN=tu-dominio.com +SANCTUM_STATEFUL_DOMAINS=tu-dominio.com +GITHUB_REPO=tu-usuario/tu-repo +``` + +Para generar APP_KEY (en tu PC local si no hay PHP en el VPS): + +```bash +php -r "echo 'base64:'.base64_encode(random_bytes(32)).PHP_EOL;" +``` + +Para generar DB_PASSWORD: + +```bash +openssl rand -base64 32 +``` + +Asegurar permisos del archivo: + +```bash +chmod 600 .env.prod +``` + +### Credenciales de la base de datos + +La base de datos MySQL se crea automaticamente con estos valores (definidos en `.env.prod`): + +| Variable | Valor | Descripcion | +|---|---|---| +| `DB_HOST` | `mysql` | Nombre del contenedor Docker (no cambiar) | +| `DB_PORT` | `3306` | Puerto interno de MySQL (no cambiar) | +| `DB_DATABASE` | `admision_2026` | Nombre de la base de datos | +| `DB_USERNAME` | `root` | Usuario de MySQL | +| `DB_PASSWORD` | Lo que pongas en `.env.prod` | La misma password para todo: MySQL root y conexion Laravel | + +> **Importante:** La password que pongas en `DB_PASSWORD` es la que usa Docker para crear el usuario root de MySQL **y** la que usa Laravel para conectarse. Debe ser la misma en ambos casos porque es una sola variable. Esta misma password es la que usas en los comandos de importacion donde dice `TU_PASSWORD`. + +--- + +### 3. Levantar MySQL e importar la base de datos + +```bash +docker compose --env-file .env.prod -f docker-compose.prod.yml up -d mysql +``` + +Esperar ~10 segundos y luego importar: + +```bash +docker exec -i admision_prod_db mysql -uroot -pTU_PASSWORD admision_2026 < admision_2026_estructura.sql +``` + +Verificar: + +```bash +docker exec admision_prod_db mysql -uroot -pTU_PASSWORD admision_2026 -e "SHOW TABLES;" +``` + +--- + +### 4. Primer deploy + +Ir a GitHub > pestaña **Actions** > seleccionar **"Build & Deploy"** > clic en **"Run workflow"** > marcar **"Desplegar en el VPS"** > confirmar. + +GitHub Actions construira las imagenes, las subira al registry y las desplegara en el VPS. + +--- + +### 5. Post-deploy (solo la primera vez) + +```bash +# Crear enlace de storage +docker exec admision_prod_backend php artisan storage:link + +# Ejecutar seeders +docker exec admision_prod_backend php artisan db:seed --class=RoleSeeder --force +``` + +--- + +### 6. Configurar nginx del host como proxy + +Crear o editar el server block: + +```bash +sudo nano /etc/nginx/sites-available/tu-dominio.com +``` + +```nginx +server { + listen 80; + server_name tu-dominio.com; + + location / { + proxy_pass http://127.0.0.1:8080; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + client_max_body_size 25M; + } +} +``` + +```bash +sudo ln -sf /etc/nginx/sites-available/tu-dominio.com /etc/nginx/sites-enabled/ +sudo nginx -t && sudo systemctl reload nginx +``` + +Para HTTPS: + +```bash +sudo certbot --nginx -d tu-dominio.com +``` + +--- + +## Deploys posteriores + +Solo necesitas ir a GitHub > **Actions** > **Run workflow**. Eso es todo. + +O si prefieres activar deploy automatico en cada push a main, edita `.github/workflows/deploy.yml` y agrega: + +```yaml +on: + workflow_dispatch: + push: + branches: [main] +``` + +--- + +## Comandos utiles en el VPS + +```bash +# Ver logs +docker compose --env-file .env.prod -f docker-compose.prod.yml logs -f + +# Ver logs de un servicio +docker compose --env-file .env.prod -f docker-compose.prod.yml logs -f backend + +# Reiniciar todo +docker compose --env-file .env.prod -f docker-compose.prod.yml restart + +# Detener todo +docker compose --env-file .env.prod -f docker-compose.prod.yml down + +# Ejecutar comandos artisan +docker exec admision_prod_backend php artisan migrate --force +docker exec admision_prod_backend php artisan cache:clear + +# Ver que imagenes estan corriendo +docker compose --env-file .env.prod -f docker-compose.prod.yml ps +``` + +--- + +## Rollback (volver a una version anterior) + +Cada imagen se etiqueta con el SHA del commit. Para volver a una version anterior: + +```bash +# En .env.prod cambiar IMAGE_TAG al SHA del commit deseado +IMAGE_TAG=abc123def456 + +# Recrear contenedores +docker compose --env-file .env.prod -f docker-compose.prod.yml up -d --force-recreate backend frontend nginx +``` + +--- + +## Notas de produccion + +- Los datos de MySQL se persisten en un volumen Docker (`mysql_data`) +- El storage de Laravel se persiste en un volumen Docker (`backend_storage`) +- El archivo `.env.prod` **nunca** debe subirse al repositorio (esta en `.gitignore`) +- Siempre usar `--env-file .env.prod` en los comandos de Docker Compose +- Las imagenes en GHCR son **privadas** (solo tu cuenta las ve) +- Para HTTPS, usar Certbot en el nginx del host