SSD Nodes Learn
Guias Matt ConnorPor Matt Connor · Atualizado 2026-07-24

Como instalar LAMP stack no Ubuntu 24.04

Guia para configurar Apache, MariaDB com unix_socket, PHP 8.3 via PHP-FPM e Certbot no Ubuntu 24.04. Aprenda a evitar erros de permissão e páginas em branco.

O que você está construindo

Uma stack LAMP consiste em quatro componentes em um servidor Ubuntu 24.04: Linux como base, Apache para requisições HTTP, MariaDB para armazenamento de dados e PHP 8.3 para execução do código. Ao final, você terá um virtual host baseado em nome servindo um diretório de aplicação real, um banco de dados com um usuário dedicado de privilégio mínimo, PHP integrado ao Apache via PHP-FPM e um certificado Let's Encrypt gratuito.

A instalação consiste em quatro comandos apt. Quase todo o conteúdo deste guia foca na integração entre as partes e nos erros comuns que fazem uma stack nova exibir uma página em branco, entregar o código-fonte como download no navegador ou impedir o acesso ao banco de dados recém-instalado. Cada erro possui um padrão identificável, listado abaixo com o texto exato que você encontrará.

Pré-requisitos e observações importantes

Considere um VPS Ubuntu 24.04 KVM recém-instalado com um usuário sudo ou root e um endereço IPv4 público. Uma stack mínima roda com 1 GB de RAM; reserve 2 GB antes de instalar uma aplicação real com banco de dados, pois os buffers padrão do MariaDB somados a alguns processos PHP-FPM consomem o primeiro gigabyte rapidamente.

Duas condições devem ser atendidas para que o Certbot funcione ao final; configure-as agora. Você precisa de um nome de domínio com um registro A apontando para o IP público do VPS — o Let's Encrypt valida via HTTP para esse nome, e um endereço IP isolado não consegue obter um certificado. Além disso, as portas 80 e 443 devem estar acessíveis pela internet, o que em muitos provedores exige a abertura no firewall da rede no painel de controle além de no ufw no servidor. Alterações de DNS podem levar até uma hora para propagar; configure o registro A primeiro e ele estará ativo quando você precisar.

Passo 1 - Instalar o Apache e confirmar a página padrão

sudo apt update
sudo apt install -y apache2

O apt inicia e habilita o serviço automaticamente. Verifique:

systemctl status apache2

Você deve ver uma linha com active (running). Agora abra http://YOUR_SERVER_IP/ em um navegador. A Apache2 Ubuntu Default Page com o banner "It works!" é o resultado correto — isso prova que o Apache está operando, não é um erro. Essa página está em /var/www/html/index.html e é servida pelo virtual host padrão 000-default.conf. Você desabilitará ambos mais tarde; por enquanto, a presença deles é o esperado.

Se a página não carregar, mas o systemctl indicar que o processo está rodando, um firewall está bloqueando o acesso. O próximo passo tratará disso.

Passo 2 - Abrir o firewall para HTTP e HTTPS

O pacote apache2 registra três perfis de aplicação ufw. Liste-os:

sudo ufw app list

Você verá Apache, Apache Full e Apache Secure. Apache é apenas a porta 80, Apache Secure é apenas a porta 443, e Apache Full contempla ambas — esta é a opção necessária, pois você adicionará TLS ao final.

sudo ufw allow OpenSSH
sudo ufw allow "Apache Full"
sudo ufw enable

Permita OpenSSH antes de executar o ufw enable. O ufw nega todo o tráfego de entrada por padrão. Habilitá-lo sem uma regra de SSH interrompe sua própria conexão assim que o serviço for ativado — você mantém a sessão atual, mas não conseguirá reconectar. Confirme com sudo ufw status; você deve ver OpenSSH, Apache Full e seus equivalentes v6 todos como ALLOW.

Passo 3 - Instalar o MariaDB e protegê-lo

sudo apt install -y mariadb-server
systemctl status mariadb

O Ubuntu 24.04 inclui o MariaDB 10.11, uma versão com suporte de longo prazo (LTS), portanto você não precisa de um repositório externo. Com o serviço em execução, realize o hardening:

sudo mysql_secure_installation

É recomendável ler as mensagens em vez de apenas pressionar Enter. Quando solicitado o current root password, pressione Enter — ainda não existe uma senha. Quando perguntar "Switch to unix_socket authentication?", a resposta não altera nada pois o recurso já vem habilitado neste pacote, então pressione n. Responda n para "Change the root password?" pelo motivo explicado no próximo parágrafo, e responda Y para o restante: remover usuários anônimos, desabilitar login remoto do root, remover o banco de dados de teste e recarregar as tabelas de privilégios.

Esta é a parte que confunde a todos. No MariaDB do Ubuntu, a conta de banco de dados root utiliza autenticação unix_socket, não uma senha. Isso significa que o banco de dados confia no usuário do operating-system que você já autenticou. Assim, o comando funciona a partir de um shell root:

sudo mysql

...e o comando te leva a um prompt MariaDB [(none)]> sem solicitar senha. O mesmo comando executado como um usuário comum é recusado, que é o objetivo principal: o acesso ao root do banco de dados está vinculado ao sudo na máquina, e não há senha para ser roubada, sofrer phishing ou ataque de brute-force. Isso é mais seguro que uma senha, portanto, não altere. A regra que decorre disso é: nunca aponte uma aplicação para a conta root. Crie um usuário dedicado para cada aplicação (Passo 7), pois uma aplicação conectando via TCP com usuário e senha não pode usar autenticação por socket, e você deve limitar cada aplicação ao seu próprio banco de dados.

Passo 4 - Instalar PHP 8.3 com PHP-FPM

O PHP padrão do Ubuntu 24.04 é o 8.3. Instale o gerenciador de processos FPM e as extensões necessárias para uma aplicação típica:

sudo apt install -y php8.3-fpm php8.3-mysql php8.3-cli \
  php8.3-curl php8.3-xml php8.3-mbstring php8.3-zip

Observe o que não está na lista: libapache2-mod-php. Esse pacote antigo incorpora um interpretador PHP dentro de cada processo do Apache. É simples, mas cada worker carrega uma cópia do PHP independentemente de estar servindo um script ou uma imagem estática; ambos compartilham o mesmo ciclo de vida, e ele só funciona com o MPM prefork do Apache — o menos eficiente. Em vez disso, o PHP-FPM executa o PHP como um pool próprio de processos com o qual o Apache se comunica via socket. O Apache pode então usar o MPM event (threaded) para arquivos estáticos e encaminhar apenas as requisições PHP, o pool é ajustado independentemente do servidor web, e a mesma configuração de FPM funcionará se você usar o nginx na frente posteriormente. É o padrão atual por um bom motivo.

O Apache acessa o FPM através do módulo proxy_fcgi. Ative-o, ative a configuração instalada pelo pacote FPM e reinicie:

sudo a2enmod proxy_fcgi setenvif
sudo a2enconf php8.3-fpm
sudo systemctl restart apache2

O a2enconf php8.3-fpm ativa o /etc/apache2/conf-available/php8.3-fpm.conf, que contém a regra que roteia arquivos PHP para o socket do FPM. O núcleo do módulo identifica qualquer arquivo .php e o encaminha para o socket em /run/php/php8.3-fpm.sock:

<FilesMatch ".+\.ph(ar|p|tml)$">
    SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost"
</FilesMatch>

Você não edita esse arquivo; ele já vem configurado corretamente. Mas saber o caminho do socket é o que permite diagnosticar erros como "PHP faz download em vez de executar" e "Primary script unknown" futuramente — ambos ocorrem quando o Apache e o FPM discordam sobre este socket ou sobre o arquivo associado a ele.

Passo 5 - Um virtual host baseado em nome para seu app

O virtual hosting baseado em nome permite que um único IP atenda vários sites; o Apache seleciona o site pelo header Host: na requisição. Crie um diretório para o app, longe do /var/www/html padrão:

sudo mkdir -p /var/www/testapp
sudo chown -R www-data:www-data /var/www/testapp
sudo chmod -R 755 /var/www/testapp

A propriedade dos arquivos é importante. O Apache e o PHP-FPM rodam como o usuário www-data no Ubuntu, portanto, os arquivos que o servidor web deve ler — e diretórios que o app deve escrever, como uma pasta de uploads — devem pertencer ao www-data. Se você também for editar arquivos com seu usuário de login, um padrão comum é ser o dono dos arquivos e adicionar seu usuário ao grupo www-data; para um deploy simples, o www-data:www-data é a opção mais previsível.

Crie o virtual host em /etc/apache2/sites-available/testapp.conf:

<VirtualHost *:80>
    ServerName app.example.com
    DocumentRoot /var/www/testapp

    <Directory /var/www/testapp>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/testapp-error.log
    CustomLog ${APACHE_LOG_DIR}/testapp-access.log combined
</VirtualHost>

Defina ServerName para o seu domínio real. O Options -Indexes impede que o Apache liste o diretório quando não houver um arquivo index — caso contrário, os visitantes podem navegar pela sua árvore de código-fonte. O AllowOverride All permite o funcionamento de um arquivo .htaccess, que a maioria das aplicações PHP espera para URLs amigáveis; remova-o em None para um pequeno ganho de performance se seu app não precisar dele. Ative este site, desative o padrão, verifique a configuração e recarregue:

sudo a2ensite testapp
sudo a2dissite 000-default
sudo apache2ctl configtest
sudo systemctl reload apache2

O apache2ctl configtest deve imprimir Syntax OK. A linha a2dissite 000-default é a que as pessoas esquecem, e é por isso que a página padrão parece travada posteriormente — detalhado na seção de erros.

Passo 6 - Verifique se o PHP está funcionando e depois delete o arquivo de teste

Crie um arquivo PHP de uma linha no diretório raiz do app:

echo "<?php phpinfo();" | sudo tee /var/www/testapp/info.php

Acesse http://app.example.com/info.php. O resultado correto é a tabela longa em roxo e cinza PHP Version 8.3.x listando os módulos carregados, com a linha Server API exibindo FPM/FastCGI. Essa última linha confirma que as requisições estão passando pelo PHP-FPM, e não pelo mod_php.

Agora delete o arquivo imediatamente:

sudo rm /var/www/testapp/info.php

O phpinfo() expõe sua versão exata do PHP, todas as extensões carregadas, caminhos de arquivos e detalhes do ambiente — uma vulnerabilidade para qualquer pessoa que esteja sondando o servidor em busca de versões com falhas conhecidas. Isso é um teste, não uma funcionalidade. Delete o arquivo assim que visualizar a página. Se o seu navegador oferecer para baixar o info.php em vez de exibir a tabela, o PHP não está configurado corretamente com o Apache; vá para a seção de erros antes de continuar.

Passo 7 - Criar o banco de dados da aplicação e um usuário com privilégios mínimos

Acesse o banco de dados como root via socket-authentication:

sudo mysql

Em seguida, crie um banco de dados e um usuário com escopo restrito a esse banco:

CREATE DATABASE appdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'a-long-random-password';
GRANT ALL PRIVILEGES ON appdb.* TO 'appuser'@'localhost';
FLUSH PRIVILEGES;
EXIT;

Três escolhas deliberadas aqui. utf8mb4 é o UTF-8 de quatro bytes real — o antigo alias utf8 trunca silenciosamente emojis e alguns caracteres CJK, portanto sempre use utf8mb4. O grant é para appdb.*, não para *.*: este usuário pode acessar apenas o seu próprio banco de dados, impedindo que uma vulnerabilidade de SQL-injection na aplicação leia as tabelas de outros sites. E 'appuser'@'localhost' restringe a conta a conexões originadas na própria máquina.

Teste como esse usuário:

mysql -u appuser -p appdb

O sistema solicitará a senha e abrirá um prompt MariaDB [appdb]>. Note que não há a flag -h — mantenha-a desativada para que o cliente conecte via Unix socket local, que é como o MariaDB identifica localhost. Um detalhe importante: para o MySQL e MariaDB, localhost significa o Unix socket e 127.0.0.1 significa uma conexão TCP. Em um MariaDB padrão no Ubuntu 24.04, o servidor ainda resolve uma conexão TCP de 127.0.0.1 para localhost, então ambos coincidem com a conta — mas em servidores com skip-name-resolve habilitado (um ajuste comum de performance e o padrão em muitas imagens de container), os dois são tratados como hosts diferentes. Uma aplicação que tenta conectar via 127.0.0.1 será rejeitada com ERROR 1045 (28000): Access denied for user 'appuser'@'127.0.0.1' (using password: YES) mesmo com a senha correta.

Portanto, aponte sua aplicação para o host localhost, usuário appuser, banco de dados appdb — nunca para root. O mysqli do PHP e o PDO alternam para o Unix socket quando o host é a string literal localhost, correspondendo à conta que você acabou de criar. Se um framework exigir um host TCP numérico, crie o usuário para coincidir com o método de conexão real — 'appuser'@'127.0.0.1', ou @'%' (acompanhado de uma regra de firewall) apenas se a conexão precisar vir de outra máquina.

Passo 8 - Adicionar HTTPS com Certbot

Servir um formulário de login via HTTP envia senhas em texto puro, e todos os navegadores modernos marcam a página como "Não segura". O Certbot resolve isso com um único comando. Instale-o com o plugin do Apache:

sudo apt install -y certbot python3-certbot-apache
sudo certbot --apache

O Certbot utiliza dois plugins aqui. O apache authenticator prova que você controla o domínio ao servir brevemente um arquivo de desafio através do seu Apache em execução, e o apache installer reescreve seu virtual host para adicionar o bloco 443, aponta para o novo certificado e redireciona todo o tráfego HTTP para HTTPS por padrão — desde o Certbot 2.0 não há pergunta sobre o redirecionamento; passe --no-redirect se precisar continuar servindo HTTP puro. Como você configurou um ServerName real no Passo 5, o Certbot detecta o domínio automaticamente. Os certificados duram 90 dias e o pacote instala um timer do systemd que os renova; verifique o timer com sudo certbot renew --dry-run, que deve terminar em Congratulations, all simulated renewals succeeded.

Para o guia completo sobre o desafio, o timer de renovação e os requisitos de DNS e firewall, veja o guia complementar em emitindo certificados TLS gratuitos do Let's Encrypt com Certbot no Apache.

Backups, upgrades, and hardening

Faça backup dos dois itens que mantêm o seu estado: os bancos de dados e o web root. Um dump lógico diário é a abordagem mais simples e confiável — sudo sh -c 'mysqldump --all-databases --single-transaction | gzip > /root/db-$(date +%F).sql.gz', seguido de cópia para fora do servidor. Envolver todo o pipeline com sudo sh -c é essencial: sem isso, o shell executa o redirect > /root/... com seu próprio usuário e falha com Permission denied, pois apenas o mysqldump herdou o sudo. O --single-transaction fornece um snapshot consistente das tabelas InnoDB sem bloqueá-las. Combine-o com um tar de /var/www e /etc/apache2/sites-available para reconstruir toda a stack em um novo VPS a partir desses arquivos.

Upgrades são um sudo apt update && sudo apt upgrade comum. O problema real é o upgrade da versão do PHP — quando uma versão futura do Ubuntu alterar o padrão para PHP 8.4, o apt pode instalar o php8.4-fpm junto com o 8.3, o socket passará a ser /run/php/php8.4-fpm.sock, e sua configuração do Apache continuará apontando para o socket do 8.3. Ative o novo conf (sudo a2enconf php8.4-fpm) e desative o antigo, ou seu site começará a retornar Primary script unknown após um upgrade que seria rotineiro. Como os lançamentos do PHP são mais rápidos que uma distro LTS, verifique as notas de lançamento do PHP atual em vez de fixar uma versão de patch.

Dois passos de hardening valem a pena no primeiro dia. Primeiro, instale o Fail2Ban monitorando o SSH no servidor — um VPS público recebe tentativas de login automatizadas em minutos, e um jail pequeno transforma milhares de tentativas em apenas algumas antes do banimento. Segundo, se você prefere gerenciar virtual hosts do Apache, bancos de dados MariaDB e usuários via navegador em vez de editar arquivos manualmente, o painel de controle Webmin funciona sobre esta exata stack e gerencia os mesmos arquivos de configuração que você acabou de escrever. Nenhum dos dois substitui o conhecimento sobre os componentes, mas ambos reduzem o atrito no dia a dia.

Modos de falha, com as strings que você verá

A página padrão não desaparece. Você editou seu virtual host, recarregou, e o navegador ainda exibe "Apache2 Ubuntu Default Page" e o banner "It works!". O Apache serve o primeiro virtual host correspondente e, quando nenhum ServerName coincide com a requisição, o primeiro arquivo por ordem alfabética vence — 000-default.conf vem antes de testapp.conf. Ou o hostname da requisição não coincide com seu ServerName, ou você nunca executou o sudo a2dissite 000-default. Desative o padrão, sudo systemctl reload apache2, e confirme com o apache2ctl -S, que imprime o mapa de vhosts e mostra qual config é a dona do padrão. Limpe o cache do navegador também; um 200 em cache da página antiga persiste facilmente.

Um arquivo .php é baixado em vez de ser executado. Você abre info.php e o navegador baixa um arquivo contendo o código-fonte <?php bruto, ou o exibe como texto puro, em vez de executá-lo. O Apache está servindo o arquivo como um asset estático porque o handler de PHP não está anexado — você pulou o sudo a2enmod proxy_fcgi, ou o sudo a2enconf php8.3-fpm, ou não reiniciou o Apache depois. Execute os três (Step 4) e recarregue. Confirme se o módulo está carregado com apache2ctl -M | grep fcgi, que deve listar proxy_fcgi_module. Isso é um vazamento de código-fonte, não um erro cosmético; corrija antes de colocar qualquer dado real no servidor.

ERROR 1698 (28000): Access denied for user 'root'@'localhost'. Você executou mysql -u root ou mariadb -u root sem o sudo. A conta root usa auth via unix_socket, então ela só aceita você quando seu usuário do OS é o root. A correção é sudo mysql — sem -u root, sem senha. Esta mensagem é o comportamento esperado de uma auth via socket funcionando corretamente, não uma instalação quebrada.

ERROR 1045 (28000): Access denied for user 'appuser'@'127.0.0.1' da aplicação, com a senha correta. A conta existe como 'appuser'@'localhost', mas sua aplicação está conectando via TCP para 127.0.0.1 em um servidor onde a resolução de hostname está desativada (skip-name-resolve), então o MariaDB entende os dois como hosts diferentes — localhost é o Unix socket, 127.0.0.1 é TCP. Aponte a aplicação para o host localhost para que ela use o socket e coincida com a conta, ou crie uma segunda conta 'appuser'@'127.0.0.1' se o framework só aceitar TCP.

AH01071: Got error 'Primary script unknown' em /var/log/apache2/testapp-error.log, com o navegador exibindo File not found.. O Apache passou a requisição para o PHP-FPM, mas o FPM não encontrou o script no caminho fornecido pelo Apache. Duas causas comuns: o socket do FPM na sua config aponta para uma versão do PHP que não está instalada (um socket php8.4 após um upgrade enquanto apenas a 8.3 está rodando), ou o arquivo realmente não está lá porque DocumentRoot e o diretório real divergem. Verifique se o socket existe com ls -l /run/php/, confirme se DocumentRoot coincide com o local do arquivo, e reinicie o php8.3-fpm e o apache2.

AH00558: apache2: Could not reliably determine the server's fully qualified domain name em cada reinicialização. Este é um aviso inofensivo, não um erro — o Apache está informando que nenhum ServerName global foi definido. Silencie-o escrevendo ServerName your.domain em /etc/apache2/conf-available/servername.conf e executando sudo a2enconf servername.

(98)Address already in use: AH00072: make_sock: could not bind to address 0.0.0.0:80 quando o Apache inicia. Outro servidor web já está ocupando a porta 80 — frequentemente um nginx perdido de um experimento anterior. Encontre-o com sudo ss -ltnp | grep :80, então pare e desative o outro serviço antes de iniciar o Apache.

FAQ

mod_php ou PHP-FPM - qual devo usar?

Use PHP-FPM. O mod_php incorpora um interpretador em cada processo do Apache e força o uso do lento prefork MPM, o que faz o Apache carregar o overhead do PHP mesmo ao servir uma imagem estática. O PHP-FPM executa o PHP como um pool separado e ajustável de forma independente, o qual o Apache acessa via socket, permitindo o uso do mais rápido event MPM, e funciona da mesma forma ao migrar para nginx futuramente. É o padrão moderno; o mod_php só faz sentido para aplicações legadas que dependem de algum comportamento in-process.

Por que meu navegador faz o download do arquivo PHP em vez de executá-lo?

O Apache está tratando o arquivo .php como um download estático porque nenhum handler de PHP está associado a ele. No Ubuntu 24.04 com FPM, isso significa que você esqueceu um dos passos sudo a2enmod proxy_fcgi, sudo a2enconf php8.3-fpm, ou o restart do Apache. Execute os três e recarregue, então verifique com apache2ctl -M | grep fcgi se proxy_fcgi_module está listado. Até que você corrija isso, o servidor está vazando código-fonte; trate isso como urgente.

Por que o acesso root é negado no MariaDB mesmo com a senha correta?

Porque não há senha — o MariaDB do Ubuntu autentica a conta root via unix_socket, vinculando-a ao usuário root do sistema operacional. O comando mysql -u root em um shell comum retorna ERROR 1698 (28000): Access denied for user 'root'@'localhost' por design. Conecte-se via sudo mysql e crie um usuário com autenticação por senha para cada aplicação em vez de reutilizar o root.

Como adiciono HTTPS ao meu site LAMP?

Instale o certbot e o python3-certbot-apache, aponte o registro A do domínio para o servidor e execute o sudo certbot --apache. O autenticador do apache comprova o controle do domínio através do seu Apache em execução e o instalador reescreve o virtual host para a porta 443 e configura a renovação automática. O guia completo de Certbot e Apache aborda o desafio, o timer de renovação e os modos de falha comuns.

#lamp#apache#mariadb#php-fpm#ubuntu