ENFOQUE BB: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.

Tutorial Docker con Laravel (Enfoque: Contenedor Primero)

¿Qué logramos?

  • Tu máquina host solo necesita Docker y Docker Compose.

  • El proyecto Laravel se genera dentro del contenedor app.

  • Todo el código se sincroniza automáticamente con tu host gracias a los volúmenes.


1. Estructura de archivos (en tu host)

Crea una carpeta para tu proyecto (estará vacía al principio) y dentro de ella los siguientes archivos:

text
mi-proyecto-laravel/
├── docker/
│   ├── nginx/
│   │   └── default.conf
│   └── php/
│       └── Dockerfile
└── docker-compose.yml

2. Archivo Dockerfile (para PHP)

Ruta: docker/php/Dockerfile
(Aquí solo preparamos el entorno PHP, aún no copiamos ningún código).

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

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

# Instalar Composer (lo necesitaremos dentro para crear el proyecto)
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

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

# Exponer puerto de PHP-FPM
EXPOSE 9000

CMD ["php-fpm"]

3. Configuración de Nginx

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;

    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;  # Apunta al servicio 'app'
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

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

4. Docker Compose (el orquestador)

Ruta: docker-compose.yml (en la raíz de tu 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   # Montamos la carpeta VACÍA de nuestro host
    networks:
      - laravel
    depends_on:
      - db
      - redis

  webserver:
    image: nginx:alpine
    container_name: laravel_webserver
    restart: unless-stopped
    ports:
      - "8000:80"          # Puerto 8000 de tu navegador
    volumes:
      - ./:/var/www/html   # Mismo volumen montado para que Nginx vea el código
      - ./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:

5. ¡Levantamos los contenedores (aún sin código)!

Ejecuta en la terminal (estando en la raíz de mi-proyecto-laravel):

bash
docker-compose up -d --build

Verifica que los 4 contenedores estén corriendo:

bash
docker-compose ps

Deberías ver: app, webserver, db y redis con estado Up.


6. Entramos al contenedor y CREAMOS el proyecto Laravel

Aquí viene la magia de tu enfoque. Vamos a entrar dentro del contenedor app y usaremos Composer para generar el proyecto justo en la carpeta montada (/var/www/html).

bash
docker-compose exec app bash

Ahora estás dentro del contenedor. Ejecuta:

bash
composer create-project laravel/laravel .

Ojo con el punto (.) : Significa "instálalo en la carpeta actual". Como el volumen está montado, todos estos archivos aparecerán automáticamente en tu carpeta del host (mi-proyecto-laravel). ¡Abré tu explorador de archivos y verás cómo se van creando!


7. Configurar el archivo .env (dentro del contenedor)

Sigue dentro del bash. Copia el entorno de ejemplo y edítalo:

bash
cp .env.example .env
nano .env   # o usa vi si prefieres

Asegúrate de que las variables de la base de datos apunten a los servicios de Docker:

env
DB_CONNECTION=mysql
DB_HOST=db              # IMPORTANTE: es el nombre del servicio en docker-compose
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laraveluser
DB_PASSWORD=secret

REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379

Guarda el archivo (Ctrl+O y Ctrl+X en nano).


8. ¡EL PASO CRUCIAL! (Arreglar permisos en tu host)

Esto es lo más importante. Como creaste los archivos dentro del contenedor (como usuario www-data o root), en tu máquina host esos archivos te pertenecerán a root y no podrás editarlos con tu editor de código (en Linux) o no tendrás permisos para borrar la carpeta vendor.

Sal del bash del contenedor (escribe exit y presiona Enter) para volver a tu terminal del host. Luego ejecuta este comando:

bash
docker-compose exec app chown -R $(id -u):$(id -g) .

¿Qué hace esto?

  • $(id -u) obtiene tu número de usuario en el host.

  • $(id -g) obtiene tu número de grupo.

  • Se lo pasa al comando chown dentro del contenedor para que cambie la propiedad de TODOS los archivos recién creados a tu usuario del host.

(Si usas Windows o Mac, este problema de permisos suele ser menor, pero ejecutarlo no hace daño).


9. Ejecutar migraciones (ya desde el host, opcional)

Ahora que el proyecto está creado y los permisos arreglados, puedes ejecutar Artisan. Puedes hacerlo desde el host (recomendado) para no tener que entrar al bash cada vez:

bash
docker-compose exec app php artisan migrate

Te preguntará si quieres crear la base de datos, escribe yes.


10. ¡Abre tu navegador!

Ve a:

text
http://localhost:8000

Deberías ver la pantalla de bienvenida de Laravel. ¡Felicidades! Has creado un proyecto Laravel dentro del contenedor.


Comandos de uso diario (desde el host)

Como el código ya está en tu máquina, puedes editarlo con VSCode, PHPStorm, etc. Para ejecutar comandos:

bash
# Ejecutar Artisan
docker-compose exec app php artisan make:controller MiControlador

# Ejecutar Composer para instalar paquetes
docker-compose exec app composer require spatie/laravel-permission

# Entrar rápidamente al bash del contenedor
docker-compose exec app bash

¿Qué pasa si borro la carpeta del host?

No pasa nada. Como el proyecto se creó dentro del contenedor pero con volumen, los archivos existen en ambos lados. Si borras todo en el host, los archivos dentro del contenedor /var/www/html también se borrarían (por el volumen).

Para recuperarlo, solo tendrías que volver a ejecutar el paso 6 (composer create-project dentro del contenedor). Así de fácil.


Diferencias clave con el Enfoque A (el del tutorial anterior)

CaracterísticaEnfoque B (TU enfoque)
PHP/Composer en host❌ No necesitas instalar nada
Creación del proyectoSe hace dentro del contenedor con composer create-project
Permisos (Linux)Requiere el paso 8 (chown) para poder editar archivos en el host
Velocidad inicialUn poco más lento la primera vez (descarga de dependencias dentro del contenedor)
PortabilidadMáxima: solo necesitas Docker para arrancar desde cero

Solución a problemas comunes con este enfoque

  • Error "Permission denied" al editar archivos en el host: Ejecuta el paso 8 (el chown). Siempre que instales nuevas dependencias con composer require dentro del contenedor, los nuevos archivos en vendor/ pueden volver a cambiar de dueño; repite el chown si te pasa.

  • Composer es lento dentro del contenedor: Puedes pasarle la flag --prefer-dist para que baje las versiones empaquetadas.

  • Quiero usar Xdebug: Se añade en el Dockerfile con pecl install xdebug, pero eso ya es para un tutorial más avanzado.

¿Qué te parece? Este enfoque es el que usa mucha gente en equipos donde cada uno tiene un sistema operativo diferente, porque evita el "en mi máquina funciona" al 100%. ¡Dime si te queda alguna duda!

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