LAMP stack installeren op Ubuntu 24.04
Bouw een volledige stack met Apache, MariaDB, PHP 8.3 en PHP-FPM. Leer hoe u vhosts en HTTPS via Certbot configureert zonder fouten in de verbindingen.
Wat u gaat bouwen
Een LAMP-stack bestaat uit vier onderdelen op één Ubuntu 24.04-server: Linux als basis, Apache voor HTTP, MariaDB voor de gegevens, en PHP 8.3 voor de uitvoering van de code. Aan het einde van deze handleiding heeft u een name-based virtual host die een applicatiedirectory serveert, een database met een toegewezen gebruiker met minimale rechten, PHP gekoppeld aan Apache via PHP-FPM, en een gratis Let's Encrypt-certificaat.
De installatie bestaat uit vier apt commando's. Bijna alles in deze gids gaat over de verbindingen tussen de onderdelen. Ook behandelt het de fouten die ervoor zorgen dat een nieuwe stack een lege pagina toont, de broncode als download naar de browser stuurt, of de toegang tot de zojuist geïnstalleerde database weigert. Al deze fouten hebben een herkenbaar patroon en worden hieronder benoemd met de exacte tekst die u zult zien.
Vereisten en belangrijke aandachtspunten
Ga uit van een schone Ubuntu 24.04 KVM VPS met een sudo-gebruiker of root-toegang en een publiek IPv4-adres. Een minimale stack draait op 1 GB RAM; wijs 2 GB toe voordat u een applicatie met een echte database installeert. De standaard buffers van MariaDB en enkele PHP-FPM workers verbruiken namelijk snel het eerste gigabyte.
Twee zaken moeten correct zijn ingesteld voordat Certbot aan het einde werkt. Zorg dat u dit nu al regelt. U heeft een domeinnaam nodig met een A-record dat naar het publieke IP van de VPS wijst. Let's Encrypt valideert via HTTP op die naam; een enkel IP-adres kan nooit een certificaat verkrijgen. Daarnaast moeten poorten 80 en 443 bereikbaar zijn vanaf het internet. Bij veel providers betekent dit dat u deze poorten moet openen in de netwerkfirewall in het controlepaneel én in ufw op de server zelf. DNS-wijzigingen kunnen tot een uur duren om te verspreiden. Stel het A-record daarom eerst in; het is actief op het moment dat u het nodig heeft.
Stap 1 - Installeer Apache en bevestig de standaardpagina
sudo apt update
sudo apt install -y apache2apt start en activeert de service voor u. Controleer dit:
systemctl status apache2U moet een regel zien met active (running). Open nu http://YOUR_SERVER_IP/ in een browser. De Apache2 Ubuntu Default Page met de grote "It works!" banner is het juiste resultaat — dit is het bewijs dat Apache werkt en geen fout. Die pagina is te vinden op /var/www/html/index.html en wordt geleverd door de standaard virtual host 000-default.conf. U zult beide later uitschakelen; op dit moment is hun aanwezigheid precies wat u wilt zien.
Als de pagina niet laadt, maar systemctl aangeeft dat het proces draait, blokkeert een firewall de verbinding. Dat is de volgende stap.
Stap 2 - Open de firewall voor HTTP en HTTPS
Het apache2 pakket registreert drie ufw applicatieprofielen. Bekijk de lijst:
sudo ufw app listU ziet Apache, Apache Full en Apache Secure. Apache is alleen poort 80, Apache Secure is alleen poort 443, en Apache Full bevat beide — dit is het profiel dat u nodig heeft, omdat u later TLS toevoegt.
sudo ufw allow OpenSSH
sudo ufw allow "Apache Full"
sudo ufw enableSta OpenSSH toe voordat u ufw enable uitvoert. ufw staat standaard alle inkomende verkeer toe als geweigerd. Het inschakelen zonder een SSH-regel verbreekt uw eigen verbinding zodra de regel actief wordt — de huidige sessie blijft bestaan, maar u kunt niet opnieuw verbinden. Controleer dit met sudo ufw status; u wilt dat OpenSSH, Apache Full en hun v6-equivalenten allemaal op ALLOW staan.
Stap 3 - Installeer MariaDB en beveilig deze
sudo apt install -y mariadb-server
systemctl status mariadbUbuntu 24.04 bevat MariaDB 10.11, een long-term-support release. U heeft daarom geen extern repository nodig. Beveilig de service nadat deze is gestart:
sudo mysql_secure_installationHet is raadzaam om de prompts te lezen in plaats van direct op Enter te drukken. Wanneer de current root password wordt gevraagd, druk op Enter — er is nog geen wachtwoord ingesteld. Wanneer de vraag "Switch to unix_socket authentication?" wordt gesteld, verandert het antwoord niets omdat dit al is ingeschakeld voor dit pakket. Druk daarom op n. Kies n bij "Change the root password?" vanwege de reden in de volgende paragraaf. Beantwoord de rest met Y: verwijder anonieme gebruikers, blokkeer remote root login, verwijder de test database en herlaad de privilege tables.
Dit deel is vaak verwarrend. Bij de MariaDB op Ubuntu gebruikt het root databaseaccount unix_socket authenticatie in plaats van een wachtwoord. Dit betekent dat de database de operating-system gebruiker vertrouwt waarmee u al bent ingelogd. Dit werkt daarom vanuit een root shell:
sudo mysql...en u krijgt een MariaDB [(none)]> prompt zonder dat er om een wachtwoord wordt gevraagd. Hetzelfde commando uitvoeren als een gebruiker zonder privileges wordt geweigerd. Dit is de kern van de beveiliging: toegang tot de database root is gekoppeld aan sudo op het systeem. Er is geen wachtwoord om te stelen, te phishen of via brute-force te kraken. Dit is veiliger dan een wachtwoord, dus laat het ongewijzigd. De regel die hieruit volgt: wijs nooit een applicatie toe aan het root account. Maak voor elke applicatie een aparte gebruiker aan (Stap 7). Een applicatie die via TCP verbindt met een gebruikersnaam en wachtwoord kan namelijk geen socket auth gebruiken, en u wilt elke applicatie beperken tot de eigen database.
Stap 4 - PHP 8.3 installeren met PHP-FPM
De standaard PHP in Ubuntu 24.04 is versie 8.3. Installeer de FPM process manager en de extensies die een typische applicatie nodig heeft:
sudo apt install -y php8.3-fpm php8.3-mysql php8.3-cli \
php8.3-curl php8.3-xml php8.3-mbstring php8.3-zipLet op wat er niet in deze lijst staat: libapache2-mod-php. Dit oudere pakket plaatst een PHP-interpreter in elke Apache-proces. Dit is eenvoudig, maar elke worker draagt een kopie van PHP bij zich, ongeacht of er een script of een statische afbeelding wordt geserveerd. De twee delen een levenscyclus en het werkt alleen met de Apache prefork MPM — de minst efficiënte optie. PHP-FPM draait PHP in plaats daarvan als een eigen pool van processen waar Apache via een socket mee communiceert. Apache kan vervolgens de threaded event MPM gebruiken voor statische bestanden en alleen PHP-verzoeken doorsturen. De pool is onafhankelijk van de webserver afgestemd. Dezelfde FPM-configuratie werkt ook als u later nginx als reverse proxy gebruikt. Dit is om een goede reden de huidige standaard.
Apache bereikt FPM via de proxy_fcgi module. Schakel deze in, activeer de configuratie die door het FPM-pakket is geplaatst, en herstart:
sudo a2enmod proxy_fcgi setenvif
sudo a2enconf php8.3-fpm
sudo systemctl restart apache2a2enconf php8.3-fpm activeert /etc/apache2/conf-available/php8.3-fpm.conf, welke de regel bevat die PHP-bestanden naar de FPM-socket routeert. De kern hiervan matcht elk .php bestand en stuurt dit door naar de socket op /run/php/php8.3-fpm.sock:
<FilesMatch ".+\.ph(ar|p|tml)$">
SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost"
</FilesMatch>U hoeft dit bestand niet te bewerken; het is correct geconfigureerd. Het kennen van het socketpad is echter essentieel voor het diagnosticeren van fouten zoals "PHP downloads instead of running" en "Primary script unknown". Beide fouten ontstaan wanneer Apache en FPM het niet eens zijn over deze socket of het bijbehorende bestand.
Stap 5 - Een naamgebaseerde virtual host voor uw app
Naamgebaseerde virtual hosting maakt het mogelijk om met één IP-adres meerdere sites te serveren; Apache selecteert de site op basis van de Host: header in de aanvraag. Maak een directory aan voor de app, ver weg van de standaard /var/www/html:
sudo mkdir -p /var/www/testapp
sudo chown -R www-data:www-data /var/www/testapp
sudo chmod -R 755 /var/www/testappEigendom is belangrijk. Apache en PHP-FPM draaien op Ubuntu beide als de www-data gebruiker. Bestanden die de webserver moet kunnen lezen — en directory's waar een app naar moet kunnen schrijven, zoals een uploads folder — moeten in bezit zijn van www-data. Als u bestanden ook als uw eigen gebruikersaccount wilt bewerken, is een gebruikelijke methode om de bestanden zelf te bezitten en uw gebruiker toe te voegen aan de www-data groep; voor een standaard installatie is www-data:www-data de meest voorspelbare optie.
Maak de virtual host aan op /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>Stel ServerName in op uw echte domein. Options -Indexes voorkomt dat Apache de directory opsomt wanneer er geen index bestand aanwezig is — anders kunnen bezoekers uw broncode doorzoeken. AllowOverride All maakt een .htaccess bestand mogelijk, wat de meeste PHP-applicaties verwachten voor pretty URLs; verwijder dit naar None voor een kleine snelheidswinst als uw app dit niet nodig heeft. Schakel deze site in, schakel de standaard site uit, controleer de configuratie en herlaad:
sudo a2ensite testapp
sudo a2dissite 000-default
sudo apache2ctl configtest
sudo systemctl reload apache2apache2ctl configtest moet Syntax OK tonen. De a2dissite 000-default regel wordt vaak vergeten, wat de reden is dat de standaardpagina later blijft verschijnen — dit wordt behandeld in de sectie over fouten.
Stap 6 - Controleer of PHP werkt, en verwijder daarna het bewijs
Plaats een PHP-bestand met één regel in de app-root:
echo "<?php phpinfo();" | sudo tee /var/www/testapp/info.phpBezoek http://app.example.com/info.php. Een correct resultaat is de lange paars-grijze PHP Version 8.3.x tabel met de geladen modules, waarbij de Server API regel FPM/FastCGI aangeeft. Deze laatste regel bevestigt dat verzoeken via PHP-FPM verlopen en niet via mod_php.
Verwijder het bestand direct daarna:
sudo rm /var/www/testapp/info.phpphpinfo() toont uw exacte PHP-versie, alle geladen extensies, bestandspaden en omgevingsdetails. Dit is een risico voor iedereen die de server scant op versies met bekende kwetsbaarheden. Dit is een test, geen functie. Verwijder het bestand zodra u de pagina heeft gezien. Als uw browser in plaats van de tabel aanbiedt om info.php te downloaden, dan is PHP niet correct gekoppeld aan Apache. Ga naar de sectie met fouten voordat u verder gaat.
Stap 7 - Maak de app-database en een gebruiker met minimale rechten aan
Open de database als de root via de socket-authenticatie:
sudo mysqlMaak vervolgens één database en één gebruiker aan die uitsluitend toegang heeft tot die database:
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;Hier zijn drie bewuste keuzes gemaakt. utf8mb4 is echte four-byte UTF-8 — het oude alias utf8 kapt emoji's en bepaalde CJK-tekens stilletjes af, dus gebruik altijd utf8mb4. De rechten worden verleend op appdb.*, niet op *.*: deze gebruiker kan alleen de eigen database aanraken en niets anders. Een SQL-injectie in de app kan hierdoor niet de tabellen van andere sites uitlezen. En 'appuser'@'localhost' beperkt het account tot verbindingen die vanaf de machine zelf komen.
Test de toegang als deze gebruiker:
mysql -u appuser -p appdbDe opdracht vraagt om het wachtwoord en geeft u een MariaDB [appdb]> prompt. Let op dat de -h flag ontbreekt — laat deze weg om via de lokale Unix socket te verbinden. MariaDB beschouwt dit als localhost. Een belangrijk detail: voor MySQL en MariaDB betekent localhost de Unix socket en betekent 127.0.0.1 een TCP-verbinding. Op een standaard Ubuntu 24.04 MariaDB-installatie wordt een TCP-verbinding vanaf 127.0.0.1 nog steeds vertaald naar localhost, waardoor beide overeenkomen met het account. Echter, op servers waar skip-name-resolve is ingeschakeld (een veelgebruikte optimalisatie en de standaard in veel container-images), worden de twee als verschillende hosts beschouwd. Een app die via 127.0.0.1 verbindt, wordt dan geweigerd met ERROR 1045 (28000): Access denied for user 'appuser'@'127.0.0.1' (using password: YES), zelfs als het wachtwoord correct is.
Stel uw applicatie daarom in op host localhost, gebruiker appuser, database appdb — nooit op root. Zowel PHP's mysqli als PDO schakelen over naar de Unix socket wanneer de host de letterlijke tekst localhost is, wat overeenkomt met het account dat u zojuist heeft aangemaakt. Als een framework een numerieke TCP-host vereist, maak de gebruiker dan aan op basis van de werkelijke verbindingsmethode — 'appuser'@'127.0.0.1', of @'%' (in combinatie met een firewall-regel) alleen als de database vanaf een andere machine bereikbaar moet zijn.
Stap 8 - HTTPS toevoegen met Certbot
Het serveren van een inlogformulier via HTTP zonder versleuteling verstuurt wachtwoorden in clear text. Elke moderne browser markeert de pagina dan als "Niet veilig". Certbot lost dit op met één opdracht. Installeer de module met de Apache-plugin:
sudo apt install -y certbot python3-certbot-apache
sudo certbot --apacheCertbot gebruikt hier twee plugins. De apache authenticator bewijst dat u de eigenaar van het domein bent door tijdelijk een challenge-bestand via uw actieve Apache te serveren. De apache installer herschrijft vervolgens uw virtual host om de 443 block toe te voegen, verwijst naar het nieuwe certificaat en leidt standaard al het HTTP-verkeer om naar HTTPS. Sinds Certbot 2.0 is er geen vraag meer over de redirect; gebruik --no-redirect als u HTTP moet blijven serveren. Omdat u in Stap 5 een echte ServerName heeft ingesteld, detecteert Certbot het domein automatisch. Certificaten zijn 90 dagen geldig en het pakket installeert een systemd-timer voor automatische vernieuwing. Controleer de timer met sudo certbot renew --dry-run, wat moet eindigen op Congratulations, all simulated renewals succeeded.
Voor de volledige uitleg over de challenge, de renewal-timer en de DNS- en firewall-vereisten, raadpleeg de handleiding het uitgeven van gratis Let's Encrypt TLS-certificaten met Certbot op Apache.
Backups, upgrades, and hardening
Maak een back-up van de twee onderdelen die uw status opslaan: de databases en de web root. Een dagelijkse logische dump is de eenvoudigste betrouwbare methode — sudo sh -c 'mysqldump --all-databases --single-transaction | gzip > /root/db-$(date +%F).sql.gz', waarna deze naar een externe locatie wordt gekopieerd. Het gebruik van sudo sh -c voor de volledige pipeline is noodzakelijk: zonder dit onderdeel voert de shell de > /root/... redirect uit als uw eigen gebruiker en mislukt dit met Permission denied, omdat alleen mysqldump de sudo heeft overgenomen. --single-transaction maakt een consistente snapshot van de InnoDB-tabellen zonder deze te vergrendelen. Combineer dit met een tar van /var/www en /etc/apache2/sites-available; hiermee kunt u de volledige stack opnieuw opbouwen op een nieuwe VPS vanuit deze bestanden.
Upgrades zijn een standaard sudo apt update && sudo apt upgrade. De kritieke factor is een PHP-versieupdate — wanneer een toekomstige Ubuntu-versie de standaard naar PHP 8.4 wijzigt, installeert apt mogelijk php8.4-fpm naast 8.3, de socket wordt /run/php/php8.4-fpm.sock, en uw Apache-configuratie wijst nog steeds naar de 8.3-socket. Schakel de nieuwe configuratie (sudo a2enconf php8.4-fpm) in en schakel de oude uit, anders geeft uw website Primary script unknown na een routineuze upgrade. Omdat PHP-releases sneller gaan dan LTS-distributies, is het raadzaam om de huidige PHP-release notes te controleren in plaats van een specifieke patchversie vast te leggen.
Twee hardening-stappen zijn op de eerste dag aanbevolen. Ten eerste: installeer Fail2Ban voor SSH-monitoring op de server — een publieke VPS krijgt binnen enkele minuten te maken met geautomatiseerde inlogpogingen, en een kleine jail zet duizenden pogingen om in een handvol voordat er een ban volgt. Ten tweede: als u liever Apache virtual hosts, MariaDB-databases en gebruikers via een browser beheert in plaats van handmatig bestanden te bewerken, dan biedt het Webmin web-gebaseerde controlepaneel een interface voor exact deze stack en beheert het dezelfde configuratiebestanden die u zojuist heeft geschreven. Geen van beide vervangt de noodzaak om de onderdelen te begrijpen, maar beide verminderen de dagelijkse frictie.
Foutmodi, met de strings die u zult zien
De standaardpagina verdwijnt niet. U heeft uw virtual host bewerkt en herladen, maar de browser toont nog steeds "Apache2 Ubuntu Default Page" met de "It works!" banner. Apache serveert de eerste overeenkomende virtual host. Wanneer geen enkele ServerName overeenkomt met de aanvraag, wint de alfabetisch eerste configuratie — 000-default.conf staat voor testapp.conf. De hostnaam van de aanvraag komt niet overeen met uw ServerName, of u heeft sudo a2dissite 000-default nooit uitgevoerd. Schakel de standaardconfiguratie uit, sudo systemctl reload apache2, en controleer dit met apache2ctl -S. Dit commando print de vhost-map en laat zien welke configuratie de standaard beheert. Leeg ook de browsercache; een gecachte 200-status van de oude pagina blijft aanwezig.
Een .php-bestand wordt gedownload in plaats van uitgevoerd. U opent info.php en de browser downloadt een bestand met de ruwe <?php broncode, of toont dit als platte tekst, in plaats van het uit te voeren. Apache serveert het bestand als een statisch bestand omdat de PHP-handler niet is gekoppeld — u heeft sudo a2enmod proxy_fcgi of sudo a2enconf php8.3-fpm overgeslagen, of u heeft Apache daarna niet herstart. Voer alle drie de stappen uit (Stap 4) en herlaad de pagina. Controleer of de module is geladen met apache2ctl -M | grep fcgi; dit moet proxy_fcgi_module vermelden. Dit is een lek van broncode en geen cosmetische fout; los dit op voordat u echte data op de server plaatst.
ERROR 1698 (28000): Access denied for user 'root'@'localhost'. U heeft mysql -u root of mariadb -u root uitgevoerd zonder sudo. Het root-account gebruikt unix_socket-authenticatie en accepteert u alleen als uw OS-gebruiker daadwerkelijk root is. De oplossing is sudo mysql — geen -u root, geen wachtwoord. Deze melding is het verwachte gedrag van werkende socket-authenticatie en geen defecte installatie.
ERROR 1045 (28000): Access denied for user 'appuser'@'127.0.0.1' vanuit de applicatie, met het juiste wachtwoord. Het account bestaat als 'appuser'@'localhost', maar uw applicatie maakt verbinding via TCP met 127.0.0.1 op een server waar hostnaam-resolutie is uitgeschakeld (skip-name-resolve). MariaDB ziet deze als verschillende hosts — localhost is de Unix-socket, 127.0.0.1 is TCP. Verwijs de applicatie naar host localhost zodat deze de socket gebruikt en overeenkomt met het account, of maak een tweede account 'appuser'@'127.0.0.1' aan als het framework alleen TCP ondersteunt.
AH01071: Got error 'Primary script unknown' in /var/log/apache2/testapp-error.log, waarbij de browser File not found. toont. Apache heeft de aanvraag doorgegeven aan PHP-FPM, maar FPM kon het script niet vinden op het pad dat Apache doorgaf. Twee gebruikelijke oorzaken: de FPM-socket in uw configuratie verwijst naar een PHP-versie die niet is geïnstalleerd (een php8.4 socket na een upgrade terwijl alleen 8.3 draait), of het bestand staat niet op de locatie omdat DocumentRoot en de werkelijke directory niet overeenkomen. Controleer of de socket bestaat met ls -l /run/php/, bevestig dat DocumentRoot overeenkomt met de locatie van het bestand, en herstart zowel php8.3-fpm als apache2.
AH00558: apache2: Could not reliably determine the server's fully qualified domain name bij elke herstart. Dit is een onschuldige waarschuwing, geen fout — Apache geeft aan dat er geen globale ServerName is ingesteld. Schakel dit uit door ServerName your.domain naar /etc/apache2/conf-available/servername.conf te schrijven en sudo a2enconf servername uit te voeren.
(98)Address already in use: AH00072: make_sock: could not bind to address 0.0.0.0:80 wanneer Apache start. Een andere webserver gebruikt al poort 80 — vaak een achtergebleven nginx van een eerder experiment. Zoek deze met sudo ss -ltnp | grep :80, stop en schakel de andere service uit voordat u Apache start.
FAQ
mod_php of PHP-FPM - welke moet ik gebruiken?
Gebruik PHP-FPM. mod_php plaatst een interpreter in elk Apache-proces en dwingt het trage prefork MPM af. Hierdoor draagt Apache PHP-overhead, zelfs bij het serveren van een statische afbeelding. PHP-FPM draait PHP als een aparte, onafhankelijk te tunen pool waar Apache via een socket bij kan. Dit werkt met het snellere threaded event MPM en is later eenvoudig over te zetten naar nginx. Het is de moderne standaard; mod_php is alleen zinvol voor legacy-applicaties die afhankelijk zijn van in-process gedrag.
Waarom downloadt mijn browser het PHP-bestand in plaats van het uit te voeren?
Apache behandelt het .php bestand als een statische download omdat er geen PHP-handler aan is gekoppeld. Op Ubuntu 24.04 met FPM betekent dit dat u een van de volgende stappen bent vergeten: sudo a2enmod proxy_fcgi, sudo a2enconf php8.3-fpm, of het herstarten van Apache. Voer alle drie uit en herlaad de pagina. Controleer vervolgens met apache2ctl -M | grep fcgi of proxy_fcgi_module in de lijst staat. Zolang dit niet is hersteld, lekt de server broncode; behandel dit als een urgente kwestie.
Waarom wordt root-toegang in MariaDB geweigerd, zelfs met het juiste wachtwoord?
Omdat er geen wachtwoord is — de MariaDB van Ubuntu authenticeert het root account via unix_socket. Hierdoor is het account gekoppeld aan de root-gebruiker van het besturingssysteem. mysql -u root vanuit een normale shell geeft standaard ERROR 1698 (28000): Access denied for user 'root'@'localhost' terug. Gebruik in plaats daarvan sudo mysql en maak voor elke applicatie een aparte gebruiker aan met wachtwoordauthenticatie in plaats van root te hergebruiken.
Hoe voeg ik HTTPS toe aan mijn LAMP-site?
Installeer certbot en python3-certbot-apache, wijs het A-record van een domein naar de server en voer vervolgens sudo certbot --apache uit. De Apache-authenticator bewijst de domeincontrole via uw draaiende Apache en de installer schrijft de virtual host voor poort 443 om en stelt automatische verlenging in. De volledige Certbot en Apache handleiding behandelt de uitdaging, de verlengings-timer en de meest voorkomende fouten.