Deploy OpenLiteSpeed web server in Docker containers with PHP-FPM, SSL certificates, and persistent volumes for scalable development environments.
Prerequisites
- Docker and Docker Compose installed
- Basic familiarity with web servers
- Understanding of container networking
What this solves
OpenLiteSpeed with Docker containers provides a flexible development environment that mirrors production setups while maintaining isolation and portability. This approach lets you test web applications with realistic performance characteristics, manage multiple PHP versions, and implement SSL/TLS configurations without affecting your host system.
Prerequisites
You need Docker and Docker Compose installed on your system. You also need basic familiarity with web server configuration and container networking concepts.
Step-by-step installation
Install Docker and Docker Compose
Install Docker engine and Docker Compose for container orchestration.
sudo apt update
sudo apt install -y apt-transport-https ca-certificates curl gnupg lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo systemctl enable --now docker
sudo usermod -aG docker $USER
Create project directory structure
Set up the directory structure for your OpenLiteSpeed Docker environment with proper permissions.
mkdir -p ~/openlitespeed-docker/{config,web,logs,ssl}
cd ~/openlitespeed-docker
chmod 755 ~/openlitespeed-docker
chmod 775 ~/openlitespeed-docker/{web,logs}
Create Docker Compose configuration
Configure the OpenLiteSpeed container with PHP-FPM, persistent volumes, and networking.
version: '3.8'
services:
openlitespeed:
image: litespeedtech/openlitespeed:1.7.19-lsphp82
container_name: openlitespeed-web
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "7080:7080"
volumes:
- ./web:/var/www/vhosts/example.com/html:rw
- ./config:/usr/local/lsws/conf/vhosts/example.com:rw
- ./logs:/usr/local/lsws/logs:rw
- ./ssl:/usr/local/lsws/conf/cert:rw
environment:
- TZ=UTC
- DOMAIN=example.com
networks:
- lsws-network
depends_on:
- mysql
- redis
mysql:
image: mysql:8.0
container_name: openlitespeed-mysql
restart: unless-stopped
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=secure_root_password_2024
- MYSQL_DATABASE=development
- MYSQL_USER=devuser
- MYSQL_PASSWORD=dev_password_2024
volumes:
- mysql-data:/var/lib/mysql
- ./config/mysql.cnf:/etc/mysql/conf.d/custom.cnf:ro
networks:
- lsws-network
command: --default-authentication-plugin=mysql_native_password
redis:
image: redis:7.2-alpine
container_name: openlitespeed-redis
restart: unless-stopped
ports:
- "6379:6379"
volumes:
- redis-data:/data
- ./config/redis.conf:/usr/local/etc/redis/redis.conf:ro
networks:
- lsws-network
command: redis-server /usr/local/etc/redis/redis.conf
volumes:
mysql-data:
driver: local
redis-data:
driver: local
networks:
lsws-network:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
Configure OpenLiteSpeed virtual host
Create the virtual host configuration for your development domain.
docRoot /var/www/vhosts/example.com/html
index {
useServer 0
indexFiles index.php index.html
autoIndex 0
}
scriptHandler {
add lsphp82 php
}
phpIniOverride {
php_admin_value upload_max_filesize 128M
php_admin_value post_max_size 128M
php_admin_value memory_limit 256M
php_admin_value max_execution_time 300
php_admin_value max_input_vars 3000
php_admin_value session.save_handler redis
php_admin_value session.save_path "tcp://redis:6379"
}
rewrite {
enable 1
autoLoadHtaccess 1
logLevel 0
}
accessControl {
allow *
}
realm SampleProtectedArea {
userDB {
location /usr/local/lsws/conf/vhosts/example.com/htpasswd
}
groupDB {
location /usr/local/lsws/conf/vhosts/example.com/htgroup
}
}
context /protected/ {
location /var/www/vhosts/example.com/html/protected/
realm SampleProtectedArea
authName Protected
required user admin
}
context /api/ {
type proxy
uri http://127.0.0.1:8080/
extraHeaders X-Forwarded-Proto $scheme
addDefaultCharset off
}
Create MySQL configuration
Configure MySQL for development workloads with appropriate memory settings.
[mysqld]
innodb_buffer_pool_size = 256M
innodb_log_file_size = 64M
innodb_flush_log_at_trx_commit = 2
innodb_file_per_table = ON
max_connections = 100
connect_timeout = 60
wait_timeout = 300
interactive_timeout = 300
query_cache_size = 32M
query_cache_type = 1
query_cache_limit = 2M
tmp_table_size = 64M
max_heap_table_size = 64M
slow_query_log = 1
slow_query_log_file = /var/lib/mysql/slow.log
long_query_time = 2
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
[mysql]
default-character-set = utf8mb4
[client]
default-character-set = utf8mb4
Configure Redis for session storage
Set up Redis configuration for PHP session handling and caching.
bind 0.0.0.0
protected-mode no
port 6379
tcp-backlog 511
timeout 300
tcp-keepalive 300
maxmemory 128mb
maxmemory-policy allkeys-lru
maxmemory-samples 5
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
loglevel notice
logfile "/data/redis.log"
Create sample PHP application
Set up a test application to verify OpenLiteSpeed functionality and database connectivity.
<?php
// PHP Info and System Check
echo "<h1>OpenLiteSpeed Development Environment</h1>";
echo "<h2>PHP Information</h2>";
echo "PHP Version: " . phpversion() . "<br>";
echo "Server Software: " . $_SERVER['SERVER_SOFTWARE'] . "<br>";
echo "Document Root: " . $_SERVER['DOCUMENT_ROOT'] . "<br>";
// Database Connection Test
echo "<h2>Database Connection</h2>";
try {
$pdo = new PDO('mysql:host=mysql;dbname=development', 'devuser', 'dev_password_2024');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo "MySQL Connection: <span style='color: green;'>SUCCESS</span><br>";
$stmt = $pdo->query('SELECT VERSION() as version');
$result = $stmt->fetch();
echo "MySQL Version: " . $result['version'] . "<br>";
} catch(PDOException $e) {
echo "MySQL Connection: <span style='color: red;'>FAILED - " . $e->getMessage() . "</span><br>";
}
// Redis Connection Test
echo "<h2>Redis Connection</h2>";
try {
$redis = new Redis();
$redis->connect('redis', 6379);
$redis->set('test_key', 'Hello from Redis!');
$value = $redis->get('test_key');
echo "Redis Connection: <span style='color: green;'>SUCCESS</span><br>";
echo "Test Value: " . $value . "<br>";
echo "Redis Version: " . $redis->info()['redis_version'] . "<br>";
} catch(Exception $e) {
echo "Redis Connection: <span style='color: red;'>FAILED - " . $e->getMessage() . "</span><br>";
}
// Session Test
echo "<h2>Session Management</h2>";
session_start();
if (!isset($_SESSION['visit_count'])) {
$_SESSION['visit_count'] = 1;
} else {
$_SESSION['visit_count']++;
}
echo "Session ID: " . session_id() . "<br>";
echo "Visit Count: " . $_SESSION['visit_count'] . "<br>";
echo "Session Handler: " . ini_get('session.save_handler') . "<br>";
echo "Session Path: " . ini_get('session.save_path') . "<br>";
// PHP Extensions
echo "<h2>Available PHP Extensions</h2>";
$extensions = get_loaded_extensions();
sort($extensions);
foreach($extensions as $ext) {
echo $ext . ", ";
}
?>
Create SSL certificate for HTTPS
Generate self-signed SSL certificates for development use.
cd ~/openlitespeed-docker/ssl
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout example.com.key \
-out example.com.crt \
-subj "/C=US/ST=Development/L=Local/O=Development/OU=IT/CN=example.com/emailAddress=admin@example.com"
chmod 644 example.com.crt
chmod 600 example.com.key
Configure firewall rules
Open the necessary ports for web traffic and administrative access.
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 7080/tcp
sudo ufw reload
sudo ufw status
Launch the container stack
Start all services using Docker Compose with proper dependency order.
cd ~/openlitespeed-docker
docker compose up -d
docker compose ps
docker compose logs openlitespeed
Configure OpenLiteSpeed admin access
Set up admin credentials for the OpenLiteSpeed web interface.
docker exec -it openlitespeed-web /usr/local/lsws/admin/misc/admpass.sh
docker exec -it openlitespeed-web /usr/local/lsws/bin/lshttpd -r
Configure SSL certificates and production deployment
Configure HTTPS virtual host
Add SSL configuration to your virtual host for secure connections.
docRoot /var/www/vhosts/example.com/html
vhSSL 1
vhssl {
keyFile /usr/local/lsws/conf/cert/example.com.key
certFile /usr/local/lsws/conf/cert/example.com.crt
certChain 1
sslProtocol 24
ciphers ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:!aNULL:!MD5:!DSS
enableECDHE Yes
renegProtection Yes
sslSessionCache Yes
sslSessionTickets Yes
enableSpdy 15
enableQuic Yes
}
index {
useServer 0
indexFiles index.php index.html
autoIndex 0
}
scriptHandler {
add lsphp82 php
}
rewrite {
enable 1
autoLoadHtaccess 1
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
}
Create production deployment script
Automate the deployment process with health checks and rollback capabilities.
#!/bin/bash
set -e
DOCKER_COMPOSE_FILE="docker-compose.yml"
DOCKER_COMPOSE_PROD_FILE="docker-compose.prod.yml"
BACKUP_DIR="./backups/$(date +%Y%m%d_%H%M%S)"
echo "=== OpenLiteSpeed Production Deployment ==="
Create backup
echo "Creating backup..."
mkdir -p "$BACKUP_DIR"
docker compose exec mysql mysqldump -u root -p"$MYSQL_ROOT_PASSWORD" --all-databases > "$BACKUP_DIR/mysql_backup.sql"
docker compose exec redis redis-cli BGSAVE
cp -r ./web "$BACKUP_DIR/"
cp -r ./config "$BACKUP_DIR/"
Pull latest images
echo "Updating Docker images..."
docker compose pull
Stop services gracefully
echo "Stopping services..."
docker compose down --timeout 30
Start services with health checks
echo "Starting production services..."
docker compose -f "$DOCKER_COMPOSE_FILE" -f "$DOCKER_COMPOSE_PROD_FILE" up -d
Wait for services to be healthy
echo "Waiting for services to be ready..."
sleep 15
Health check
echo "Performing health checks..."
if curl -f -s http://localhost:80 > /dev/null && curl -f -s https://localhost:443 -k > /dev/null; then
echo "✓ Health checks passed"
echo "✓ Deployment successful"
# Cleanup old backups (keep last 5)
find ./backups -maxdepth 1 -type d -name "20*" | sort -r | tail -n +6 | xargs rm -rf
else
echo "✗ Health checks failed - rolling back"
docker compose down
# Restore from backup logic would go here
exit 1
fi
echo "Deployment completed successfully!"
chmod +x ~/openlitespeed-docker/deploy.sh
Create production Docker Compose override
Configure production-specific settings with resource limits and security hardening.
version: '3.8'
services:
openlitespeed:
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
reservations:
cpus: '0.5'
memory: 512M
security_opt:
- no-new-privileges:true
read_only: false
tmpfs:
- /tmp:size=100M,noexec,nosuid,nodev
environment:
- TZ=UTC
- DOMAIN=yourdomain.com
- ENVIRONMENT=production
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
mysql:
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
reservations:
cpus: '0.25'
memory: 256M
security_opt:
- no-new-privileges:true
environment:
- MYSQL_ROOT_PASSWORD_FILE=/run/secrets/mysql_root_password
secrets:
- mysql_root_password
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 30s
timeout: 5s
retries: 3
start_period: 30s
redis:
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
reservations:
cpus: '0.1'
memory: 64M
security_opt:
- no-new-privileges:true
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
secrets:
mysql_root_password:
file: ./secrets/mysql_root_password.txt
Configure log rotation and monitoring
Set up log management and basic monitoring for the containerized environment.
/home/user/openlitespeed-docker/logs/*.log {
daily
missingok
rotate 7
compress
delaycompress
copytruncate
create 644 root root
notifempty
sharedscripts
postrotate
docker exec openlitespeed-web /usr/local/lsws/bin/lshttpd -g
endscript
}
sudo cp ~/openlitespeed-docker/config/logrotate.conf /etc/logrotate.d/openlitespeed-docker
sudo chmod 644 /etc/logrotate.d/openlitespeed-docker
Configure PHP-FPM and web applications
Create PHP-FPM pool configuration
Configure dedicated PHP-FPM pools for different applications with resource isolation.
[app]
user = www-data
group = www-data
listen = 127.0.0.1:9001
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6
pm.max_requests = 500
request_terminate_timeout = 300
request_slowlog_timeout = 60
slowlog = /var/log/php-fpm/app-slow.log
php_admin_value[upload_max_filesize] = 64M
php_admin_value[post_max_size] = 64M
php_admin_value[memory_limit] = 256M
php_admin_value[max_execution_time] = 300
php_admin_value[max_input_vars] = 3000
php_admin_value[session.save_handler] = redis
php_admin_value[session.save_path] = "tcp://redis:6379"
php_admin_value[opcache.enable] = 1
php_admin_value[opcache.memory_consumption] = 128
php_admin_value[opcache.interned_strings_buffer] = 16
php_admin_value[opcache.max_accelerated_files] = 10000
php_admin_value[opcache.validate_timestamps] = 0
env[ENVIRONMENT] = production
env[DB_HOST] = mysql
env[DB_NAME] = development
env[DB_USER] = devuser
env[DB_PASS] = dev_password_2024
env[REDIS_HOST] = redis
env[REDIS_PORT] = 6379
Create advanced application example
Build a more comprehensive PHP application that demonstrates caching, database operations, and session management.
<?php
class DevEnvironment {
private $pdo;
private $redis;
public function __construct() {
$this->connectDatabase();
$this->connectRedis();
$this->initializeDatabase();
}
private function connectDatabase() {
try {
$this->pdo = new PDO(
'mysql:host=mysql;dbname=development;charset=utf8mb4',
'devuser',
'dev_password_2024',
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_PERSISTENT => true
]
);
} catch(PDOException $e) {
throw new Exception('Database connection failed: ' . $e->getMessage());
}
}
private function connectRedis() {
try {
$this->redis = new Redis();
$this->redis->connect('redis', 6379);
$this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_JSON);
} catch(Exception $e) {
throw new Exception('Redis connection failed: ' . $e->getMessage());
}
}
private function initializeDatabase() {
$this->pdo->exec(
"CREATE TABLE IF NOT EXISTS page_views (
id INT AUTO_INCREMENT PRIMARY KEY,
ip_address VARCHAR(45),
user_agent TEXT,
page_url VARCHAR(255),
visit_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_visit_time (visit_time),
INDEX idx_ip_address (ip_address)
) ENGINE=InnoDB"
);
}
public function recordPageView() {
$stmt = $this->pdo->prepare(
"INSERT INTO page_views (ip_address, user_agent, page_url) VALUES (?, ?, ?)"
);
$stmt->execute([
$_SERVER['REMOTE_ADDR'],
$_SERVER['HTTP_USER_AGENT'] ?? 'Unknown',
$_SERVER['REQUEST_URI']
]);
// Increment Redis counter
$this->redis->incr('total_page_views');
$this->redis->incr('daily_views:' . date('Y-m-d'));
$this->redis->expire('daily_views:' . date('Y-m-d'), 86400 * 7);
}
public function getStatistics() {
$cache_key = 'statistics_' . date('Y-m-d_H');
$stats = $this->redis->get($cache_key);
if (!$stats) {
$stmt = $this->pdo->query(
"SELECT
COUNT(*) as total_views,
COUNT(DISTINCT ip_address) as unique_visitors,
DATE(visit_time) as visit_date
FROM page_views
WHERE visit_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY DATE(visit_time)
ORDER BY visit_date DESC"
);
$stats = $stmt->fetchAll();
$this->redis->setex($cache_key, 3600, json_encode($stats));
} else {
$stats = json_decode($stats, true);
}
return $stats;
}
public function displayEnvironmentInfo() {
session_start();
$this->recordPageView();
$stats = $this->getStatistics();
echo "<h1>OpenLiteSpeed Development Environment</h1>";
echo "<div style='display: grid; grid-template-columns: 1fr 1fr; gap: 20px;'>";
// System Information
echo "<div><h2>System Information</h2>";
echo "<ul>";
echo "<li>PHP Version: " . phpversion() . "</li>";
echo "<li>Server: " . ($_SERVER['SERVER_SOFTWARE'] ?? 'Unknown') . "</li>";
echo "<li>Environment: " . (getenv('ENVIRONMENT') ?: 'development') . "</li>";
echo "<li>Load Average: " . sys_getloadavg()[0] . "</li>";
echo "<li>Memory Usage: " . round(memory_get_usage(true)/1024/1024, 2) . " MB</li>";
echo "</ul></div>";
// Database Status
echo "<div><h2>Database Status</h2>";
$stmt = $this->pdo->query('SHOW STATUS LIKE "Threads_connected"');
$connections = $stmt->fetch()['Value'];
echo "<ul>";
echo "<li>Active Connections: $connections</li>";
echo "<li>Total Page Views: " . $this->redis->get('total_page_views') . "</li>";
echo "<li>Today's Views: " . ($this->redis->get('daily_views:' . date('Y-m-d')) ?: 0) . "</li>";
echo "</ul></div>";
echo "</div>";
// Statistics Table
if (!empty($stats)) {
echo "<h2>7-Day Statistics</h2>";
echo "<table border='1' style='width:100%; border-collapse: collapse;'>";
echo "<tr><th>Date</th><th>Total Views</th><th>Unique Visitors</th></tr>";
foreach($stats as $row) {
echo "<tr>";
echo "<td>" . $row['visit_date'] . "</td>";
echo "<td>" . $row['total_views'] . "</td>";
echo "<td>" . $row['unique_visitors'] . "</td>";
echo "</tr>";
}
echo "</table>";
}
}
}
try {
$app = new DevEnvironment();
$app->displayEnvironmentInfo();
} catch(Exception $e) {
echo "<h1>Application Error</h1>";
echo "<p style='color: red;'>" . htmlspecialchars($e->getMessage()) . "</p>";
}
?>
Verify your setup
Test all components of your OpenLiteSpeed Docker environment to ensure proper functionality.
# Check all containers are running
docker compose ps
Test HTTP access
curl -I http://localhost:80/
Test HTTPS access (ignore certificate warnings for development)
curl -I -k https://localhost:443/
Check application functionality
curl -s http://localhost:80/app.php | grep -o "<h1>.*</h1>"
Verify database connectivity
docker exec openlitespeed-mysql mysql -u devuser -p"dev_password_2024" -e "SELECT 'Database OK' as status;"
Check Redis functionality
docker exec openlitespeed-redis redis-cli ping
View container logs
docker compose logs --tail=50 openlitespeed
Check resource usage
docker stats --no-stream
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Container won't start | Port conflicts or permission issues | Check docker compose logs and ensure ports 80, 443, 7080 are free |
| 502 Bad Gateway error | PHP-FPM not running or misconfigured | Check PHP configuration in /usr/local/lsws/lsphp82/etc/php.ini |
| Database connection failed | MySQL not ready or wrong credentials | Wait for MySQL to fully start, verify environment variables |
| SSL certificate warnings | Self-signed certificate | Accept warnings for development or use proper certificates |
| Session data not persisting | Redis connection issues | Verify Redis container is running and accessible |
| File upload failures | Permission or size limits | Check upload_max_filesize and directory permissions |
| Admin panel inaccessible | Firewall or container networking | Ensure port 7080 is open and container is running |
Next steps
- Set up OpenLiteSpeed with WordPress and LSCache optimization for content management systems
- Configure NGINX reverse proxy with SSL termination for production load balancing
- Implement Docker container monitoring with Prometheus and Grafana for observability
- Deploy OpenLiteSpeed containers to Kubernetes for orchestration
- Configure OpenLiteSpeed clustering for high availability for production scaling
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Default values
PROJECT_NAME="${1:-openlitespeed-docker}"
DOMAIN="${2:-example.com}"
INSTALL_DIR="${3:-$HOME/$PROJECT_NAME}"
# Function to print colored messages
print_status() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Usage function
usage() {
echo "Usage: $0 [PROJECT_NAME] [DOMAIN] [INSTALL_DIR]"
echo "Example: $0 myproject mysite.local /opt/openlitespeed"
echo "Defaults: PROJECT_NAME=openlitespeed-docker, DOMAIN=example.com, INSTALL_DIR=~/openlitespeed-docker"
exit 1
}
# Cleanup function
cleanup() {
print_error "Installation failed. Cleaning up..."
if [ -d "$INSTALL_DIR" ]; then
rm -rf "$INSTALL_DIR"
fi
}
# Set trap for cleanup on error
trap cleanup ERR
# Check prerequisites
if [[ $EUID -eq 0 ]]; then
print_error "Do not run this script as root. Use a regular user with sudo privileges."
exit 1
fi
if ! command -v sudo &> /dev/null; then
print_error "sudo is required but not installed."
exit 1
fi
# Auto-detect distribution
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update"
PKG_INSTALL="apt install -y"
DOCKER_REPO_SETUP="deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
GPG_KEY_CMD="curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg"
FIREWALL_CMD="ufw"
;;
almalinux|rocky|centos|rhel|ol)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
DOCKER_REPO_URL="https://download.docker.com/linux/centos/docker-ce.repo"
FIREWALL_CMD="firewall-cmd"
;;
fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
DOCKER_REPO_URL="https://download.docker.com/linux/fedora/docker-ce.repo"
FIREWALL_CMD="firewall-cmd"
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum update -y"
PKG_INSTALL="yum install -y"
DOCKER_REPO_URL="https://download.docker.com/linux/centos/docker-ce.repo"
FIREWALL_CMD="firewall-cmd"
;;
*)
print_error "Unsupported distribution: $ID"
exit 1
;;
esac
else
print_error "Cannot detect distribution. /etc/os-release not found."
exit 1
fi
print_status "Detected distribution: $PRETTY_NAME"
# Step 1: Update system packages
echo -e "\n[1/7] Updating system packages..."
sudo $PKG_UPDATE
# Step 2: Install prerequisites
echo -e "\n[2/7] Installing prerequisites..."
if [ "$PKG_MGR" = "apt" ]; then
sudo $PKG_INSTALL apt-transport-https ca-certificates curl gnupg lsb-release
elif [ "$PKG_MGR" = "dnf" ] || [ "$PKG_MGR" = "yum" ]; then
sudo $PKG_INSTALL ca-certificates curl gnupg
fi
# Step 3: Install Docker
echo -e "\n[3/7] Installing Docker..."
if [ "$PKG_MGR" = "apt" ]; then
eval "$GPG_KEY_CMD"
echo "$DOCKER_REPO_SETUP" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo $PKG_UPDATE
sudo $PKG_INSTALL docker-ce docker-ce-cli containerd.io docker-compose-plugin
else
sudo $PKG_MGR config-manager --add-repo "$DOCKER_REPO_URL"
sudo $PKG_INSTALL docker-ce docker-ce-cli containerd.io docker-compose-plugin
fi
# Step 4: Configure Docker service
echo -e "\n[4/7] Configuring Docker service..."
sudo systemctl enable docker
sudo systemctl start docker
sudo usermod -aG docker "$USER"
# Step 5: Create project directory structure
echo -e "\n[5/7] Creating project directory structure..."
mkdir -p "$INSTALL_DIR"/{config,web,logs,ssl}
chmod 755 "$INSTALL_DIR"
chmod 755 "$INSTALL_DIR"/{config,web,logs,ssl}
# Step 6: Create configuration files
echo -e "\n[6/7] Creating configuration files..."
# Create docker-compose.yml
cat > "$INSTALL_DIR/docker-compose.yml" << 'EOF'
version: '3.8'
services:
openlitespeed:
image: litespeedtech/openlitespeed:1.7.19-lsphp82
container_name: openlitespeed-web
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "7080:7080"
volumes:
- ./web:/var/www/vhosts/example.com/html:rw
- ./config:/usr/local/lsws/conf/vhosts/example.com:rw
- ./logs:/usr/local/lsws/logs:rw
- ./ssl:/usr/local/lsws/conf/cert:rw
environment:
- TZ=UTC
- DOMAIN=example.com
networks:
- lsws-network
depends_on:
- mysql
- redis
mysql:
image: mysql:8.0
container_name: openlitespeed-mysql
restart: unless-stopped
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=secure_root_password_2024
- MYSQL_DATABASE=development
- MYSQL_USER=devuser
- MYSQL_PASSWORD=dev_password_2024
volumes:
- mysql-data:/var/lib/mysql
networks:
- lsws-network
command: --default-authentication-plugin=mysql_native_password
redis:
image: redis:7.2-alpine
container_name: openlitespeed-redis
restart: unless-stopped
ports:
- "6379:6379"
volumes:
- redis-data:/data
networks:
- lsws-network
volumes:
mysql-data:
driver: local
redis-data:
driver: local
networks:
lsws-network:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
EOF
# Update domain in docker-compose.yml
sed -i "s/example.com/$DOMAIN/g" "$INSTALL_DIR/docker-compose.yml"
# Create sample index.php
cat > "$INSTALL_DIR/web/index.php" << 'EOF'
<?php
echo "<h1>OpenLiteSpeed + Docker Development Environment</h1>";
echo "<p>PHP Version: " . phpversion() . "</p>";
echo "<p>Current Time: " . date('Y-m-d H:i:s') . "</p>";
// Test MySQL connection
try {
$pdo = new PDO('mysql:host=mysql;dbname=development', 'devuser', 'dev_password_2024');
echo "<p style='color: green;'>MySQL Connection: Success</p>";
} catch(PDOException $e) {
echo "<p style='color: red;'>MySQL Connection: Failed - " . $e->getMessage() . "</p>";
}
// Test Redis connection
try {
$redis = new Redis();
$redis->connect('redis', 6379);
echo "<p style='color: green;'>Redis Connection: Success</p>";
} catch(Exception $e) {
echo "<p style='color: red;'>Redis Connection: Failed - " . $e->getMessage() . "</p>";
}
?>
EOF
# Set proper ownership
chown -R "$USER:$USER" "$INSTALL_DIR"
chmod 644 "$INSTALL_DIR/docker-compose.yml" "$INSTALL_DIR/web/index.php"
# Step 7: Configure firewall (if available and running)
echo -e "\n[7/7] Configuring firewall..."
if command -v "$FIREWALL_CMD" &> /dev/null; then
if [ "$FIREWALL_CMD" = "ufw" ]; then
if sudo ufw status | grep -q "Status: active"; then
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 7080/tcp
print_status "UFW rules added for ports 80, 443, 7080"
fi
elif [ "$FIREWALL_CMD" = "firewall-cmd" ]; then
if sudo systemctl is-active --quiet firewalld; then
sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --permanent --add-port=443/tcp
sudo firewall-cmd --permanent --add-port=7080/tcp
sudo firewall-cmd --reload
print_status "Firewalld rules added for ports 80, 443, 7080"
fi
fi
fi
# Disable trap
trap - ERR
# Final verification and instructions
echo -e "\n${GREEN}Installation completed successfully!${NC}"
echo -e "\n${YELLOW}Important Notes:${NC}"
echo "1. Log out and log back in for Docker group membership to take effect"
echo "2. Project directory: $INSTALL_DIR"
echo "3. To start the environment:"
echo " cd $INSTALL_DIR"
echo " docker compose up -d"
echo "4. Access points:"
echo " - Website: http://localhost or http://$DOMAIN (if configured in /etc/hosts)"
echo " - Admin Panel: https://localhost:7080 (admin/123456)"
echo " - MySQL: localhost:3306 (devuser/dev_password_2024)"
echo " - Redis: localhost:6379"
echo -e "\n${YELLOW}Security Reminder:${NC} Change default passwords before production use!"
Review the script before running. Execute with: bash install.sh