Ubuntu 24.04 पर LAMP stack कैसे install करें
Ubuntu 24.04 पर Apache, MariaDB, PHP 8.3 और PHP-FPM setup करने की guide। Certbot के साथ free HTTPS और name-based vhost setup करने का सही तरीका सीखें।
आप क्या बना रहे हैं
एक LAMP stack एक Ubuntu 24.04 server पर चार components है: Linux, HTTP के लिए Apache, डेटा के लिए MariaDB, और code चलाने के लिए PHP 8.3। अंत में, आपके पास एक name-based virtual host होगा जो application directory को serve करेगा, एक database होगा जिसमें dedicated least-privilege user होगा, PHP-FPM के माध्यम से Apache में connected PHP होगा, और एक free Let's Encrypt certificate होगा।
Installation में चार apt commands शामिल हैं। इस guide में लगभग सब कुछ components के बीच connection और वे छोटी गलतियाँ हैं जिनसे नया stack blank page show करता है, source code को download के रूप में browser को देता है, या database access करने से रोकता है। इन सभी गलतियों के विशिष्ट लक्षण (signatures) होते हैं, और नीचे प्रत्येक का नाम दिया गया है जैसा कि वे दिखाई देंगे।
Prerequisites और महत्वपूर्ण सावधानियां
मान लीजिए कि आपके पास एक नया Ubuntu 24.04 KVM VPS है, जिसमें sudo user या root access है, और एक public IPv4 address है। Minimal stack 1 GB RAM में चल सकता है; लेकिन किसी real database-backed application को चलाने से पहले इसे 2 GB RAM दें। MariaDB के default buffers और कुछ PHP-FPM workers मिलकर 1 GB RAM को जल्दी भर देते हैं।
Certbot के सही ढंग से काम करने के लिए ये दो शर्तें पूरी होनी चाहिए, इसलिए इन्हें अभी तैयार कर लें। आपको एक domain name की आवश्यकता होगी जिसका A record VPS के public IP पर point करता हो — Let's Encrypt उस name पर HTTP के माध्यम से validation करता है, और केवल IP address पर certificate प्राप्त नहीं किया जा सकता। इसके अलावा, internet से ports 80 और 443 तक पहुंचना (reachable) अनिवार्य है। कई providers के मामले में, इसका मतलब है कि आपको इन्हें control panel के network firewall में साथ ही ufw में भी open करना होगा। DNS changes को propagate होने में एक घंटा लग सकता है, इसलिए A record पहले ही सेट कर दें ताकि जरूरत पड़ने तक यह live हो जाए।
Step 1 - Apache install karein aur default page confirm karein
sudo apt update
sudo apt install -y apache2apt aapke liye service start aur enable kar deta hai. Isse check karein:
systemctl status apache2Aapko active (running) wali line dikhni chahiye. Ab browser mein http://YOUR_SERVER_IP/ kholein. "It works!" banner ke saath Apache2 Ubuntu Default Page dikhna sahi hai — yeh is baat ka saboot hai ki Apache kaam kar raha hai, yeh koi error nahi hai. Woh page /var/www/html/index.html par hai aur default virtual host 000-default.conf dwara serve kiya ja raha hai. Aap baad mein dono ko disable kar denge; abhi ke liye inka hona zaroori hai.
Agar page load nahi ho raha hai lekin systemctl dikhata hai ki process chal raha hai, toh firewall rukawat ban raha hai. Agla step wahi hai.
Step 2 - HTTP और HTTPS के लिए firewall खोलें
apache2 package तीन ufw application profiles रजिस्टर करता है। उनकी सूची यहाँ है:
sudo ufw app listआपको Apache, Apache Full, और Apache Secure दिखाई देंगे। Apache केवल port 80 है, Apache Secure केवल 443 है, और Apache Full दोनों है — आपको इसी की आवश्यकता है, क्योंकि आप अंत में TLS जोड़ रहे हैं।
sudo ufw allow OpenSSH
sudo ufw allow "Apache Full"
sudo ufw enableufw enable चलाने से पहले OpenSSH को allow करें। ufw डिफ़ॉल्ट रूप से सभी incoming traffic को deny करता है। यदि आप SSH rule के बिना इसे enable करते हैं, तो activate होते ही आपका connection कट जाएगा — आपकी current session तो बनी रहेगी लेकिन आप दोबारा reconnect नहीं कर पाएंगे। sudo ufw status से पुष्टि करें; आपको OpenSSH, Apache Full, और उनके v6 equivalents के लिए ALLOW पढ़ना चाहिए।
Step 3 - MariaDB install karein aur use secure karein
sudo apt install -y mariadb-server
systemctl status mariadbUbuntu 24.04 mein MariaDB 10.11 (long-term-support release) pehle se aata hai, isliye aapko kisi external repository ki zaroorat nahi hai. Service chalne ke baad, ise secure karein:
sudo mysql_secure_installationEnter button dabane se pehle prompts ko padhna zaroori hai. Jab current root password pucha jaye, to Enter dabayein — abhi koi password nahi hai. Jab "Switch to unix_socket authentication?" pucha jaye, to iska jawab badalne se koi farq nahi padega kyunki is package mein yeh pehle se enabled hai, isliye n dabayein. Agle paragraph mein diye gaye karan ki wajah se "Change the root password?" ke liye n kahein, phir baaki sawalon ke liye Y ka jawab dein: anonymous users ko hatayein, remote root login ko disallow karein, test database ko drop karein, aur privilege tables ko reload karein.
Yeh hissa sabko confuse karta hai. Ubuntu ke MariaDB mein root database account unix_socket authentication ka upyog karta hai, password ka nahi. Iska matlab hai ki database us operating-system user par bharosa karta hai jiske roop mein aapne pehle hi authenticate kar liya hai. Isliye yeh root shell se kaam karta hai:
sudo mysql...aur yeh bina password maange aapko MariaDB [(none)]> prompt par le jata hai. Agar vahi command kisi unprivileged user se chalayi jaye to woh reject ho jayegi, jo ki iska mukhya uddeshya hai: database root ka access machine par sudo se juda hai, aur ise churaane, phish karne, ya brute-force karne ke liye koi password nahi hai. Yeh password se zyada secure hai, isliye ise na badlein. Isse nikalne wala niyam yeh hai: kisi bhi application ko root account se kabhi connect na karein. Har application ke liye ek dedicated user banayein (Step 7), kyunki TCP ke madhyam se username aur password ke saath connect hone wali app socket auth ka upyog nahi kar sakti, aur aap chahte hain ki har app sirf apne database tak hi seemit rahe.
Step 4 - PHP 8.3 और PHP-FPM इंस्टॉल करें
Ubuntu 24.04 का default PHP 8.3 है। FPM process manager और एक typical app के लिए आवश्यक extensions इंस्टॉल करें:
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इस list में जो नहीं है, उस पर ध्यान दें: libapache2-mod-php। यह पुराना package हर Apache process के अंदर एक PHP interpreter को embed करता है। यह सरल है, लेकिन हर worker एक PHP copy साथ रखता है, चाहे वह script serve कर रहा हो या static image। दोनों का lifecycle एक ही होता है, और यह केवल Apache के prefork MPM के साथ काम करता है — जो सबसे कम efficient है। इसके बजाय, PHP-FPM PHP को अपने स्वयं के process pool के रूप में चलाता है जिससे Apache एक socket के माध्यम से बात करता है। Apache फिर static files के लिए threaded event MPM का उपयोग कर सकता है और केवल PHP requests को आगे भेज सकता है। यह pool web server से स्वतंत्र रूप से tuned होता है, और यदि आप आगे nginx का उपयोग करते हैं, तो वही FPM setup काम करेगा। अच्छे कारणों से ही यह वर्तमान default है।
Apache, proxy_fcgi module के माध्यम से FPM से जुड़ता है। इसे enable करें, FPM package द्वारा दी गई config को enable करें, और restart करें:
sudo a2enmod proxy_fcgi setenvif
sudo a2enconf php8.3-fpm
sudo systemctl restart apache2a2enconf php8.3-fpm, /etc/apache2/conf-available/php8.3-fpm.conf को activate करता है, जिसमें वह rule होता है जो PHP files को FPM socket पर route करता है। इसका मुख्य हिस्सा किसी भी .php file को match करता है और उसे /run/php/php8.3-fpm.sock पर स्थित socket पर forward करता है:
<FilesMatch ".+\.ph(ar|p|tml)$">
SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost"
</FilesMatch>आपको उस file को edit करने की आवश्यकता नहीं है; वह सही तरीके से आती है। लेकिन socket path को जानना ही आपको बाद में होने वाली "PHP downloads instead of running" और "Primary script unknown" जैसी failures को diagnose करने में मदद करेगा — ये दोनों ही Apache और FPM के बीच इस socket या इसके पीछे की file को लेकर असहमति के कारण होते हैं।
Step 5 - आपके app के लिए name-based virtual host
Name-based virtual hosting एक ही IP पर कई sites चलाने की अनुमति देता है; Apache request में Host: header के आधार पर site का चयन करता है। App के लिए एक directory बनाएँ, जिसे default /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/testappOwnership महत्वपूर्ण है। Ubuntu पर Apache और PHP-FPM दोनों www-data user के रूप में चलते हैं, इसलिए जिन files को web server को पढ़ना है — और जिन directories में app को write करना है, जैसे कि uploads folder — उनका owner www-data होना चाहिए। यदि आप अपने login user के रूप में files को edit करना चाहते हैं, तो एक सामान्य तरीका यह है कि files के owner आप स्वयं हों और अपने user को www-data group में जोड़ दें; एक साधारण deploy के लिए, www-data:www-data सबसे सही विकल्प है।
/etc/apache2/sites-available/testapp.conf पर virtual host बनाएँ:
<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>ServerName को अपने real domain पर सेट करें। Options -Indexes Apache को index file न होने पर directory list करने से रोकता है — अन्यथा visitors आपके source tree को browse कर सकते हैं। AllowOverride All एक .htaccess file को काम करने देता है, जिसकी अधिकांश PHP applications को pretty URLs के लिए आवश्यकता होती है; यदि आपके app को इसकी आवश्यकता नहीं है, तो गति (speed) बढ़ाने के लिए इसे None में रखें। इस site को enable करें, default site को disable करें, config चेक करें, और reload करें:
sudo a2ensite testapp
sudo a2dissite 000-default
sudo apache2ctl configtest
sudo systemctl reload apache2apache2ctl configtest को Syntax OK print करना चाहिए। a2dissite 000-default line को लोग अक्सर भूल जाते हैं, और इसी कारण default page बाद में stuck दिखाई देता है — इसका विवरण failures section में दिया गया है।
Step 6 - PHP के चलने की पुष्टि करें, फिर प्रमाण को हटा दें
App root में एक line वाली PHP file डालें:
echo "<?php phpinfo();" | sudo tee /var/www/testapp/info.phphttp://app.example.com/info.php पर जाएँ। सही परिणाम के रूप में आपको एक लंबी purple-and-grey PHP Version 8.3.x table दिखेगी जिसमें आपके loaded modules की सूची होगी, और Server API line पर FPM/FastCGI लिखा होगा। वह आखिरी line पुष्टि करती है कि requests PHP-FPM के माध्यम से जा रहे हैं, mod_php के माध्यम से नहीं।
अब इसे तुरंत हटा दें:
sudo rm /var/www/testapp/info.phpphpinfo() आपकी exact PHP version, सभी loaded extensions, file paths, और environment details को उजागर करता है — यह उन लोगों के लिए एक अवसर है जो server पर ज्ञात vulnerabilities (holes) वाले version की तलाश में हैं। यह केवल एक test है, feature नहीं। Page देखने के तुरंत बाद इसे हटा दें। यदि table के बजाय आपका browser info.php को download करने का विकल्प देता है, तो PHP, Apache के साथ connected नहीं है; कुछ भी करने से पहले failures section पर जाएँ।
Step 7 - Create the app database and a least-privilege user
Open the database as the socket-authenticated root:
sudo mysqlThen create one database and one user scoped to exactly that 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;Three deliberate choices here. utf8mb4 is real four-byte UTF-8 — the old utf8 alias silently truncates emoji and some CJK characters, so always use utf8mb4. The grant is on appdb.*, not *.*: this user can touch its own database and nothing else, so a SQL-injection hole in the app cannot read every other site's tables. And 'appuser'@'localhost' restricts the account to connections originating on the box itself.
Test it as that user:
mysql -u appuser -p appdbIt asks for the password and drops you at a MariaDB [appdb]> prompt. Notice there is no -h flag — leave it off and the client connects over the local Unix socket, which is exactly what MariaDB counts as localhost. One gotcha worth knowing: to MySQL and MariaDB, localhost means the Unix socket and 127.0.0.1 means a TCP connection. On a stock Ubuntu 24.04 MariaDB the server still resolves a TCP connection from 127.0.0.1 back to localhost, so both match the account — but on servers with skip-name-resolve enabled (a common performance tweak, and the norm in many container images), the two are matched as different hosts, and an app that dials 127.0.0.1 is refused with ERROR 1045 (28000): Access denied for user 'appuser'@'127.0.0.1' (using password: YES) even when the password is correct.
So point your application at host localhost, user appuser, database appdb — never at root. PHP's mysqli and PDO both switch to the Unix socket when the host is the literal string localhost, matching the account you just created. If a framework insists on a numeric TCP host, create the user to match how it actually connects — 'appuser'@'127.0.0.1', or @'%' (paired with a firewall rule) only if it must reach the database from another machine.
Step 8 - Add HTTPS with Certbot
Serving a login form over plain HTTP sends passwords in clear text, and every modern browser flags the page as "Not secure". Certbot fixes that in one command. Install it with the Apache plugin:
sudo apt install -y certbot python3-certbot-apache
sudo certbot --apacheCertbot uses two plugins here. The apache authenticator proves you control the domain by briefly serving a challenge file through your running Apache, and the apache installer then rewrites your virtual host to add the 443 block, points it at the new certificate, and redirects all HTTP traffic to HTTPS by default — since Certbot 2.0 there is no redirect question; pass --no-redirect if you need to keep serving plain HTTP. Because you set a real ServerName in Step 5, Certbot detects the domain automatically. Certificates last 90 days and the package installs a systemd timer that renews them; verify the timer with sudo certbot renew --dry-run, which should end in Congratulations, all simulated renewals succeeded.
For the full walk-through of the challenge, the renewal timer, and the DNS and firewall requirements, see the companion guide on issuing free Let's Encrypt TLS certificates with Certbot on Apache.
Backups, upgrades, and hardening
Apne state ko sambhalne wali do cheezon ka backup lein: databases aur web root. Raat mein ek baar logical dump lena sabse saral aur bharosemand tarika hai — sudo sh -c 'mysqldump --all-databases --single-transaction | gzip > /root/db-$(date +%F).sql.gz', jise baad mein box se bahar copy kar liya jata hai. Poore pipeline ko sudo sh -c ke andar wrap karna zaroori hai: iske bina shell > /root/... redirect ko aapke user ke roop mein chalayega aur Permission denied ke saath fail ho jayega, kyunki sirf mysqldump ne sudo ko inherit kiya hai. --single-transaction InnoDB tables ko lock kiye bina ek consistent snapshot deta hai. Iske saath /var/www aur /etc/apache2/sites-available ka tar rakhein, taaki aap un files se ek naye VPS par poora stack rebuild kar sakein.
Upgrades ek aam sudo apt update && sudo apt upgrade hain. Sabse badi samasya PHP version bump ki aati hai — jab koi future Ubuntu default ko PHP 8.4 par move kar deta hai, toh apt 8.3 ke saath php8.4-fpm install kar sakta hai, socket /run/php/php8.4-fpm.sock ban jata hai, aur aapka Apache config abhi bhi 8.3 socket ko point karta hai. Nayi conf (sudo a2enconf php8.4-fpm) ko enable karein aur purani ko disable karein, varna ek routine upgrade ke baad aapki site Primary script unknown dikhane lagegi. Kyunki PHP releases LTS distro se zyada tezi se badalte hain, isliye kisi patch version ko pin karne ke bajaye current PHP release notes check karein.
Pehle din do hardening steps karna zaroori hai. Pehla, box par Fail2Ban SSH monitoring install karein — ek public VPS par minutes mein hi automated login attempts aane lagte hain, aur ek chota jail hazaron koshishon ko ban hone se pehle kuch hi attempts tak simit kar deta hai. Doosra, agar aap Apache virtual hosts, MariaDB databases, aur users ko files edit karne ke bajaye browser se manage karna chahte hain, toh Webmin web-based control panel isi stack par kaam karta hai aur wahi config files chalata hai jo aapne likhi hain. Dono mein se koi bhi components ki samajh ko replace nahi karta, lekin dono rozana ke kaam ko aasaan banate hain.
Failure modes, with the strings you will see
The default page will not go away. You edited your virtual host, reloaded, and the browser still shows "Apache2 Ubuntu Default Page" and its "It works!" banner. Apache serves the first matching virtual host, and when no ServerName matches the request, the alphabetically first config wins — 000-default.conf sorts before testapp.conf. Either the request's host name does not match your ServerName, or you never ran sudo a2dissite 000-default. Disable the default, sudo systemctl reload apache2, and confirm with apache2ctl -S, which prints the vhost map and shows which config owns the default. Clear the browser cache too; a cached 200 from the old page happily persists.
A .php file downloads instead of running. You open info.php and the browser downloads a file containing the raw <?php source, or shows it as plain text, instead of running it. Apache is serving the file as a static asset because the PHP handler is not attached — you skipped sudo a2enmod proxy_fcgi, or sudo a2enconf php8.3-fpm, or did not restart Apache afterwards. Run all three (Step 4) and reload. Confirm the module is loaded with apache2ctl -M | grep fcgi, which should list proxy_fcgi_module. This is a source-code leak, not a cosmetic bug, so fix it before putting anything real on the server.
ERROR 1698 (28000): Access denied for user 'root'@'localhost'. You ran mysql -u root or mariadb -u root without sudo. The root account uses unix_socket auth, so it only accepts you when your OS user is actually root. The fix is sudo mysql — no -u root, no password. This message is the expected behaviour of socket auth working correctly, not a broken install.
ERROR 1045 (28000): Access denied for user 'appuser'@'127.0.0.1' from the application, with the right password. The account exists as 'appuser'@'localhost', but your app is connecting over TCP to 127.0.0.1 on a server where host-name resolution is disabled (skip-name-resolve), so MariaDB matches the two as different hosts — localhost is the Unix socket, 127.0.0.1 is TCP. Point the app at host localhost so it uses the socket and matches the account, or create a second account 'appuser'@'127.0.0.1' if the framework will only speak TCP.
AH01071: Got error 'Primary script unknown' in /var/log/apache2/testapp-error.log, with the browser showing File not found.. Apache handed the request to PHP-FPM, but FPM could not find the script at the path Apache gave it. Two usual causes: the FPM socket in your config points at a PHP version that is not installed (a php8.4 socket after an upgrade while only 8.3 runs), or the file genuinely is not there because DocumentRoot and the real directory disagree. Check the socket exists with ls -l /run/php/, confirm DocumentRoot matches where the file lives, and restart both php8.3-fpm and apache2.
AH00558: apache2: Could not reliably determine the server's fully qualified domain name on every restart. This is a harmless warning, not an error — Apache is telling you no global ServerName is set. Silence it by writing ServerName your.domain into /etc/apache2/conf-available/servername.conf and running sudo a2enconf servername.
(98)Address already in use: AH00072: make_sock: could not bind to address 0.0.0.0:80 when Apache starts. Another web server already holds port 80 — often a stray nginx from a previous experiment. Find it with sudo ss -ltnp | grep :80, then stop and disable the other service before starting Apache.
FAQ
mod_php या PHP-FPM - मुझे किसका उपयोग करना चाहिए?
PHP-FPM का उपयोग करें। mod_php प्रत्येक Apache process में एक interpreter को जोड़ देता है और slow prefork MPM का उपयोग करने के लिए मजबूर करता है, जिससे Apache पर PHP का overhead बढ़ जाता है, भले ही वह केवल एक static image serve कर रहा हो। PHP-FPM, PHP को एक अलग, independently tuned pool के रूप में चलाता है जिसे Apache एक socket के माध्यम से एक्सेस करता है। यह faster threaded event MPM के साथ काम करता है, और बाद में nginx पर भी बिना किसी बदलाव के काम करता है। यह आधुनिक default है; mod_php केवल उन्हीं legacy apps के लिए उपयोगी है जिन्हें in-process behaviour की आवश्यकता होती है।
मेरा browser PHP file को चलाने के बजाय download क्यों कर रहा है?
Apache .php file को static download की तरह treat कर रहा है क्योंकि इसके साथ कोई PHP handler attached नहीं है। Ubuntu 24.04 पर FPM के साथ, इसका मतलब है कि आपने sudo a2enmod proxy_fcgi, sudo a2enconf php8.3-fpm, या उसके बाद Apache restart करना छोड़ दिया है। तीनों को run करें और reload करें, फिर apache2ctl -M | grep fcgi से verify करें कि proxy_fcgi_module list में है या नहीं। जब तक आप इसे ठीक नहीं करते, server आपका source code leak कर रहा है, इसलिए इसे urgent समझें।
सही password होने के बावजूद MariaDB में root access denied क्यों आ रहा है?
क्योंकि कोई password नहीं है — Ubuntu का MariaDB root account को unix_socket द्वारा authenticate करता है, जो इसे operating-system के root user से जोड़ता है। एक normal shell से mysql -u root चलाने पर design के अनुसार ERROR 1698 (28000): Access denied for user 'root'@'localhost' मिलता है। इसके बजाय sudo mysql से connect करें, और root का पुन: उपयोग करने के बजाय किसी भी application के लिए एक अलग password-authenticated user बनाएँ।
मैं अपनी LAMP site में HTTPS कैसे जोड़ूँ?
certbot और python3-certbot-apache install करें, domain के A record को server पर point करें, और फिर sudo certbot --apache run करें। Apache authenticator आपके running Apache के माध्यम से domain control को prove करता है, और installer port 443 के लिए virtual host को rewrite करता है और automatic renewal setup करता है। full Certbot and Apache walk-through में इस प्रक्रिया, renewal timer, और common failure modes के बारे में विस्तार से बताया गया है।