SSD Nodes Learn Hosting plans →
Guias Matt ConnorPor Matt Connor · Atualizado 2026-08-02

Como instalar LAMP no Ubuntu 24.04 com PHP-FPM

Configure Apache, MariaDB com unix_socket, PHP 8.3 via PHP-FPM, vhost por nome e HTTPS grátis com Certbot no Ubuntu 24.04, passo a passo.

O que você está construindo

Uma pilha LAMP consiste em quatro componentes em um servidor Ubuntu 24.04: Linux como base, Apache respondendo a requisições HTTP, MariaDB armazenando os dados e PHP 8.3 executando o código. Ao final, você terá um host virtual baseado em nome servindo um diretório de aplicação real, um banco de dados com um usuário dedicado e privilégios mínimos, PHP integrado ao Apache por meio do PHP-FPM e um certificado gratuito da Let's Encrypt.

A instalação em si consiste em quatro comandos apt. Quase tudo neste guia trata da integração entre esses componentes e do pequeno conjunto de erros que faz uma pilha recém-instalada exibir uma página em branco, entregar o código-fonte ao navegador como download ou impedir o acesso ao banco de dados que você acabou de instalar. Todos esses problemas têm um padrão que você pode reconhecer, e cada um é identificado abaixo com o texto exato que você verá.

Pré-requisitos e limitações importantes

Considere uma VPS KVM Ubuntu 24.04 recém-instalada, com um usuário sudo ou root e um endereço IPv4 público. Uma pilha mínima funciona com 1 GB de RAM. Reserve 2 GB antes de instalar uma aplicação real que use banco de dados, porque os buffers padrão do MariaDB e alguns workers do PHP-FPM consomem rapidamente o primeiro gigabyte.

Duas condições precisam ser atendidas para que o Certbot funcione no final. Configure-as agora. Você precisa de um nome de domínio com um registro A apontando para o IP público da VPS. O Let's Encrypt faz a validação por HTTP nesse nome. Um endereço IP simples nunca pode receber um certificado. As portas 80 e 443 também precisam estar acessíveis pela internet. Em muitos provedores, isso significa liberá-las no firewall de rede do painel de controle e também no ufw do servidor. As alterações de DNS podem levar até uma hora para se propagar. Configure o registro A primeiro para que ele esteja ativo quando você precisar dele.

Etapa 1 - Instale o Apache e confirme a página padrão

sudo apt update
sudo apt install -y apache2

apt inicia e habilita o serviço para você. 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 grande "It works!", é o resultado correto. Ela confirma que o Apache está atendendo, não é um erro. Essa página está em /var/www/html/index.html e é atendida pelo host virtual padrão fornecido em 000-default.conf. Você desabilitará ambos mais adiante. Por enquanto, a presença deles é exatamente o que você deve ver.

Se a página não carregar, mas systemctl indicar que o processo está em execução, há um firewall bloqueando o acesso. Essa é a próxima etapa.

Etapa 2 - Abra o firewall para HTTP e HTTPS

O pacote apache2 registra três perfis de aplicativo ufw. Liste-os:

sudo ufw app list

Você verá Apache, Apache Full e Apache Secure. Apache usa apenas a porta 80, Apache Secure usa apenas a porta 443 e Apache Full usa ambas. Esse é o perfil que você quer, porque adicionará TLS no final.

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

Permita OpenSSH antes de executar ufw enable. Por padrão, ufw nega todo o tráfego de entrada. Ativá-lo sem uma regra para SSH interrompe sua própria conexão assim que ele é ativado. Você mantém a sessão atual, mas não consegue se reconectar. Confirme com sudo ufw status. OpenSSH, Apache Full e os equivalentes v6 devem estar todos definidos como ALLOW.

Etapa 3 - Instale o MariaDB e proteja-o

sudo apt install -y mariadb-server
systemctl status mariadb

O Ubuntu 24.04 fornece o MariaDB 10.11, uma versão com suporte de longo prazo. Portanto, você não precisa de um repositório externo. Com o serviço em execução, reforce a segurança:

sudo mysql_secure_installation

Leia os prompts em vez de pressionar Enter repetidamente. Quando for solicitada a senha root atual, pressione Enter, pois ela ainda não existe. Quando aparecer "Switch to unix_socket authentication?", a resposta não altera nada, porque esse recurso já está habilitado neste pacote. Portanto, pressione n. Responda n a "Change the root password?" pelo motivo explicado no próximo parágrafo. Depois, responda Y às demais perguntas: remova os usuários anônimos, desabilite o login remoto do root, remova o banco de dados de teste e recarregue as tabelas de privilégios.

Esta é a parte que costuma causar confusão. No MariaDB do Ubuntu, a conta de banco de dados root usa autenticação unix_socket, não uma senha. Isso significa que o banco confia no usuário do sistema operacional com o qual você já se autenticou. Portanto, isto funciona em um shell do root:

sudo mysql

...e abre um prompt do MariaDB [(none)]> sem solicitar senha. O mesmo comando executado por um usuário sem privilégios é recusado. Esse é o objetivo: o acesso ao root do banco de dados está vinculado a sudo no servidor, e não existe uma senha para roubar, obter por phishing ou descobrir por força bruta. Isso é mais seguro do que uma senha, portanto, mantenha essa configuração. A regra resultante é: nunca configure um aplicativo para usar a conta root. Crie um usuário dedicado para cada aplicativo (Etapa 7), porque um aplicativo que se conecta por TCP usando nome de usuário e senha não pode usar autenticação por socket. Além disso, cada aplicativo deve ficar limitado ao próprio banco de dados.

Etapa 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á nessa lista: libapache2-mod-php. Esse pacote mais antigo incorpora um interpretador PHP em cada processo do Apache. Ele é simples, mas cada worker mantém uma cópia do PHP, esteja atendendo um script ou uma imagem estática. Ambos compartilham o mesmo ciclo de vida, e ele funciona somente com o MPM prefork do Apache, o menos eficiente. O PHP-FPM, por outro lado, executa o PHP em seu próprio pool de processos, com o qual o Apache se comunica por meio de um socket. Assim, o Apache pode usar o MPM event, baseado em threads, para arquivos estáticos e encaminhar somente as requisições PHP. O pool pode ser ajustado independentemente do servidor web, e a mesma configuração do FPM funcionará depois se você colocar o nginx na frente. Esse é o padrão atual por um bom motivo.

O Apache acessa o FPM por meio 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

a2enconf php8.3-fpm ativa /etc/apache2/conf-available/php8.3-fpm.conf, que contém a regra que encaminha arquivos PHP para o socket do FPM. A regra corresponde a 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>

Não edite esse arquivo; ele já vem com a configuração correta. No entanto, conhecer o caminho do socket permite diagnosticar posteriormente os erros "PHP é baixado em vez de ser executado" e "Primary script unknown". Ambos ocorrem porque o Apache e o FPM estão usando caminhos diferentes para esse socket ou para o arquivo associado a ele.

Etapa 5 - Um host virtual baseado em nome para seu aplicativo

A hospedagem virtual baseada em nome permite que um único IP atenda a vários sites; o Apache escolhe o site pelo cabeçalho Host: na solicitação. Crie um diretório para o aplicativo, bem separado 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 é importante. O Apache e o PHP-FPM são executados como o usuário www-data no Ubuntu. Portanto, os arquivos que o servidor Web precisa ler e os diretórios nos quais o aplicativo precisa gravar, como uma pasta de uploads, devem pertencer a www-data. Se você também editar arquivos com seu usuário de login, um padrão comum é ser o proprietário dos arquivos e adicionar seu usuário ao grupo www-data; para uma implantação simples, www-data:www-data é a opção mais previsível.

Crie o host virtual 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 como seu domínio real. Options -Indexes impede que o Apache liste o diretório quando não há um arquivo de índice. Caso contrário, os visitantes poderão navegar pela sua árvore de código-fonte. AllowOverride All permite o funcionamento de um arquivo .htaccess, que a maioria dos aplicativos PHP espera para usar URLs amigáveis; altere-o para None para obter um pequeno ganho de velocidade se o aplicativo não precisar desse recurso. Ative este site, desative o padrão, verifique a configuração e recarregue o serviço:

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

apache2ctl configtest deve exibir Syntax OK. A linha a2dissite 000-default é a que as pessoas mais esquecem. Por isso, a página padrão pode parecer travada mais tarde, conforme descrito na seção de falhas.

Etapa 6 - Comprove que o PHP está funcionando e exclua o arquivo de teste

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

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, que lista 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 exclua o arquivo imediatamente:

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

phpinfo() expõe a versão exata do PHP, todas as extensões carregadas, os caminhos dos arquivos e detalhes do ambiente. Essas informações facilitam a exploração do servidor por alguém que esteja procurando uma versão com uma vulnerabilidade conhecida. Esse arquivo serve apenas para teste, não é um recurso do aplicativo. Exclua-o assim que visualizar a página. Se o navegador oferecer o download de info.php em vez de exibir a tabela, o PHP não está integrado ao Apache. Vá para a seção de falhas antes de fazer qualquer outra coisa.

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

Abra o banco de dados como root autenticado pelo socket:

sudo mysql

Em seguida, crie um banco de dados e um usuário com acesso restrito exatamente 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;

Há três escolhas deliberadas aqui. utf8mb4 é UTF-8 real de quatro bytes. O alias antigo utf8 trunca silenciosamente emojis e alguns caracteres CJK. Portanto, use sempre utf8mb4. A concessão de privilégios é feita em appdb.*, não em *.*. Esse usuário pode acessar o próprio banco de dados e nada mais. Assim, uma falha de SQL injection na aplicação não pode ler as tabelas de todos os outros sites. Além disso, 'appuser'@'localhost' restringe a conta a conexões originadas no próprio servidor.

Teste com esse usuário:

mysql -u appuser -p appdb

O comando solicita a senha e abre um prompt MariaDB [appdb]>. Observe que não há a flag -h. Deixe-a de fora. Assim, o cliente se conecta pelo socket Unix local, exatamente o que o MariaDB considera localhost. Há um detalhe importante: para MySQL e MariaDB, localhost significa o socket Unix, e 127.0.0.1 significa uma conexão TCP. Em uma instalação padrão do MariaDB no Ubuntu 24.04, o servidor ainda resolve uma conexão TCP de 127.0.0.1 para localhost. Portanto, ambas correspondem à conta. Porém, em servidores com skip-name-resolve habilitado, uma otimização de desempenho comum e o padrão em muitas imagens de contêiner, os dois valores são tratados como hosts diferentes. Nesse caso, uma aplicação que se conecta a 127.0.0.1 é recusada com ERROR 1045 (28000): Access denied for user 'appuser'@'127.0.0.1' (using password: YES), mesmo quando a senha está correta.

Configure a aplicação para usar o host localhost, o usuário appuser e o banco de dados appdb. Nunca use root. O mysqli do PHP e o PDO usam o socket Unix quando o host é a string literal localhost. Isso corresponde à conta que você acabou de criar. Se um framework exigir um host TCP numérico, crie o usuário de acordo com a forma como ele realmente se conecta: 'appuser'@'127.0.0.1' ou @'%', combinado com uma regra de firewall, somente se ele precisar acessar o banco de dados a partir de outra máquina.

Etapa 8 - Adicione HTTPS com Certbot

Servir um formulário de login por HTTP simples envia as senhas em texto sem criptografia, e todos os navegadores modernos sinalizam a página como "Não seguro". O Certbot resolve isso com um comando. Instale-o com o plugin do Apache:

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

O Certbot usa dois plugins aqui. O autenticador do apache comprova que você controla o domínio ao servir temporariamente um arquivo de desafio pelo Apache em execução, e o instalador do apache reescreve o host virtual para adicionar o bloco 443, aponta-o 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 simples. Como você definiu um ServerName real na Etapa 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 com Congratulations, all simulated renewals succeeded.

Para o passo a passo completo do desafio, do timer de renovação e dos requisitos de DNS e firewall, consulte o guia complementar sobre emissão de certificados TLS gratuitos da Let's Encrypt com Certbot no Apache.

Backups, atualizações e reforço de segurança

Faça backup dos dois itens que armazenam seu estado: os bancos de dados e a raiz da web. Um dump lógico noturno é a abordagem confiável mais simples, sudo sh -c 'mysqldump --all-databases --single-transaction | gzip > /root/db-$(date +%F).sql.gz', e depois deve ser copiado para fora do servidor. Encapsular todo o pipeline em sudo sh -c é importante: sem isso, o shell executa o redirecionamento > /root/... como seu próprio usuário e falha com Permission denied, porque somente mysqldump herdou sudo. --single-transaction fornece um snapshot consistente das tabelas InnoDB sem bloqueá-las. Combine isso com um tar de /var/www e /etc/apache2/sites-available, e você poderá reconstruir toda a stack em um VPS novo usando esses arquivos.

As atualizações são um sudo apt update && sudo apt upgrade comum. O caso problemático é uma atualização da versão do PHP, quando uma versão futura do Ubuntu alterar o padrão para PHP 8.4, apt poderá instalar php8.4-fpm junto com a versão 8.3, o socket passará a ser /run/php/php8.4-fpm.sock, e a configuração do Apache ainda apontará para o socket da versão 8.3. Habilite a nova configuração (sudo a2enconf php8.4-fpm) e desabilite a antiga, ou seu site começará a retornar Primary script unknown após uma atualização normal. Como as versões do PHP avançam mais rapidamente que uma distribuição LTS, consulte as notas de versão atuais do PHP em vez de fixar uma versão de patch.

Vale a pena aplicar duas medidas de reforço de segurança no primeiro dia. Primeiro, configure o Fail2Ban monitorando o SSH no servidor. Um VPS público recebe tentativas automatizadas de login em poucos minutos, e uma jail pequena reduz milhares de tentativas a poucas antes do bloqueio. Segundo, se preferir gerenciar hosts virtuais do Apache, bancos de dados MariaDB e usuários por meio de um navegador em vez de editar arquivos manualmente, o painel de controle baseado na web Webmin funciona sobre esta stack exata e altera os mesmos arquivos de configuração que você acabou de criar. Nenhuma das duas opções substitui o entendimento dos componentes, mas ambas reduzem o trabalho diário.

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

A página padrão não desaparece. Você editou o host virtual, recarregou a configuração, mas o navegador ainda mostra "Apache2 Ubuntu Default Page" e o banner "It works!". O Apache atende o primeiro host virtual correspondente. Quando nenhum ServerName corresponde à solicitação, vence a configuração que vem primeiro em ordem alfabética. 000-default.conf vem antes de testapp.conf. O nome do host da solicitação não corresponde ao seu ServerName, ou você não executou sudo a2dissite 000-default. Desative o host padrão com sudo systemctl reload apache2 e confirme com apache2ctl -S. Esse comando exibe o mapa de hosts virtuais e mostra qual configuração é a padrão. Limpe também o cache do navegador. Uma resposta 200 armazenada da página antiga pode continuar sendo exibida.

Um arquivo .php é baixado em vez de executado. Você abre info.php, mas o navegador baixa um arquivo que contém o código-fonte bruto <?php ou o exibe como texto simples, em vez de executá-lo. O Apache está servindo o arquivo como um recurso estático porque o manipulador do PHP não está associado, você ignorou sudo a2enmod proxy_fcgi ou sudo a2enconf php8.3-fpm, ou não reiniciou o Apache depois disso. Execute os três comandos (Etapa 4) e recarregue a página. Confirme se o módulo está carregado com apache2ctl -M | grep fcgi. A saída deve listar proxy_fcgi_module. Isso é um vazamento de código-fonte, não um problema visual. Corrija-o antes de colocar qualquer conteúdo real no servidor.

ERROR 1698 (28000): Access denied for user 'root'@'localhost'. Você executou mysql -u root ou mariadb -u root sem sudo. A conta root usa autenticação unix_socket. Portanto, ela só aceita a conexão quando o usuário do sistema operacional é realmente root. A correção é sudo mysql, sem -u root e sem senha. Essa mensagem indica o comportamento esperado da autenticação por socket funcionando corretamente. Ela não indica uma instalação com defeito.

ERROR 1045 (28000): Access denied for user 'appuser'@'127.0.0.1' na aplicação, mesmo com a senha correta. A conta existe como 'appuser'@'localhost', mas a aplicação está se conectando por TCP a 127.0.0.1 em um servidor no qual a resolução de nomes de host está desativada (skip-name-resolve). Assim, o MariaDB considera os dois hosts diferentes: localhost é o socket Unix, e 127.0.0.1 é TCP. Configure a aplicação para usar o host localhost. Assim, ela usará o socket e corresponderá à conta. Como alternativa, crie uma segunda conta 'appuser'@'127.0.0.1' se o framework aceitar somente TCP.

AH01071: Got error 'Primary script unknown' em /var/log/apache2/testapp-error.log, enquanto o navegador mostra File not found.. O Apache encaminhou a solicitação para o PHP-FPM, mas o FPM não encontrou o script no caminho fornecido pelo Apache. Há duas causas comuns. O socket do FPM na configuração aponta para uma versão do PHP que não está instalada, como um socket php8.4 após uma atualização enquanto somente a versão 8.3 está em execução. Ou o arquivo realmente não existe porque DocumentRoot e o diretório real são diferentes. Verifique se o socket existe com ls -l /run/php/. Confirme se DocumentRoot corresponde ao local onde o arquivo está armazenado. Depois, reinicie php8.3-fpm e apache2.

AH00558: apache2: Could not reliably determine the server's fully qualified domain name a cada reinicialização. Esse aviso é inofensivo, não um erro. O Apache está informando que nenhum ServerName global foi definido. Elimine o aviso definindo 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 é iniciado. Outro servidor web já está usando a porta 80, geralmente uma instância do nginx deixada em execução após um teste anterior. Encontre o processo com sudo ss -ltnp | grep :80. Depois, pare e desative o outro serviço antes de iniciar o Apache.

FAQ

mod_php ou PHP-FPM - qual devo usar?

Use PHP-FPM. mod_php incorpora um interpretador em cada processo do Apache e força o MPM lento prefork, portanto o Apache mantém a sobrecarga do PHP mesmo ao servir uma imagem estática. O PHP-FPM executa o PHP em um pool separado e ajustado de forma independente, que o Apache acessa por um socket, funciona com o MPM encadeado mais rápido event e pode ser transferido sem alterações para o nginx posteriormente. Ele é o padrão moderno; mod_php só faz sentido para uma aplicação legada que dependa de algum comportamento no processo.

Por que meu navegador baixa o arquivo PHP em vez de executá-lo?

O Apache está tratando o arquivo .php como um download estático porque nenhum manipulador PHP está associado a ele. No Ubuntu 24.04 com FPM, isso significa que você não executou um dos comandos sudo a2enmod proxy_fcgi, sudo a2enconf php8.3-fpm ou não reiniciou o Apache depois disso. Execute os três comandos e recarregue a página. Em seguida, verifique com apache2ctl -M | grep fcgi se proxy_fcgi_module está listado. Até corrigir o problema, o servidor está expondo o código-fonte. Trate isso como urgente.

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

Como não existe uma senha, o MariaDB do Ubuntu autentica a conta root por unix_socket, vinculando-a ao usuário root do sistema operacional. mysql -u root executado em um shell normal retorna ERROR 1698 (28000): Access denied for user 'root'@'localhost' por design. Conecte-se usando sudo mysql e crie um usuário separado, autenticado por senha, para qualquer aplicação em vez de reutilizar root.

Como adiciono HTTPS ao meu site LAMP?

Instale certbot e python3-certbot-apache, aponte o registro A de um domínio para o servidor e execute sudo certbot --apache. O autenticador do apache comprova o controle do domínio por meio do 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 do Certbot e Apache explica o desafio, o temporizador de renovação e os modos comuns de falha.

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