docker-laravel-enfoque AA bueno

 

  • La sintaxis en docker-compose.yml es:
    volumes: - ./ruta/local:/ruta/en/contenedor

El . significa "busca el Dockerfile en la carpeta actual".

¿Qué hace - ./:/var/www/html?

  • ./ es la carpeta actual de tu host (donde está el docker-compose.yml).

  • /var/www/html es la ruta dentro del contenedor.

  • Efecto: Todo lo que esté en ./ se ve dentro del contenedor en /var/www/html, y todo lo que se cree dentro del contenedor en /var/www/html aparecerá automáticamente en tu ./. Es una sincronización bidireccional y en tiempo real.


 El "bind mount" (volumes: - ./:/var/www/html) es el corazón de la sincronización entre tu PC y el contenedor.

Usar Docker con Laravel

En este tutorial aprenderás a configurar un entorno de desarrollo para Laravel usando Docker y Docker Compose. Con Docker podrás ejecutar tu aplicación en contenedores aislados, lo que facilita la reproducibilidad, evita conflictos de versiones y simplifica el trabajo en equipo.


1. ¿Por qué usar Docker con Laravel?

  • Entorno consistente: Todos los desarrolladores usan la misma configuración de PHP, base de datos, servidor web, etc.

  • Aislamiento: Cada servicio (PHP, Nginx, MySQL, Redis) corre en su propio contenedor.

  • Portabilidad: Puedes mover tu aplicación a cualquier servidor que tenga Docker instalado.

  • Fácil escalado: Con Docker Compose puedes levantar servicios adicionales fácilmente.


2. Requisitos previos

Antes de empezar, asegúrate de tener instalado:

  • Docker (descargar)

  • Docker Compose (viene incluido con Docker Desktop, o se instala por separado en Linux)

Verifica que todo funciona:

bash
docker --version
docker-compose --version

3. Crear un proyecto Laravel (opcional)

Si ya tienes un proyecto Laravel, puedes saltar este paso. Si no, crea uno nuevo:

bash
composer create-project laravel/laravel mi-proyecto
cd mi-proyecto

Nota: Necesitas tener Composer instalado globalmente. Si no, puedes usar la imagen de Composer con Docker, pero para simplificar asumimos que lo tienes.


4. Estructura de archivos que vamos a crear

Dentro de la raíz de tu proyecto Laravel, crea la siguiente estructura:

text
mi-proyecto/
├── docker/
│   ├── nginx/
│   │   └── default.conf
│   └── php/
│       └── Dockerfile
├── docker-compose.yml
└── .env (el de Laravel)

5. Configurar el servicio PHP (Dockerfile)

Creamos un Dockerfile para el servicio PHP-FPM. Este archivo define la imagen que usaremos para ejecutar PHP y Laravel.

Ruta: docker/php/Dockerfile

dockerfile
FROM php:8.2-fpm

# Instalar dependencias del sistema
RUN apt-get update && apt-get install -y \
    git \
    curl \
    libpng-dev \
    libonig-dev \
    libxml2-dev \
    zip \
    unzip

# Limpiar caché de apt
RUN apt-get clean && rm -rf /var/lib/apt/lists/*

# Instalar extensiones de PHP necesarias para Laravel
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd

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

# Establecer directorio de trabajo
WORKDIR /var/www/html

# Copiar los archivos de la aplicación (esto se hará con volúmenes en desarrollo)
# pero copiamos el código fuente para producción (opcional)
# COPY . /var/www/html

# Ajustar permisos (en desarrollo, usaremos el usuario de la máquina anfitriona)
RUN chown -R www-data:www-data /var/www/html

# Exponer el puerto 9000 para PHP-FPM
EXPOSE 9000

CMD ["php-fpm"]

6. Configurar Nginx

Creamos un archivo de configuración para Nginx que apunte a Laravel.

Ruta: docker/nginx/default.conf

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

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";

    index index.php index.html;

    charset utf-8;

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

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass app:9000;  # nombre del servicio en docker-compose
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

7. Docker Compose

El archivo docker-compose.yml define todos los servicios que necesitamos: la aplicación (PHP-FPM), el servidor web (Nginx), la base de datos (MySQL) y opcionalmente Redis.

Ruta: docker-compose.yml (en la raíz del proyecto)

yaml
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: docker/php/Dockerfile
    container_name: laravel_app
    restart: unless-stopped
    working_dir: /var/www/html
    volumes:
      - ./:/var/www/html
    networks:
      - laravel
    depends_on:
      - db
      - redis
    environment:
      - PHP_IDE_CONFIG=serverName=docker
    extra_hosts:
      - "host.docker.internal:host-gateway"  # para acceso a la máquina host

  webserver:
    image: nginx:alpine
    container_name: laravel_webserver
    restart: unless-stopped
    ports:
      - "8000:80"
    volumes:
      - ./:/var/www/html
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
    networks:
      - laravel

  db:
    image: mysql:8.0
    container_name: laravel_db
    restart: unless-stopped
    ports:
      - "3306:3306"
    environment:
      MYSQL_DATABASE: laravel
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_USER: laraveluser
      MYSQL_PASSWORD: secret
    volumes:
      - db_data:/var/lib/mysql
    networks:
      - laravel

  redis:
    image: redis:alpine
    container_name: laravel_redis
    restart: unless-stopped
    ports:
      - "6379:6379"
    networks:
      - laravel

networks:
  laravel:
    driver: bridge

volumes:
  db_data:

8. Variables de entorno de Laravel

Ajusta tu archivo .env de Laravel para que se conecte a los servicios definidos en Docker. Edita las variables de conexión a la base de datos:

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

REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379

Nota: DB_HOST=db porque el servicio se llama db en el docker-compose.


9. Construir y ejecutar los contenedores

Desde la raíz del proyecto, ejecuta:

bash
docker-compose up -d --build

Esto construirá las imágenes y levantará los contenedores en segundo plano (-d).

Para ver los logs:

bash
docker-compose logs -f

Si todo funciona, deberías poder acceder a tu aplicación en:

text
http://localhost:8000

Deberías ver la página de bienvenida de Laravel.


10. Comandos útiles dentro del contenedor

Para ejecutar comandos de Artisan o Composer dentro del contenedor app, usa:

bash
# Ejecutar comandos de Artisan
docker-compose exec app php artisan migrate

# Ejecutar Composer
docker-compose exec app composer install

# Abrir una shell dentro del contenedor
docker-compose exec app bash

Si prefieres ejecutar comandos sin tener que escribir docker-compose exec app cada vez, puedes crear un alias:

bash
alias sail='docker-compose exec app'

Luego podrías usar sail php artisan ..., pero el alias no es persistente. En proyectos Laravel, existe Laravel Sail, que es una herramienta oficial que usa Docker y proporciona un script ./vendor/bin/sail. Si usas Sail, la configuración es similar, pero este tutorial es genérico.


11. Migrar y poblar la base de datos

Con los contenedores corriendo, ejecuta:

bash
docker-compose exec app php artisan migrate
docker-compose exec app php artisan db:seed

12. Acceso a la base de datos desde el host

Puedes conectarte a MySQL usando cualquier cliente (por ejemplo, DBeaver, TablePlus) con los siguientes datos:

  • Host: localhost

  • Puerto: 3306

  • Usuario: laraveluser

  • Contraseña: secret

  • Base de datos: laravel

También puedes ingresar al contenedor de la base de datos:

bash
docker-compose exec db mysql -u laraveluser -p
# Introduce la contraseña: secret

13. Detener y eliminar los contenedores

Para detener los contenedores sin eliminarlos:

bash
docker-compose stop

Para detener y eliminar contenedores, redes y volúmenes (los datos de la BD se conservan porque usamos un volumen):

bash
docker-compose down

Si quieres eliminar también los volúmenes (y perder los datos de la BD):

bash
docker-compose down -v

14. Tips y buenas prácticas

  • Volúmenes para desarrollo: Hemos montado el código fuente en /var/www/html con un volumen, así que los cambios en tu máquina se reflejan instantáneamente en el contenedor.

  • Permisos: En Linux puede haber problemas de permisos con los archivos generados por el contenedor (por ejemplo, storage o bootstrap/cache). Puedes solucionarlo ejecutando dentro del contenedor:

    bash
    docker-compose exec app chown -R www-data:www-data storage bootstrap/cache
  • Optimizar para producción: En producción, no uses volúmenes para el código; en su lugar, copia el código dentro de la imagen en el Dockerfile y usa una imagen optimizada.

  • Usa variables de entorno: Define todas las configuraciones sensibles en el archivo .env y no las subas al repositorio.

  • Añadir otros servicios: Puedes agregar fácilmente servicios como Elasticsearch, Meilisearch, Mailhog, etc., definiéndolos en el docker-compose.


15. Conclusión

Ahora tienes un entorno Docker completo para desarrollar con Laravel. Puedes compartir este repositorio con tu equipo y todos tendrán la misma configuración. Recuerda que puedes ajustar las versiones de PHP, MySQL, Nginx, etc., según tus necesidades.

Si encuentras algún error, revisa los logs con docker-compose logs -f y asegúrate de que todos los servicios estén levantados correctamente.

Enfoque A (El que puse en el tutorial): Proyecto en el Host

  • Cómo es: Instalas Composer en tu máquina, creas el proyecto Laravel en tu disco duro y luego montas esa carpeta dentro del contenedor con un volumes:.

  • Ventajas:

    • Rendimiento: En Windows/Mac, los volúmenes montados son más rápidos para el IDE (PHPStorm, VSCode) porque los archivos están en el sistema de archivos nativo.

    • Permisos: En Linux, evitas que los archivos generados dentro del contenedor (como vendor/ o bootstrap/cache) pertenezcan al usuario root o www-data, lo que te obligaría a usar sudo para borrarlos.

    • Git: Puedes hacer git status directamente sin entrar al contenedor.

  • Desventaja: Necesitas tener PHP y Composer instalados en tu máquina host.


Enfoque B (El que propones): Contenedor primero, proyecto dentro

  • Cómo es: Solo tienes Docker instalado. Creas los contenedores vacíos y, desde dentro del contenedor app, ejecutas composer create-project para generar Laravel.

  • Ventajas:

    • Cero dependencias en el host: No necesitas instalar PHP, Composer ni nada más que Docker.

    • Versiones exactas: La versión de PHP la dicta el contenedor, no tu sistema operativo.

  • Desventajas:

    • Permisos (el gran problema): En Linux, si ejecutas composer dentro del contenedor, los archivos se crearán con el usuario www-data (o root). En tu host, esos archivos te pertenecerán a ti, pero como root, y no podrás editarlos con tu usuario normal sin sudo.

    • Rendimiento: Crear el proyecto dentro de un volumen montado puede ser un poco más lento en algunos sistemas.


¿Cómo se hace exactamente el Enfoque B (el tuyo)?

Te enseño los pasos para que lo hagas como prefieres. Asumiendo que tienes la misma estructura de docker-compose.yml que te puse antes:

1. Levanta solo los contenedores (sin código aún)

Asegúrate de que la carpeta ./mi-proyecto esté vacía (o ni siquiera la crees). Ejecuta:

bash
docker-compose up -d --build

Esto levantará los contenedores app, webserver, db, etc. La carpeta de tu host estará vacía, pero dentro del contenedor /var/www/html también estará vacía.

2. Entra al contenedor app

bash
docker-compose exec app bash

3. Dentro del contenedor, crea el proyecto Laravel

Una vez dentro del bash del contenedor, ejecuta:

bash
composer create-project laravel/laravel .

Importante: El punto (.) al final es clave, porque estás diciendo "instálalo en la carpeta actual" (/var/www/html).

4. Ajusta el .env

Dentro del contenedor, edita el .env o cópialo desde .env.example:

bash
cp .env.example .env
nano .env  # O usa vi

Asegúrate de poner las credenciales de la BD como te mostré antes (DB_HOST=db).

5. Sal del contenedor y da permisos (solo si usas Linux)

Vuelve a tu terminal del host y ejecuta:

bash
# Esto asegura que los archivos creados dentro del contenedor
# sean propiedad de TU usuario en el host, y no de 'root'
docker-compose exec app chown -R $(id -u):$(id -g) .

(En Windows/Mac esto suele ser menos problemático porque Docker maneja la traducción de usuarios internamente).

6. Ejecuta las migraciones

bash
docker-compose exec app php artisan migrate

El método "oficial" de Laravel: Laravel Sail

Curiosamente, Laravel Sail (la herramienta oficial de Laravel para Docker) combina ambos métodos:

  1. Primero te pide que crees el proyecto con Composer en el host (Enfoque A).

  2. Pero si no tienes Composer, te dan un comando mágico con Docker para crearlo dentro (Enfoque B):

bash
# Así se crea un proyecto Laravel usando Docker sin tener Composer instalado:
docker run --rm \
    -v $(pwd):/opt \
    -w /opt \
    laravelsail/php83-composer:latest \
    bash -c "composer create-project laravel/laravel mi-proyecto"

¿Cuál te recomiendo?

  • Si usas Windows o Mac: Cualquiera de los dos funciona bien. El del tutorial (Enfoque A) es más rápido para empezar.

  • Si usas Linux: Te recomiendo el Enfoque A (crear el proyecto en el host) para evitar dolores de cabeza con los permisos de vendor/ y storage/.

Dicho esto, tu intuición es totalmente acertada. Hacerlo dentro del contenedor es más "puro" en términos de contenerización. Si te encuentras con problemas de permisos en Linux, ya sabes el comando chown para arreglarlo.

¿Qué sistema operativo usas? Si me dices eso, puedo darte la receta exacta para que no tengas ni un solo problema de permisos

Comentarios

Entradas más populares de este blog

12. Hola Mundo en Docker.

11¿Qué es Docker? y ¿Por qué debo saberlo?

14. Publish and Detached modes