2.1-Instalación de Docker (nuevo método oficial), configuración de un proyecto Laravel con Nginx y MySQL en contenedores

 

Solución recomendada

En tu terminal WSL, ejecuta:

sudo chown -R $USER:$USER ~/laravel

Te pedirá tu contraseña de Linux.

Después verifica:

ls -l ~/laravel/docker-compose/nginx/app.laravel.conf

Instalación de Docker (nuevo método oficial), configuración de un proyecto Laravel con Nginx y MySQL en contenedores

Este tutorial te guiará paso a paso desde la instalación de Docker y Docker Compose (como plugin) en Linux (Debian/Ubuntu) hasta la configuración completa de un entorno de desarrollo para Laravel con tres servicios: PHP-FPM, Nginx y MySQL. Además, entenderás el funcionamiento de los volúmenes (bind mounts) que sincronizan automáticamente los archivos entre tu máquina host y los contenedores.

Novedad: Hoy en día Docker Compose se instala como un plugin y se utiliza con el comando docker compose (con espacio, sin guion). El antiguo docker-compose (con guion) está en desuso. A lo largo de este tutorial usaremos la sintaxis moderna.


Tabla de contenido

  1. ¿Qué son los volúmenes en Docker y por qué son mágicos?

  2. Instalación de Docker y Docker Compose (método oficial)

  3. Estructura de archivos inicial

  4. Creación del Dockerfile personalizado

  5. Creación del archivo docker-compose.yml (servicio app)

  6. Construir y levantar el contenedor de la aplicación

  7. Creación del proyecto Laravel dentro del contenedor

  8. Verificar la sincronización en tiempo real

  9. Configurar Nginx y MySQL

    • Paso 1: Detener contenedores y crear estructura para Nginx

    • Paso 2: Crear archivo de configuración de Nginx

    • Paso 3: Modificar docker-compose.yml (agregar Nginx y MySQL)

    • Paso 4: Ajustar el archivo .env de Laravel

    • Paso 5: Levantar los contenedores con la nueva configuración

    • Paso 6: Verificar que la aplicación funciona

  10. Solución de problemas comunes

  11. Conclusión

  12. Comandos útiles de Docker Compose (nueva sintaxis)


¿Qué son los volúmenes en Docker y por qué son mágicos?

Cuando trabajamos con contenedores, todo lo que sucede dentro de ellos está aislado del sistema anfitrión. Sin embargo, para desarrollar aplicaciones necesitamos que los cambios en el código se reflejen instantáneamente y que los archivos generados (como los de Composer) estén disponibles en nuestro editor local.

Aquí es donde entran los bind mounts:

  • Un bind mount monta un directorio del host dentro del contenedor.

  • La sintaxis en docker-compose.yml es:

yaml
volumes:
  - ./ruta/local:/ruta/en/contenedor

Cualquier archivo creado, modificado o eliminado en el contenedor dentro de esa ruta, aparece automáticamente en tu host y viceversa.

Esto permite editar el código con tu IDE favorito mientras el contenedor ejecuta la aplicación, sin necesidad de reconstruir la imagen.

En este tutorial usaremos el siguiente montaje:

yaml
volumes:
  - ./:/var/www

Es decir, el directorio actual (donde tienes tu proyecto) se monta en /var/www dentro del contenedor. Todos los archivos de Laravel vivirán ahí.


Instalación de Docker y Docker Compose (método oficial)

Sigue estos pasos para instalar Docker y Docker Compose (como plugin) usando el repositorio oficial. Este es el método recomendado actualmente.

1. Actualizar paquetes e instalar dependencias

bash
sudo apt update
sudo apt install ca-certificates curl -y

2. Agregar la clave GPG oficial de Docker

bash
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

3. Agregar el repositorio oficial de Docker a las fuentes de APT

bash
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

4. Actualizar el índice de paquetes e instalar Docker

bash
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
  • docker-ce: El motor de Docker (Community Edition).

  • docker-compose-plugin: El plugin de Docker Compose (ya no necesitas instalar docker-compose por separado).

5. Verificar la instalación

Para confirmar que todo ha ido bien, ejecuta el contenedor de prueba:

bash
sudo docker run hello-world

Deberías ver el mensaje de bienvenida de Docker.

6. Agregar tu usuario al grupo docker (para evitar usar sudo)

bash
sudo groupadd docker          # si no existe
sudo usermod -aG docker $USER

Para aplicar los cambios sin cerrar sesión:

bash
newgrp docker

Prueba que todo funciona sin sudo:

bash
docker run hello-world

7. Verificar la versión de Docker Compose

Con el nuevo plugin, el comando es docker compose (con espacio). Verifica que está instalado:

bash
docker compose version

Deberías ver algo como Docker Compose version v2.xx.x. Si ves esto, estás listo para usar la sintaxis moderna.


Estructura de archivos inicial

Antes de empezar, asegúrate de tener una carpeta vacía para tu proyecto. Nosotros la llamaremos laravel-docker (puedes elegir cualquier nombre).

Crear la carpeta:

bash
mkdir laravel-docker
cd laravel-docker

Árbol inicial en el host:

text
~/laravel-docker/
├── Dockerfile          (lo crearemos)
└── docker-compose.yml  (lo crearemos)

Creación del Dockerfile personalizado

Vamos a crear un Dockerfile que defina la imagen de nuestro contenedor principal (PHP-FPM). Este contenedor ejecutará el código de Laravel.

Crear el archivo Dockerfile

Dentro de ~/laravel-docker/, ejecuta:

bash
nano Dockerfile

Pega el siguiente contenido (explicado línea por línea):

dockerfile
# 1. Imagen base: PHP 8.3 con FPM (recomendado para Laravel 10/11)
FROM php:8.3-fpm

# 2. Argumentos para el usuario (se pasan al construir la imagen)
ARG USER=laravel
ARG UID=1000

# 3. Instalar dependencias del sistema y extensiones de PHP
RUN apt-get update && apt-get install -y \
    git \
    curl \
    libpng-dev \
    libonig-dev \
    libxml2-dev \
    zip \
    unzip \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

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

# 5. Instalar Composer (globalmente)
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

# 6. Crear un usuario no root (para seguridad)
RUN useradd -G www-data,root -u ${UID} -d /home/${USER} ${USER} \
    && mkdir -p /home/${USER}/.composer \
    && chown -R ${USER}:${USER} /home/${USER}

# 7. Establecer el directorio de trabajo
WORKDIR /var/www

# 8. Cambiar al usuario creado (a partir de aquí se ejecuta con ese usuario)
USER ${USER}

# 9. Comando por defecto (opcional, se puede sobrescribir en docker-compose)
CMD ["php-fpm"]

Explicación del Dockerfile:

  • FROM php:8.2-fpm: imagen base ligera con PHP 8.2 y FPM.

  • ARG USER y ARG UID: permiten pasar el nombre y UID del usuario del host desde docker-compose, para que los archivos creados tengan los mismos propietarios.

  • Instalamos paquetes esenciales y extensiones de PHP (pdo_mysql, mbstring, gd, etc.) necesarias para Laravel.

  • Instalamos Composer globalmente.

  • Creamos un usuario no root con el UID proporcionado, lo agregamos a los grupos www-data y root.

  • El directorio de trabajo es /var/www.

  • Cambiamos al usuario creado para que los comandos se ejecuten con sus permisos.


Creación del archivo docker-compose.yml (servicio app)

Este archivo orquesta nuestros servicios. Definiremos el servicio app (nuestro contenedor PHP-FPM). Más adelante añadiremos MySQL y Nginx.

Crear docker-compose.yml

bash
nano docker-compose.yml

Pega el siguiente contenido:

yaml
version: '3'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        user: ${USER:-udemy}   # Usa el usuario del sistema o 'udemy' por defecto
        uid: ${UID:-1000}      # Usa el UID del sistema o 1000 por defecto
    image: laravel-app
    container_name: laravel-app-1
    restart: unless-stopped
    working_dir: /var/www
    volumes:
      - ./:/var/www            # <-- El bind mount mágico
    networks:
      - laravel

networks:
  laravel:
    driver: bridge

Puntos clave:

  • build: construye la imagen a partir del Dockerfile del directorio actual, pasando los argumentos user y uid. Para obtener el UID actual de tu usuario, ejecuta id -u y asígnalo a una variable de entorno o simplemente usa 1000.

  • volumes: - ./:/var/www: bind mount que monta el directorio local dentro del contenedor en /var/www. Este es el corazón de la sincronización.

  • networks: crea una red interna laravel para que los servicios se comuniquen entre sí.


Construir y levantar el contenedor de la aplicación

Ejecutar docker compose up -d

Nota: Usamos el nuevo comando docker compose (sin guion).

bash
docker compose up -d

Si tienes instalada la versión independiente de Docker Compose (v1), el comando correcto es:

docker-compose
up -d

Este comando:

  • Construye la imagen según el Dockerfile (descarga la base php:8.2-fpm y ejecuta los pasos).

  • Crea el contenedor laravel-app-1 y lo inicia en segundo plano (-d).

  • Monta el volumen ./ en /var/www.

Posible problema: La descarga de la imagen base a veces se queda "congelada". Si eso ocurre, presiona Ctrl+C y vuelve a ejecutar el comando. Como ya se habrán descargado algunas capas, el proceso continuará más rápido.

Verificar que el contenedor está corriendo

bash
docker ps

Deberías ver algo como:

text
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS   NAMES
xxxxxxxxxxxx   laravel-app    "docker-php-entrypoi…"   ... seconds     Up ...                 laravel-app-1

Comandos útiles:

  • Detener el contenedor: docker stop <nombre_o_id>

  • Borrar el contenedor: docker rm <nombre_o_id>

  • Forzar el borrado de uno activo: docker rm -f <nombre_o_id>

  • Vercontenedores docker ps

  • construir contenedor:docker compose up -d

  • Ingresar al contenedor:docker exec -it laravel-app-1 /bin/bash

  • Salir del contenedor:exit


Creación del proyecto Laravel dentro del contenedor

Ahora usaremos Composer (instalado en el contenedor) para crear un nuevo proyecto Laravel dentro de /var/www. Gracias al bind mount, los archivos que genere Composer aparecerán instantáneamente en tu host.

Acceder al contenedor

bash
docker exec -it laravel-app-1 /bin/bash

Ahora estás dentro del contenedor como el usuario que definiste. El prompt debería mostrar algo como udemy@<container_id>:/var/www$.

Crear el proyecto Laravel

Dentro del contenedor, ejecuta:

bash
composer create-project laravel/laravel application --prefer-dist

Composer descargará Laravel y creará una carpeta application dentro de /var/www con todos los archivos del framework.

¡Milagro de los volúmenes! En el mismo instante, abre tu explorador de archivos en el host (~/laravel-docker/) y verás que aparece una carpeta application con toda la estructura de Laravel. Los archivos se han sincronizado automáticamente desde el contenedor al host.

Estructura en el host (después de composer create-project):

text
~/laravel-docker/
├── application/
│   ├── app/
│   ├── bootstrap/
│   ├── config/
│   ├── database/
│   ├── public/
│   ├── resources/
│   ├── routes/
│   ├── storage/
│   ├── tests/
│   ├── vendor/
│   ├── .env
│   ├── composer.json
│   └── ... (otros archivos)
├── Dockerfile
└── docker-compose.yml

Estructura en el contenedor: exactamente la misma, porque /var/www está montado sobre el directorio del host. Ambos comparten el mismo árbol de archivos.

Copiar los archivos al directorio raíz y eliminar la subcarpeta application

Para que el código quede directamente en /var/www y no en una subcarpeta, podemos mover todos los archivos de application/ a la raíz del proyecto.

Desde el host (fuera del contenedor):

bash
cp -RT application/ .
rm -rf application

O desde dentro del contenedor:

bash
cp -RT application/ . && rm -rf application

Verifica que la estructura ahora sea:

text
~/laravel-docker/
├── app/
├── bootstrap/
├── config/
├── database/
├── public/
├── resources/
├── routes/
├── storage/
├── tests/
├── vendor/
├── .env
├── composer.json
├── Dockerfile
└── docker-compose.yml

Generar la clave de la aplicación (APP_KEY)

Dentro del contenedor (o desde el host, pero es mejor dentro para asegurar permisos), ejecuta:

bash
php artisan key:generate

Esto modificará el archivo .env que está sincronizado, así que verás el cambio en el host.


Lo más recomendable es ejecutar php artisan key:generate dentro del contenedor, no en el host.


Verificar la sincronización en tiempo real

Para confirmar que el bind mount funciona, realiza una prueba:

  1. Desde el host, crea un archivo test.txt dentro de ~/laravel-docker/.

  2. Dentro del contenedor, ejecuta ls /var/www y verás test.txt.

  3. Desde el contenedor, crea otro archivo touch /var/www/from-container.txt.

  4. En el host, verás que aparece from-container.txt automáticamente.

Esto demuestra que el directorio es compartido en ambos sentidos.


Configurar Nginx y MySQL.

Comandos útiles:

  • Detener el contenedor: docker stop <nombre_o_id>

  • Borrar el contenedor: docker rm <nombre_o_id>

  • Forzar el borrado de uno activo: docker rm -f <nombre_o_id>

  • Vercontenedores docker ps

  • construir contenedor:docker compose up -d

  • Ingresar al contenedor:docker exec -it laravel-app-1 /bin/bash

  • Salir del contenedor:exit

Ahora vamos a agregar los servicios de Nginx (servidor web) y MySQL (base de datos) para tener un entorno completo.

  • Salir del contenedor:exit

Paso 1: Detener contenedores y crear estructura para Nginx

Primero, asegurémonos de que ningún contenedor esté corriendo:

bash
docker compose down

docker stop laravel_app_1

docker compose-down

docker-compose down

Detener y eliminar los contenedores

bash

# Detener todos los contenedores

docker stop laravel-app-1 laravel-docker-nginx-1 laravel-docker-db-1


# Eliminar los contenedores

docker rm laravel-app-1 laravel-docker-nginx-1 laravel-docker-db-1


docker rm 533dd6c21efb_laravel_app_1 laravel_nginx_1   laravel_db_1


# Opcional: Eliminar también los volúmenes (base de datos)

docker volume ls


docker volume rm laravel-docker_db_data

Luego, crea la estructura de carpetas para la configuración de Nginx:

bash
mkdir -p docker-compose/nginx

Paso 2: Crear archivo de configuración de Nginx

Dentro de docker-compose/nginx, crea un archivo llamado app.laravel.conf:

bash
nano docker-compose/nginx/app.laravel.conf

crea:
default.conf

Pega el siguiente contenido (es un boilerplate oficial de Laravel para Nginx):

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

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

    index index.php;

    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;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

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

Partes clave que debes revisar:

  • root /var/www/public; → ruta donde Nginx buscará el index.php.

  • listen 80; → puerto en el que escuchará dentro del contenedor.

  • fastcgi_pass app:9000; → el nombre del contenedor de PHP-FPM (en nuestro caso app) y el puerto 9000 (el que expone PHP-FPM).

Guarda el archivo (Ctrl+O, Enter) y sal (Ctrl+X).

Paso 3: Modificar docker-compose.yml (agregar Nginx y MySQL)

Ahora editaremos docker-compose.yml para agregar los dos nuevos servicios. Abre el archivo:

bash
nano docker-compose.yml

Agregar el servicio Nginx:

Debajo del servicio app, añade el siguiente bloque (respeta la indentación con dos espacios):

yaml
  nginx:
    image: nginx:alpine
    restart: unless-stopped
    ports:
      - "8000:80"
    volumes:
      - ./:/var/www
      - ./docker-compose/nginx:/etc/nginx/conf.d
    networks:
      - laravel

Agregar el servicio MySQL (db):

Añade otro bloque para la base de datos:

yaml
  db:
    image: mysql:8
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: ${DB_DATABASE}
      MYSQL_USER: ${DB_USERNAME}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
    networks:
      - laravel

El archivo completo debería verse así:

yaml
version: '3'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        user: ${USER:-udemy}
        uid: ${UID:-1000}
    image: laravel-app
    container_name: laravel-app-1
    restart: unless-stopped
    working_dir: /var/www
    volumes:
      - ./:/var/www
    networks:
      - laravel

  nginx:
    image: nginx:alpine
    restart: unless-stopped
    ports:
      - "8000:80"
    volumes:
      - ./:/var/www
      - ./docker-compose/nginx:/etc/nginx/conf.d
    networks:
      - laravel

  db:
    image: mysql:8
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: ${DB_DATABASE}
      MYSQL_USER: ${DB_USERNAME}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
    networks:
      - laravel

networks:
  laravel:
    driver: bridge

Guarda los cambios (Ctrl+O, Enter) y sal (Ctrl+X).

Paso 4: Ajustar el archivo .env de Laravel

El archivo .env en la raíz de tu proyecto contiene las variables de conexión a la base de datos. Debemos editarlo para que los valores coincidan con los que usaremos en el contenedor db.

Abre el .env:

bash
nano .env

Busca o agrega las siguientes líneas (ajusta los valores como desees):

env
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=laravel_db
DB_USERNAME=laravel_user
DB_PASSWORD=secret

Puntos importantes:

  • DB_HOST debe ser db (el nombre del servicio en Docker Compose). No uses localhost ni 127.0.0.1, porque dentro del contenedor de la aplicación, localhost sería él mismo, no la base de datos.

  • DB_DATABASE, DB_USERNAME y DB_PASSWORD deben coincidir con lo que definiste en el environment del servicio db.

  • El puerto 3306 es el predeterminado de MySQL.

Guarda el archivo.

Paso 5: Levantar los contenedores con la nueva configuración

Ahora que todo está listo, levanta los servicios con Docker Compose (nuevo comando):

docker-compose down -v   # elimina contenedores y volúmenes viejos
bash
docker compose up -d

Este comando construirá la imagen de la aplicación (si no está construida), descargará las imágenes de Nginx y MySQL (si no las tienes), y creará los tres contenedores.

Verifica que los tres contenedores estén en ejecución:

bash
docker ps

Deberías ver app, nginx y db en la lista.

docker exec -it laravel-app-1 php artisan key:generate

Paso 6: Verificar que la aplicación funciona

Abre tu navegador y visita http://localhost:8000. Deberías ver la página de bienvenida de Laravel.

Si ves algún error, revisa los logs con:

bash
docker compose logs nginx
docker compose logs app
docker compose logs db

Solución de problemas comunes

La descarga de imágenes se queda pegada

Si al levantar los contenedores ves que la descarga de una imagen se detiene sin avanzar, presiona Ctrl + C para cancelar y vuelve a ejecutar docker compose up -d. Como muchas capas ya están descargadas, el proceso continuará más rápido.

Error de conexión a la base de datos

  • Verifica que DB_HOST en .env sea db y no localhost.

  • Comprueba que los nombres de usuario y contraseña coincidan con los definidos en environment del servicio db.

  • Asegúrate de que el servicio db esté corriendo: docker ps | grep db.

Error de permiso en los volúmenes

Si Nginx no puede leer los archivos, ajusta los permisos en el host o usa user: www-data en el servicio app.

El puerto 8000 ya está ocupado

Cambia el puerto en ports (por ejemplo, "8001:80") y accede por ese nuevo puerto.

La configuración de Nginx no se carga

Asegúrate de que el archivo de configuración termine en .conf y esté en la ruta correcta del volumen (./docker-compose/nginx). Dentro del contenedor, Nginx lee todos los archivos .conf en /etc/nginx/conf.d.

Composer no se encuentra dentro del contenedor

Verifica que en tu Dockerfile hayas instalado Composer correctamente (con COPY --from=composer:latest o con el instalador).

El contenedor no se crea correctamente

Revisa los logs con docker compose logs para identificar errores específicos.


Conclusión

Has configurado exitosamente un entorno completo de desarrollo para Laravel con Docker, que incluye:

  • PHP-FPM con todas las extensiones necesarias.

  • Composer para la gestión de dependencias.

  • Nginx como servidor web.

  • MySQL como base de datos.

  • Volúmenes (bind mounts) que sincronizan los archivos entre el host y los contenedores.

Ahora puedes desarrollar tu aplicación Laravel editando los archivos en tu máquina local y viendo los cambios reflejados automáticamente en los contenedores, todo con un entorno aislado y reproducible.

Comandos útiles de Docker Compose (nueva sintaxis)

ComandoDescripción
docker compose up -dLevanta los contenedores en segundo plano
docker compose downDetiene y elimina los contenedores
docker compose logsMuestra los logs de todos los servicios
docker compose logs nginxMuestra los logs de un servicio específico
docker compose exec app bashAccede a la terminal del contenedor app
docker compose restartReinicia todos los servicios
docker compose buildReconstruye las imágenes sin levantar los contenedores

Nota: El antiguo comando docker-compose (con guion) ya no es necesario; ahora usamos docker compose (con espacio) como plugin integrado.

  1. Reinicia los contenedores para asegurar que la red se establezca correctamente:

    bash
    docker compose down
    docker compose up -d

    docker-compose up -d
  2. Verifica que el .env tenga DB_HOST=db exactamente (sin espacios, sin comillas, sin localhost).

  3. Prueba la conectividad desde dentro del contenedor app:

    bash
    docker exec -it laravel-app-1 bash
    ping db

    Si el ping falla, entonces el nombre db no se está resolviendo. Si el ping funciona, el problema está en la configuración de PHP (quizás el puerto o las credenciales).

  4. Si el ping falla, asegúrate de que el servicio db esté definido con el nombre correcto en docker-compose.yml. Si lo llamaste de otra forma (ej. mysql), entonces cambia DB_HOST por ese nombre.


Nota adicional: si no quieres usar contraseña en MySQL

En tu docker-compose.yml, en el servicio db, puedes añadir:

yaml
environment:
  MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
  MYSQL_DATABASE: ${DB_DATABASE}
  # No definas MYSQL_USER ni MYSQL_PASSWORD

Luego, en .env:

env
DB_USERNAME=root
DB_PASSWORD=

Pero ojo: esto solo es recomendable en desarrollo local.


Opción 1: Usar el usuario root (la más sencilla, sin crear usuario extra)

Modifica tu .env para usar root con contraseña vacía (ya que tienes MYSQL_ALLOW_EMPTY_PASSWORD: yes).

.env (Opción 1)

env
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=laravel_db
DB_USERNAME=root
DB_PASSWORD=

docker-compose.yml (Opción 1)

yaml
db:
  image: mysql:8
  restart: unless-stopped
  environment:
    MYSQL_ALLOW_EMPTY_PASSWORD: "yes"   # Permite que root no tenga contraseña
    MYSQL_DATABASE: ${DB_DATABASE}
    # No definimos MYSQL_USER ni MYSQL_PASSWORD porque usamos root
  networks:
    - laravel

Ventaja: No necesitas crear un usuario adicional, solo usas root.
Desventaja: Es menos seguro (pero para desarrollo local está bien).


Opción 2: Crear un usuario dedicado laravel_user (más recomendable)

Descomenta las variables MYSQL_USER y MYSQL_PASSWORD en el compose, así MySQL creará ese usuario automáticamente. Luego en .env usas ese usuario y su contraseña.

.env (Opción 2)

env
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=laravel_db
DB_USERNAME=laravel_user
DB_PASSWORD=secret   # Pon la contraseña que quieras (coincide con MYSQL_PASSWORD)

docker-compose.yml (Opción 2)

yaml
db:
  image: mysql:8
  restart: unless-stopped
  environment:
    MYSQL_ALLOW_EMPTY_PASSWORD: yes   # Permite que root no tenga contraseña (opcional)
    MYSQL_DATABASE: ${DB_DATABASE}
    MYSQL_USER: ${DB_USERNAME}        # <--- Descomentado
    MYSQL_PASSWORD: ${DB_PASSWORD}    # <--- Descomentado
    # MYSQL_ROOT_PASSWORD no es necesaria si usas ALLOW_EMPTY_PASSWORD
  networks:
    - laravel

Ventaja: Mejor práctica, usas un usuario con privilegios limitados a esa base de datos.


Paso obligatorio para ambas opciones

Elimina la línea version: '3' al inicio de tu docker-compose.yml para evitar el warning (ya es obsoleta).

Luego, reinicia los contenedores desde cero para que los cambios en las variables de entorno se apliquen correctamente. Esto borrará los datos antiguos de MySQL (pero no importa, es desarrollo):

bash
docker compose down -v   # -v elimina el volumen de MySQL
docker compose up -d

docker-compose up -d

Verifica que el contenedor db arranque bien:

bash
docker compose logs db

Deberías ver ready for connections al final.

Finalmente, prueba la conexión desde el contenedor app:

bash
docker exec -it laravel-app-1 php -r "echo gethostbyname('db');"

Si devuelve una IP, y los logs de db no muestran errores, entonces visita http://localhost:8000

 La tabla sessions no existe

¡Excelente! El error de conexión ya está resuelto (usuario y contraseña funcionan). Ahora el problema es que Laravel está intentando leer la tabla sessions en la base de datos, pero esa tabla aún no existe porque no has ejecutado las migraciones.

Tienes dos opciones para solucionarlo:


Opción A: Ejecutar las migraciones para crear la tabla sessions (y todas las demás)

Esta es la solución más completa y recomendada si planeas usar la base de datos para sesiones, caché, etc.

1. Accede al contenedor app

bash
docker exec -it laravel-app-1 bash

2. Ejecuta las migraciones

Dentro del contenedor, ejecuta:

bash
php artisan migrate

Esto creará todas las tablas que necesita Laravel, incluyendo sessions, users, password_reset_tokens, failed_jobs, etc.

Cuando te pregunte sobre la base de datos, escribe yes.

Pasos para usar Tinker

1. Acceder a Tinker

Desde tu terminal dentro del contenedor (ya estás en /var/www como udemy), ejecuta:

bash
php artisan tinker

2. Ver el nombre de la base de datos

Dentro de Tinker, escribe:

php
>>> config('database.connections.mysql.database')

Esto te devolverá el nombre de la base de datos (según tu archivo .env debería ser laravel_db).

3. Listar todas las tablas

Para ver qué tablas existen en la base de datos, puedes usar:

php
>>> DB::select('SHOW TABLES');

O más elegante con el esquema de Laravel:

php
>>> use Illuminate\Support\Facades\Schema;
>>> Schema::getConnection()->getDoctrineSchemaManager()->listTableNames();

4. Verificar las migraciones ejecutadas

Laravel guarda un registro en la tabla migrations. Puedes consultarla:

php
>>> DB::table('migrations')->get();

Deberías ver las migraciones que acabas de ejecutar (create_users_table, create_cache_table, create_jobs_table).

5. Consultar datos de una tabla (ej. users)

php
>>> App\Models\User::all();

O si prefieres usar el DB facade:

php
>>> DB::table('users')->get();

6. Salir de Tinker

Escribe exit o presiona Ctrl + D.

3. Genera la clave de la aplicación (si no lo hiciste antes)

bash
php artisan key:generate

4. Sal del contenedor

bash
exit

5. Recarga la página

Ve a http://localhost:8000. Ahora deberías ver la página de bienvenida de Laravel sin errore

******************

Verificación final

Después de ejecutar las migraciones, puedes verificar que la tabla sessions existe:

bash
docker exec -it laravel-app-1 php artisan tinker

Dentro de Tinker, escribe:

php
DB::table('sessions')->get();

Si no da error, la tabla existe.


Resumen de comandos para ejecutar ahora

bash
# Entrar al contenedor
docker exec -it laravel-app-1 bash

# Ejecutar migraciones
php artisan migrate

# Generar clave
php artisan key:generate

# Salir
exit

# Recargar http://localhost:8000

¡Listo! Ahora tu aplicación Laravel debería funcionar sin errores. 🚀


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