Docker Compose com PHP e MySQL: ambiente de desenvolvimento completo

Docker Compose com PHP e MySQL: ambiente de desenvolvimento completo

Instalar PHP, MySQL, Apache e phpMyAdmin direto no computador funciona até o dia em que você precisa de duas versões diferentes do PHP, ou até um colega entrar no projeto e passar a tarde configurando tudo de novo. Com Docker Compose, PHP e MySQL sobem juntos com um único comando, com as mesmas versões para todo o time — e somem sem deixar rastro quando você não precisa mais deles.

Neste tutorial vamos montar um ambiente completo com PHP 8.4 + Apache, MySQL 8.4 e phpMyAdmin, explicando cada linha do compose.yaml. Ao final, você terá uma página PHP lendo dados do banco e o conhecimento para adaptar o mesmo modelo a Laravel, WordPress ou qualquer outro projeto PHP.

O que é o Docker Compose

O Docker Compose é uma ferramenta para definir e executar aplicações com vários containers. Em vez de digitar longos comandos docker run para cada serviço, você descreve tudo em um arquivo YAML — imagens, portas, volumes, variáveis e dependências — e controla o conjunto com comandos simples como docker compose up e docker compose down.

Se você ainda não conhece imagens, containers e volumes, comece pelo tutorial Docker para iniciantes. Este guia parte desses conceitos.

Nas versões atuais, o Compose vem integrado ao Docker como um plugin e é chamado com docker compose (com espaço). O antigo docker-compose (com hífen) foi descontinuado.

A arquitetura que vamos montar

Serviço Imagem Porta no seu computador Função
app Construída a partir de php:8.4-apache 8080 Executa o código PHP
db mysql:8.4 Não exposta Banco de dados
phpmyadmin phpmyadmin:5 8081 Interface web para o MySQL

Os três containers ficam em uma rede interna criada pelo Compose. Dentro dela, cada serviço é encontrado pelo nome: o PHP se conecta ao banco usando o host db, e não localhost. Esse é o detalhe que mais confunde quem está começando, então guarde-o.

Passo 1: estrutura de pastas

Crie a seguinte estrutura:

Estrutura de pastas do projeto Docker Compose PHP MySQL
Arquivos do projeto: compose.yaml, Dockerfile, .env, script SQL e o código PHP
  • compose.yaml descreve os serviços.
  • Dockerfile personaliza a imagem do PHP.
  • .env guarda senhas e nomes do banco.
  • docker/mysql/init.sql cria a tabela e os dados iniciais.
  • src/ contém o código da aplicação.

Passo 2: o arquivo compose.yaml

Este é o coração do tutorial. Crie o compose.yaml na raiz:

Arquivo compose.yaml com os serviços app, db e phpmyadmin
O compose.yaml define os três serviços, o volume e a dependência entre eles

Agora, serviço por serviço.

Serviço app (PHP + Apache)

  • build: . — em vez de usar uma imagem pronta, o Compose constrói uma a partir do Dockerfile da pasta atual.
  • ports: "8080:80" — publica a porta 80 do Apache na porta 8080 do seu computador.
  • volumes: ./src:/var/www/html — um bind mount: a pasta src do seu computador aparece dentro do container. Você edita o código no seu editor e o resultado aparece ao recarregar o navegador, sem rebuild.
  • environment — as credenciais do banco chegam ao PHP como variáveis de ambiente. Os valores ${DB_NAME} e similares vêm do arquivo .env.
  • depends_on com condition: service_healthy — o PHP só inicia depois que o MySQL estiver realmente pronto para conexões, e não apenas com o container criado.

Serviço db (MySQL)

  • image: mysql:8.4 — versão LTS do MySQL. Fixar a versão evita surpresas quando a imagem latest mudar.
  • environment — a imagem oficial do MySQL lê essas variáveis na primeira inicialização para criar a senha do root, o banco e o usuário da aplicação.
  • dados-mysql:/var/lib/mysql — um volume nomeado onde o MySQL grava os dados. Sem ele, tudo seria apagado ao remover o container.
  • init.sql em /docker-entrypoint-initdb.d/ — scripts nessa pasta rodam automaticamente apenas na primeira vez, quando o volume está vazio.
  • healthcheck — a cada 5 segundos, o Compose executa mysqladmin ping. Quando responde, o serviço fica healthy e libera o app.

Repare que o db não publica porta. O PHP e o phpMyAdmin o acessam pela rede interna, e o banco não fica exposto. Se quiser conectar um cliente como DBeaver ou MySQL Workbench, adicione ports: - "3306:3306".

Serviço phpmyadmin

Uma interface web para administrar o banco. O PMA_HOST: db aponta para o serviço do MySQL. Acesse em http://localhost:8081 com o usuário e a senha definidos no .env.

Passo 3: o Dockerfile e o arquivo .env

A imagem oficial php:8.4-apache não vem com a extensão de MySQL habilitada. O Dockerfile resolve isso com o utilitário docker-php-ext-install, que já vem na imagem:

Dockerfile baseado em php:8.4-apache instalando pdo_mysql
Duas linhas bastam para adicionar o driver PDO do MySQL

Precisa de outras extensões? Adicione na mesma linha: RUN docker-php-ext-install pdo_mysql mysqli bcmath. Para extensões que dependem de bibliotecas do sistema, como gd e zip, é necessário instalar pacotes com apt-get antes.

Em seguida, crie o .env na raiz (o Compose lê esse arquivo automaticamente):

DB_NAME=loja
DB_USER=app
DB_PASSWORD=senha_app_123
DB_ROOT_PASSWORD=senha_root_456

Adicione o .env ao .gitignore e versione um .env.example sem as senhas reais. O motivo é explicado em Variáveis de ambiente e arquivo .env.

Passo 4: dados iniciais e o código PHP

O script docker/mysql/init.sql cria uma tabela de produtos com três registros:

CREATE TABLE IF NOT EXISTS produtos (
  id INT AUTO_INCREMENT PRIMARY KEY,
  nome VARCHAR(100) NOT NULL,
  preco DECIMAL(10,2) NOT NULL
);

INSERT INTO produtos (nome, preco) VALUES
  ('Teclado mecânico', 349.90),
  ('Mouse sem fio', 129.00),
  ('Monitor 27"', 1499.00);

E o src/index.php conecta ao banco com PDO e lista os produtos:

Código index.php conectando ao MySQL com PDO usando variáveis de ambiente
O PHP lê as credenciais das variáveis de ambiente e usa o host db

Pontos importantes do código:

  • O DSN usa getenv('DB_HOST'), que vale db — o nome do serviço no Compose.
  • PDO::ERRMODE_EXCEPTION faz o PDO lançar exceções em vez de falhar em silêncio.
  • htmlspecialchars() protege a página contra XSS ao exibir dados do banco.
  • charset=utf8mb4 garante acentos e emojis corretos.

Passo 5: validando e subindo o ambiente

Antes de subir, valide o arquivo. O docker compose config mostra a configuração final, com as variáveis do .env já substituídas — se houver erro de indentação no YAML, ele aponta aqui:

Terminal com docker compose config validando os serviços e variáveis
O config confirma os três serviços e as variáveis carregadas do .env

Agora suba tudo:

docker compose up -d --build
  • up cria e inicia os containers.
  • -d roda em segundo plano.
  • --build força a construção da imagem do app (use sempre que alterar o Dockerfile).
Terminal com docker compose up criando rede, volume e containers, e docker compose ps
O Compose cria a rede e o volume, espera o MySQL ficar healthy e inicia o PHP e o phpMyAdmin

Na primeira execução o Docker baixa as imagens, então pode levar alguns minutos. Observe a ordem: o db fica Healthy antes do app iniciar, graças ao depends_on com healthcheck.

Abra http://localhost:8080:

Página PHP no navegador listando produtos do MySQL via Docker Compose
O PHP rodando no container app lê os produtos do MySQL no container db

Seu ambiente Docker Compose com PHP e MySQL está funcionando. Edite o index.php, salve e recarregue a página: a mudança aparece na hora, graças ao bind mount.

Passo 6: comandos do dia a dia com Docker Compose

docker compose ps                 # status dos serviços
docker compose logs -f app        # logs do PHP/Apache em tempo real
docker compose exec app bash      # terminal dentro do container PHP
docker compose exec db mysql -u app -p loja   # cliente MySQL
docker compose restart app        # reinicia um serviço
docker compose stop               # para tudo (mantém containers)
docker compose down               # remove containers e rede (mantém o volume)
docker compose down -v            # remove também o volume (APAGA o banco)
Terminal com docker compose exec no MySQL, logs do Apache e docker compose down
Consultando o banco pelo terminal, lendo os logs e desligando o ambiente

Atenção à diferença entre down e down -v: o primeiro preserva os dados do banco no volume; o segundo apaga tudo. Use o -v quando quiser recomeçar do zero — por exemplo, para rodar o init.sql novamente.

Adaptando para Laravel e outros frameworks

O mesmo modelo serve para projetos maiores com poucos ajustes:

  • Laravel: aponte o DocumentRoot do Apache para a pasta public e preencha no .env do Laravel DB_CONNECTION=mysql, DB_HOST=db e as credenciais. Rode comandos com docker compose exec app php artisan migrate. Veja o básico do framework em Laravel da instalação ao Hello World.
  • Composer dentro do container: adicione ao Dockerfile a linha COPY --from=composer:2 /usr/bin/composer /usr/bin/composer.
  • Nginx + PHP-FPM: troque a imagem php:8.4-apache por php:8.4-fpm e adicione um serviço nginx. É a combinação mais usada em produção.
  • Redis, filas e e-mail: adicione serviços como redis:7 e mailpit ao mesmo arquivo.

Erros comuns com Docker Compose, PHP e MySQL

“SQLSTATE[HY000] [2002] Connection refused” — o PHP tentou conectar antes de o MySQL estar pronto, ou você usou localhost como host. Use DB_HOST=db e o depends_on com healthcheck.

“could not find driver” — a extensão pdo_mysql não foi instalada. Confira o Dockerfile e rode docker compose up -d --build.

“Access denied for user” — você alterou as senhas no .env depois que o volume já existia. As variáveis do MySQL só valem na primeira inicialização. Rode docker compose down -v (isso apaga os dados) e suba novamente.

O init.sql não executou — ele só roda com o volume vazio. Mesma solução: down -v e up.

“port is already allocated” — outro serviço usa a porta 8080 ou 8081. Troque o lado esquerdo do mapeamento, por exemplo "8090:80".

Erro de indentação no YAML — o YAML usa espaços (nunca tabs) e a indentação importa. O docker compose config mostra a linha do problema.

Perguntas frequentes

Qual a diferença entre docker-compose.yml e compose.yaml?

Os dois funcionam. compose.yaml é o nome preferido na especificação atual; docker-compose.yml continua aceito por compatibilidade. A chave version: no topo do arquivo também é obsoleta e pode ser removida.

Posso usar Docker Compose em produção?

Pode, especialmente em servidores únicos e projetos pequenos ou médios. Nesse caso, remova os bind mounts de código, use imagens com o código copiado no build, não exponha o banco e guarde senhas com mais cuidado. Para vários servidores e alta disponibilidade, considere orquestradores como Kubernetes.

Os dados do MySQL ficam salvos se eu desligar o computador?

Sim. Os dados ficam no volume dados-mysql, que sobrevive a docker compose stop, down e reinicializações. Só são apagados com docker compose down -v ou docker volume rm.

Como trocar a versão do PHP?

Altere a linha FROM do Dockerfile (por exemplo, php:8.3-apache) e rode docker compose up -d --build. Em segundos você testa o projeto em outra versão, sem instalar nada no computador.

Conclusão

Você montou um ambiente com Docker Compose, PHP e MySQL do zero: entendeu cada linha do compose.yaml, personalizou a imagem do PHP, inicializou o banco com um script SQL, conectou via PDO usando variáveis de ambiente e aprendeu os comandos para operar o ambiente no dia a dia. A partir de agora, qualquer pessoa do seu time sobe o projeto com docker compose up.

Com o ambiente pronto, que tal fazer sua aplicação PHP conversar com o mundo? No tutorial Como enviar SMS com PHP usando a SDK da APIBrasil você adiciona envio de SMS ao projeto em poucas linhas, e a mesma SDK da APIBrasil oferece consultas de CEP, CNPJ, placas e WhatsApp.

Loading

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *