API Laravel — Despliegue en AWS EC2 con CI/CD
Esta memoria técnica documenta el proceso completo para levantar una API REST en Laravel en un servidor AWS EC2, exponiéndola a través de Apache 2 con PHP 8.2 y MariaDB como base de datos. Además, se implementó un pipeline de CI/CD con GitHub Actions que automatiza la instalación de dependencias, la compilación de assets con Vite y el despliegue hacia el servidor.
Ver API en el servidor EC2Objetivo del proyecto
El objetivo principal fue diseñar e implementar una API Laravel genérica lista para producción, desplegada en AWS EC2, con:
- Entorno Linux (Ubuntu) optimizado para Laravel.
- Configuración de Apache y VirtualHost apuntando a
public/. - Base de datos MariaDB con usuario y permisos dedicados creados desde consola SQL.
- Pipeline de CI/CD que compile Vite y despliegue al servidor sin intervención manual.
- Manejo correcto de .env, permisos en
storage/y caches de Laravel.
Arquitectura general
Cliente (browser / API client)
↓ HTTP / HTTPS
Apache 2 (VirtualHost)
↓ DocumentRoot: /var/www/laravel-api-frutas/public
PHP 8.2
↓
Aplicación Laravel 12 (API REST)
↓
MariaDB (instancia local en EC2)
- Servidor: AWS EC2 con Ubuntu Server 22.04 LTS.
- Web server: Apache 2, módulo
rewriteactivado. - Aplicación: Laravel (última versión 12.x) con rutas de API y vistas de autenticación.
- BD: MariaDB local en la misma instancia EC2.
Tecnologías y herramientas
- Laravel 12 — Framework backend principal.
- PHP 8.2 — Runtime configurado con extensiones:
mbstring,bcmath,intl,pdo_mysql,curl,xml,ctype,json,tokenizer. - Composer — Gestión de dependencias PHP.
- Node.js 20 + npm — Entorno para Vite.
- Vite — Bundler de assets (genera
public/build/manifest.json). - MariaDB — Base de datos relacional.
- Git & GitHub — Control de versiones y remoto.
- GitHub Actions — Pipeline de CI/CD.
- SSH (llaves públicas/privadas) — Acceso seguro y despliegue automático.
Preparación del servidor EC2 y VirtualHost de Apache
En la instancia EC2 se realizaron los siguientes pasos para preparar el entorno de Laravel:
- Actualización del sistema:
sudo apt update && sudo apt upgrade -y. - Instalación base:
apache2,git,curl,unzip. - Instalación de PHP 8.2 y extensiones requeridas.
- Instalación de MariaDB y configuración con
sudo mysql_secure_installation. - Clonado del proyecto en
/var/www/laravel-api-frutas.
Posteriormente se definió un VirtualHost dedicado para la API:
<VirtualHost *:80>
ServerName 78.13.228.8
DocumentRoot /var/www/laravel-api-frutas/public
<Directory /var/www/laravel-api-frutas/public>
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/laravel-api-error.log
CustomLog ${APACHE_LOG_DIR}/laravel-api-access.log combined
</VirtualHost>
- Guardado en
/etc/apache2/sites-available/laravel-api.conf. - Habilitado con
sudo a2ensite laravel-apiy deshabilitado el sitio por defecto si era necesario. - Activado el módulo
rewrite:sudo a2enmod rewrite. - Reinicio de Apache:
sudo systemctl restart apache2.
Configuración de la base de datos
La base de datos se configuró directamente desde la consola de MariaDB, sin herramientas externas:
- Ingreso a la consola:
sudo mariadb(omysql -u root -p). - Creación de la base de datos para la API:
CREATE DATABASE api_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - Creación de un usuario específico para la aplicación:
CREATE USER 'api_user'@'localhost' IDENTIFIED BY '********'; - Asignación de permisos:
GRANT ALL PRIVILEGES ON api_db.* TO 'api_user'@'localhost'; - Refresco de privilegios:
FLUSH PRIVILEGES; - Configuración del
.envde Laravel conDB_DATABASE,DB_USERNAMEyDB_PASSWORDcorrectos. - Ejecución de migraciones desde el pipeline:
php artisan migrate --force.
Pipeline de CI/CD con GitHub Actions
El despliegue se automatizó con un workflow llamado
Deploy Laravel API to EC2 ubicado en
.github/workflows/deploy.yml:
name: Deploy Laravel API to EC2
on:
push:
branches:
- main
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
env:
APP_DIR: /var/www/laravel-api-frutas
steps:
# 1) Descargar código del repo
- name: Checkout code
uses: actions/checkout@v4
# 2) Configurar PHP 8.2 + Composer
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
extensions: mbstring, bcmath, intl, pdo_mysql, curl, xml, ctype, json, tokenizer
coverage: none
tools: composer
# 3) Cache de dependencias de Composer
- name: Cache Composer dependencies
uses: actions/cache@v4
with:
path: vendor
key: composer-${{ hashFiles('composer.lock') }}
restore-keys: composer-
# 4) Instalar dependencias PHP (EN GITHUB)
- name: Install PHP dependencies
run: composer install --no-interaction --no-progress --prefer-dist --no-dev --optimize-autoloader
# 5) Configurar Node 20 para Vite
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
# 6) Cache de node_modules
- name: Cache node modules
uses: actions/cache@v4
with:
path: node_modules
key: node-${{ hashFiles('package-lock.json') }}
restore-keys: node-
# 7) Instalar dependencias JS
- name: Install NPM dependencies
run: npm ci
# 8) Compilar assets con Vite
- name: Build assets
run: npm run build
# 9) Subir proyecto compilado a EC2 (sin borrar la carpeta => .env se conserva)
- name: Upload project to EC2 via SCP
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USER }}
key: ${{ secrets.EC2_SSH_KEY }}
port: ${{ secrets.EC2_PORT }}
source: ./
target: ${{ env.APP_DIR }}
overwrite: true # sobreescribe archivos que sí existan en el source
rm: false # NO borra la carpeta destino (conserva tu .env)
strip_components: 0
# 10) Comandos remotos en EC2 (migraciones, caches, permisos, restart Apache)
- name: Run remote Laravel commands on EC2
uses: appleboy/ssh-action@v1.1.0
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USER }}
key: ${{ secrets.EC2_SSH_KEY }}
port: ${{ secrets.EC2_PORT }}
script: |
cd $APP_DIR
# Migraciones en producción
php artisan migrate --force
# Limpiar y regenerar caches
php artisan config:clear
php artisan cache:clear
php artisan route:clear
php artisan view:clear
php artisan config:cache
php artisan route:cache
php artisan view:cache
# Permisos correctos para storage y cache
sudo chown -R ubuntu:www-data $APP_DIR
sudo chmod -R 775 storage bootstrap/cache
# Reiniciar Apache
sudo systemctl restart apache2
Este pipeline se ejecuta automáticamente en cada push a
main o de forma manual con
workflow_dispatch.
Se asegura de que el build de Laravel y Vite se realice
en GitHub, y solo se copian archivos compilados
hacia EC2. La opción rm: false garantiza que
la carpeta remota no se borre, manteniendo el .env y
otros archivos propios del servidor.
Problemas encontrados y cómo se resolvieron
1. Errores en logs y permisos de storage/
Al acceder a rutas de autenticación se recibió un error indicando que
storage/logs/laravel.log no podía abrirse.
- Causa: permisos insuficientes para el usuario del servidor web.
- Solución: ajustar propietarios y permisos:
sudo chown -R ubuntu:www-data /var/www/laravel-api-frutasysudo chmod -R 775 storage bootstrap/cache. - Estos comandos se integraron al script remoto del workflow.
2. Error de Vite: ViteManifestNotFoundException
Laravel mostraba un error al usar @vite() porque no existía
public/build/manifest.json en el servidor.
- Causa: el build de Vite no se había ejecutado en producción.
- Solución inicial: ejecutar
npm installynpm run buildmanualmente en EC2. - Solución definitiva: mover el build al pipeline de GitHub Actions
(
npm ci+npm run build), de forma que siempre se genere el manifest antes de desplegar.
3. Problemas de SSH y timeouts al copiar archivos
Los primeros intentos de despliegue fallaron con errores del tipo
ssh: no key found y timeouts en la conexión al puerto 22.
- Se generó un par de llaves específico para GitHub Actions:
ssh-keygen -t ed25519 -C "github-actions" -f github_actions. - La clave pública se añadió en
~/.ssh/authorized_keysdel usuarioubuntuen EC2. - La clave privada se guardó en el secreto
EC2_SSH_KEYdel repositorio. - Se revisaron los Security Groups para abrir el puerto 22 desde Internet (al menos mientras se terminaba la configuración del pipeline).
4. MissingAppKeyException (APP_KEY faltante)
Tras uno de los despliegues, Laravel lanzó
Illuminate\Encryption\MissingAppKeyException.
- Causa: el archivo
.envdel servidor perdió o no tenía unAPP_KEYválido. - Solución:
- Regenerar la clave con
php artisan key:generatedirectamente en EC2. - Asegurarse de que
.envno está en el repo y se mantiene solo en el servidor. - Configurar el step de
scpconrm: falsepara no borrar la carpeta completa del proyecto.
- Regenerar la clave con
Seguridad y buenas prácticas
- Uso de llave SSH exclusiva para el runner de GitHub Actions.
- Variables sensibles manejadas como GitHub Secrets:
EC2_HOST,EC2_USER,EC2_PORT,EC2_SSH_KEY. .envno versionado (listado en.gitignore) y mantenido sólo en EC2.- Permisos mínimos requeridos en
storage/ybootstrap/cache/. - Usuario de BD dedicado solo con permisos sobre el esquema de la API.
Resultados y aprendizajes
Al finalizar, se obtuvo una API Laravel funcional, accesible desde Internet, con un flujo de despliegue estable y automatizado. Algunos aprendizajes clave:
- Configuración completa de un stack Laravel + Apache + PHP 8.2 + MariaDB en EC2.
- Comprensión práctica de cómo integrar Composer + Vite dentro de un pipeline de GitHub Actions.
- Manejo de errores típicos de producción:
permisos, logs, Vite,
APP_KEYy SSH. - Separación limpia entre código, configuración sensible y artefactos de build.
- Base sólida para futuras APIs Laravel desplegadas con el mismo patrón api-laravel-aws-ec2.