How to Deploy PHP Website on Ubuntu VPS: Complete LAMP Stack Guide
A comprehensive guide to deploying your PHP website on an Ubuntu VPS using Apache, MySQL, and PHP (LAMP Stack).
## Prerequisites
Before you begin, ensure you have:
- A VPS running **Ubuntu 20.04/22.04/24.04**
- A domain name pointing to your VPS IP
- SSH access to your server
- Root or sudo privileges
---
## Initial Server Setup
### Step 1: Connect to Your VPS
```bash
ssh root@your_server_ip
```
### Step 2: Create a Non-Root User
```bash
# Create new user
adduser webadmin
# Grant sudo privileges
usermod -aG sudo webadmin
# Switch to new user
su - webadmin
```
### Step 3: Update System Packages
```bash
sudo apt update && sudo apt upgrade -y
```
---
## Installing Apache Web Server
### Step 1: Install Apache
```bash
sudo apt install apache2 -y
```
### Step 2: Start and Enable Apache
```bash
# Start Apache
sudo systemctl start apache2
# Enable on boot
sudo systemctl enable apache2
# Check status
sudo systemctl status apache2
```
### Step 3: Verify Installation
Open your browser and navigate to:
```
http://your_server_ip
```
You should see the Apache2 Ubuntu Default Page.
### Step 4: Configure Firewall
```bash
# Enable UFW
sudo ufw enable
# Allow SSH first!
sudo ufw allow OpenSSH
# Allow Apache
sudo ufw allow 'Apache Full'
# Check status
sudo ufw status
```
---
## Installing MySQL Database
### Step 1: Install MySQL Server
```bash
sudo apt install mysql-server -y
```
### Step 2: Secure MySQL Installation
```bash
sudo mysql_secure_installation
```
Follow the prompts:
- Set up VALIDATE PASSWORD plugin (recommended: Yes)
- Set root password
- Remove anonymous users (Yes)
- Disallow root login remotely (Yes)
- Remove test database (Yes)
- Reload privilege tables (Yes)
### Step 3: Create Database and User
```bash
# Access MySQL
sudo mysql
# Create database
CREATE DATABASE mywebsite;
# Create user with password
CREATE USER 'webuser'@'localhost' IDENTIFIED BY 'StrongPassword123!';
# Grant privileges
GRANT ALL PRIVILEGES ON mywebsite.* TO 'webuser'@'localhost';
# Apply changes
FLUSH PRIVILEGES;
# Exit
EXIT;
```
### Step 4: Test MySQL Connection
```bash
mysql -u webuser -p mywebsite
```
---
## Installing PHP
### Step 1: Install PHP and Common Extensions
```bash
sudo apt install php libapache2-mod-php php-mysql -y
```
### Step 2: Install Additional PHP Extensions
```bash
sudo apt install php-curl php-gd php-mbstring php-xml php-zip php-intl php-bcmath php-json -y
```
### Step 3: Verify PHP Installation
```bash
php -v
```
### Step 4: Test PHP Processing
Create a test file:
```bash
sudo nano /var/www/html/info.php
```
Add this content:
```php
<?php
phpinfo();
?>
```
Visit `http://your_server_ip/info.php` to verify PHP is working.
> ⚠️ **Important**: Delete this file after testing:
> ```bash
> sudo rm /var/www/html/info.php
> ```
### Step 5: Configure Apache to Prefer PHP Files
```bash
sudo nano /etc/apache2/mods-enabled/dir.conf
```
Move `index.php` to the front:
```apache
<IfModule mod_dir.c>
DirectoryIndex index.php index.html index.cgi index.pl index.xhtml index.htm
</IfModule>
```
Restart Apache:
```bash
sudo systemctl restart apache2
```
---
## Configuring Virtual Hosts
### Step 1: Create Directory Structure
```bash
# Create web directory
sudo mkdir -p /var/www/yourdomain.com/public_html
# Set ownership
sudo chown -R $USER:$USER /var/www/yourdomain.com
# Set permissions
sudo chmod -R 755 /var/www/yourdomain.com
```
### Step 2: Create Virtual Host Configuration
```bash
sudo nano /etc/apache2/sites-available/yourdomain.com.conf
```
**Virtual Host Configuration:**
```apache
<VirtualHost *:80>
ServerAdmin [email protected]
ServerName yourdomain.com
ServerAlias www.yourdomain.com
DocumentRoot /var/www/yourdomain.com/public_html
<Directory /var/www/yourdomain.com/public_html>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
# Logging
ErrorLog ${APACHE_LOG_DIR}/yourdomain.com_error.log
CustomLog ${APACHE_LOG_DIR}/yourdomain.com_access.log combined
</VirtualHost>
```
### Step 3: Enable the Site
```bash
# Enable virtual host
sudo a2ensite yourdomain.com.conf
# Enable mod_rewrite (for .htaccess)
sudo a2enmod rewrite
# Disable default site (optional)
sudo a2dissite 000-default.conf
# Test configuration
sudo apache2ctl configtest
# Restart Apache
sudo systemctl restart apache2
```
---
## Deploying Your PHP Application
### Option 1: Upload via SFTP
Use an SFTP client like FileZilla:
- Host: your_server_ip
- Username: webadmin
- Port: 22
- Upload files to `/var/www/yourdomain.com/public_html/`
### Option 2: Clone from Git Repository
```bash
cd /var/www/yourdomain.com/public_html
# Clone your repository
git clone https://github.com/yourusername/your-php-app.git .
```
### Option 3: Upload via SCP
From your local machine:
```bash
scp -r /path/to/local/files/* webadmin@your_server_ip:/var/www/yourdomain.com/public_html/
```
### Set Proper Permissions
```bash
# Set ownership
sudo chown -R www-data:www-data /var/www/yourdomain.com/public_html
# Set directory permissions
sudo find /var/www/yourdomain.com/public_html -type d -exec chmod 755 {} \;
# Set file permissions
sudo find /var/www/yourdomain.com/public_html -type f -exec chmod 644 {} \;
# Writable directories (uploads, cache, etc.)
sudo chmod -R 775 /var/www/yourdomain.com/public_html/uploads
sudo chmod -R 775 /var/www/yourdomain.com/public_html/cache
```
### Configure Database Connection
Create or edit your config file:
```php
<?php
// config.php
define('DB_HOST', 'localhost');
define('DB_NAME', 'mywebsite');
define('DB_USER', 'webuser');
define('DB_PASS', 'StrongPassword123!');
try {
$pdo = new PDO(
"mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=utf8mb4",
DB_USER,
DB_PASS,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false
]
);
} catch (PDOException $e) {
die("Connection failed: " . $e->getMessage());
}
?>
```
### Import Database (if needed)
```bash
mysql -u webuser -p mywebsite < /path/to/database.sql
```
---
## SSL Certificate Setup
### Step 1: Install Certbot
```bash
sudo apt install certbot python3-certbot-apache -y
```
### Step 2: Obtain SSL Certificate
```bash
sudo certbot --apache -d yourdomain.com -d www.yourdomain.com
```
Follow the prompts:
- Enter email address
- Agree to Terms of Service
- Choose to redirect HTTP to HTTPS (recommended)
### Step 3: Verify Auto-Renewal
```bash
# Test renewal
sudo certbot renew --dry-run
# Check timer status
sudo systemctl status certbot.timer
```
### Step 4: Verify HTTPS
Visit `https://yourdomain.com` to confirm SSL is working.
---
## PHP Configuration Optimization
### Edit PHP Configuration
```bash
sudo nano /etc/php/8.1/apache2/php.ini
```
> Replace `8.1` with your PHP version (check with `php -v`)
### Recommended Settings
```ini
; Maximum upload file size
upload_max_filesize = 64M
post_max_size = 64M
; Maximum execution time
max_execution_time = 300
; Memory limit
memory_limit = 256M
; Error handling (production)
display_errors = Off
log_errors = On
error_log = /var/log/php_errors.log
; Timezone
date.timezone = UTC
; Session security
session.cookie_httponly = 1
session.cookie_secure = 1
session.use_strict_mode = 1
```
### Restart Apache
```bash
sudo systemctl restart apache2
```
---
## Security Hardening
### 1. Hide Apache Version
```bash
sudo nano /etc/apache2/conf-available/security.conf
```
Set:
```apache
ServerTokens Prod
ServerSignature Off
```
### 2. Hide PHP Version
In `php.ini`:
```ini
expose_php = Off
```
### 3. Disable Directory Listing
Already done in virtual host with `Options -Indexes`
### 4. Create .htaccess Security Rules
```bash
nano /var/www/yourdomain.com/public_html/.htaccess
```
```apache
# Prevent access to sensitive files
<FilesMatch "^\.">
Order allow,deny
Deny from all
</FilesMatch>
# Block access to config files
<FilesMatch "(config\.php|\.env|composer\.json|composer\.lock)$">
Order allow,deny
Deny from all
</FilesMatch>
# Prevent script execution in uploads
<Directory "/var/www/yourdomain.com/public_html/uploads">
<FilesMatch "\.(php|phtml|php3|php4|php5|php7|phps|pl|py|jsp|asp|cgi|sh)$">
Order allow,deny
Deny from all
</FilesMatch>
</Directory>
# Security headers
<IfModule mod_headers.c>
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
</IfModule>
```
### 5. Secure MySQL
```bash
# Ensure MySQL only listens locally
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
```
Verify this line exists:
```
bind-address = 127.0.0.1
```
### 6. Enable Fail2Ban
```bash
# Install Fail2Ban
sudo apt install fail2ban -y
# Enable and start
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
```
---
## Optional: Install phpMyAdmin
### Step 1: Install phpMyAdmin
```bash
sudo apt install phpmyadmin -y
```
During installation:
- Select `apache2` as the web server
- Choose `Yes` to configure database with dbconfig-common
- Set a password for phpMyAdmin
### Step 2: Enable PHP mcrypt (if needed)
```bash
sudo phpenmod mcrypt
sudo systemctl restart apache2
```
### Step 3: Secure phpMyAdmin
```bash
sudo nano /etc/apache2/conf-available/phpmyadmin.conf
```
Add access restriction:
```apache
<Directory /usr/share/phpmyadmin>
# Restrict by IP
Order Deny,Allow
Deny from all
Allow from your_local_ip
# Or use password protection
AuthType Basic
AuthName "Restricted Access"
AuthUserFile /etc/phpmyadmin/.htpasswd
Require valid-user
</Directory>
```
Create password file:
```bash
sudo htpasswd -c /etc/phpmyadmin/.htpasswd admin
sudo systemctl restart apache2
```
---
## Troubleshooting
### Common Issues and Solutions
#### 500 Internal Server Error
```bash
# Check Apache error logs
sudo tail -f /var/log/apache2/error.log
# Check PHP error logs
sudo tail -f /var/log/php_errors.log
# Check file permissions
ls -la /var/www/yourdomain.com/public_html/
```
#### Permission Denied Errors
```bash
# Fix ownership
sudo chown -R www-data:www-data /var/www/yourdomain.com/public_html
# Fix permissions
sudo find /var/www/yourdomain.com -type d -exec chmod 755 {} \;
sudo find /var/www/yourdomain.com -type f -exec chmod 644 {} \;
```
#### MySQL Connection Refused
```bash
# Check MySQL is running
sudo systemctl status mysql
# Restart MySQL
sudo systemctl restart mysql
# Check if listening
sudo ss -tulpn | grep mysql
```
#### .htaccess Not Working
```bash
# Ensure mod_rewrite is enabled
sudo a2enmod rewrite
# Check AllowOverride is set to All
sudo nano /etc/apache2/sites-available/yourdomain.com.conf
# Restart Apache
sudo systemctl restart apache2
```
### Useful Commands
```bash
# View Apache status
sudo systemctl status apache2
# View Apache error log
sudo tail -f /var/log/apache2/error.log
# View access log
sudo tail -f /var/log/apache2/access.log
# Test Apache configuration
sudo apache2ctl configtest
# Check PHP modules
php -m
# Check disk space
df -h
# Check memory usage
free -m
```
---
## Deployment Workflow
For future updates:
```bash
# SSH into server
ssh webadmin@your_server_ip
# Navigate to web directory
cd /var/www/yourdomain.com/public_html
# Pull latest changes
git pull origin main
# Clear cache (if applicable)
rm -rf cache/*
# Set permissions
sudo chown -R www-data:www-data .
# Restart Apache (if needed)
sudo systemctl restart apache2
```
---
## Quick Reference
| Action | Command |
|--------|---------|
| Start Apache | `sudo systemctl start apache2` |
| Stop Apache | `sudo systemctl stop apache2` |
| Restart Apache | `sudo systemctl restart apache2` |
| Reload Apache | `sudo systemctl reload apache2` |
| Start MySQL | `sudo systemctl start mysql` |
| Restart MySQL | `sudo systemctl restart mysql` |
| Apache Error Log | `sudo tail -f /var/log/apache2/error.log` |
| Test Config | `sudo apache2ctl configtest` |
| Renew SSL | `sudo certbot renew` |
---
## Conclusion
You now have a fully functional PHP website running on your Ubuntu VPS with:
- **Apache** as the web server
- **MySQL** as the database
- **PHP** for server-side processing
- **SSL/HTTPS** via Let's Encrypt
- **Security hardening** for production
Your LAMP stack is now production-ready!
---
*Last Updated: January 2026*
*Tags: #PHP #Ubuntu #VPS #LAMP #Apache #MySQL #WebHosting #Deployment*
Comments (0)