Web servers are essential components of modern infrastructure, serving everything from simple static websites to complex web applications. Alpine Linux’s minimal footprint and security-focused design make it an excellent choice for hosting web servers in production environments.
In this guide, I’ll walk you through installing and configuring three popular web servers on Alpine Linux: Apache HTTP Server, Nginx, and Lighttpd. Each server has its own strengths and use cases, and I’ll help you understand which one might be best for your specific needs.
Prerequisites
Before we begin, ensure you have:
- Alpine Linux system with root access
- Updated package repositories
- Basic understanding of command line operations
- Network connectivity for package downloads
Let’s start by updating our system:
apk update && apk upgrade
Installing Apache HTTP Server
Apache is one of the most widely used web servers in the world, known for its flexibility and extensive module system.
Basic Apache Installation
What we’re doing: Installing Apache and its documentation package.
apk add apache2 apache2-doc
Starting and Enabling Apache
What we’re doing: Starting the Apache service and enabling it to start automatically on boot.
rc-update add apache2 default
service apache2 start
Basic Apache Configuration
The main Apache configuration file is located at /etc/apache2/httpd.conf
. Let’s make some basic configurations:
# Edit the main configuration file
vi /etc/apache2/httpd.conf
Key configuration changes to make:
# Uncomment ServerName directive
ServerName localhost:80
# Set the document root
DocumentRoot "/var/www/localhost/htdocs"
# Enable useful modules
LoadModule rewrite_module modules/mod_rewrite.so
LoadModule ssl_module modules/mod_ssl.so
Creating a Test Page
What we’re doing: Creating a simple HTML page to test our Apache installation.
mkdir -p /var/www/localhost/htdocs
echo '<h1>Apache on Alpine Linux is working!</h1>' > /var/www/localhost/htdocs/index.html
Configuring Virtual Hosts
What we’re doing: Setting up virtual hosts to serve multiple websites from one Apache instance.
# Create vhost configuration directory
mkdir -p /etc/apache2/conf.d
# Create a virtual host configuration
cat > /etc/apache2/conf.d/example.conf << 'EOF'
<VirtualHost *:80>
ServerName example.local
DocumentRoot /var/www/example
ErrorLog /var/log/apache2/example_error.log
CustomLog /var/log/apache2/example_access.log combined
<Directory /var/www/example>
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
EOF
Apache Security Hardening
What we’re doing: Implementing basic security measures for Apache.
# Edit security configuration
cat >> /etc/apache2/conf.d/security.conf << 'EOF'
# Hide Apache version
ServerTokens Prod
ServerSignature Off
# Prevent access to .htaccess files
<Files ~ "^\.ht">
Require all denied
</Files>
# Disable directory browsing
Options -Indexes
# Prevent access to sensitive files
<FilesMatch "\.(htaccess|htpasswd|ini|log|sh|inc|bak)$">
Require all denied
</FilesMatch>
EOF
Installing Nginx
Nginx is known for its high performance, low resource usage, and excellent handling of concurrent connections.
Basic Nginx Installation
What we’re doing: Installing Nginx and its documentation.
apk add nginx nginx-doc
Starting and Enabling Nginx
What we’re doing: Starting Nginx and configuring it to start automatically.
rc-update add nginx default
service nginx start
Basic Nginx Configuration
The main Nginx configuration is in /etc/nginx/nginx.conf
. Let’s optimize it for Alpine Linux:
# Backup original configuration
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.backup
# Create optimized configuration
cat > /etc/nginx/nginx.conf << 'EOF'
user nginx;
worker_processes auto;
worker_rlimit_nofile 65535;
events {
worker_connections 1024;
use epoll;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Logging format
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# Performance optimizations
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# Security headers
server_tokens off;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
# Gzip compression
gzip on;
gzip_vary on;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
# Include server configurations
include /etc/nginx/conf.d/*.conf;
}
EOF
Creating Nginx Server Blocks
What we’re doing: Setting up server blocks (Nginx’s equivalent to Apache’s virtual hosts).
# Create web directory
mkdir -p /var/www/html
# Create default server block
cat > /etc/nginx/conf.d/default.conf << 'EOF'
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.html index.htm;
server_name _;
location / {
try_files $uri $uri/ =404;
}
# Security configurations
location ~ /\. {
deny all;
}
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
EOF
Creating Test Content
What we’re doing: Creating a test page for Nginx.
echo '<h1>Nginx on Alpine Linux is working!</h1>' > /var/www/html/index.html
chown -R nginx:nginx /var/www/html
Nginx SSL Configuration
What we’re doing: Preparing Nginx for SSL/TLS encryption.
# Create SSL directory
mkdir -p /etc/nginx/ssl
# Generate self-signed certificate for testing
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/nginx/ssl/nginx.key \
-out /etc/nginx/ssl/nginx.crt \
-subj "/C=US/ST=State/L=City/O=Organization/CN=localhost"
# Create SSL server block
cat > /etc/nginx/conf.d/ssl.conf << 'EOF'
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
root /var/www/html;
index index.html;
server_name localhost;
ssl_certificate /etc/nginx/ssl/nginx.crt;
ssl_certificate_key /etc/nginx/ssl/nginx.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
try_files $uri $uri/ =404;
}
}
EOF
Installing Lighttpd
Lighttpd is a lightweight web server that’s particularly efficient for serving static content and has a small memory footprint.
Basic Lighttpd Installation
What we’re doing: Installing Lighttpd and its modules.
apk add lighttpd lighttpd-mod_auth lighttpd-mod_rewrite
Starting and Enabling Lighttpd
What we’re doing: Starting Lighttpd and enabling automatic startup.
rc-update add lighttpd default
service lighttpd start
Basic Lighttpd Configuration
The main configuration file is /etc/lighttpd/lighttpd.conf
. Let’s configure it:
# Backup original configuration
cp /etc/lighttpd/lighttpd.conf /etc/lighttpd/lighttpd.conf.backup
# Create basic configuration
cat > /etc/lighttpd/lighttpd.conf << 'EOF'
var.basedir = "/var/www/localhost"
var.logdir = "/var/log/lighttpd"
var.statedir = "/var/lib/lighttpd"
server.modules = (
"mod_access",
"mod_alias",
"mod_compress",
"mod_redirect",
"mod_rewrite",
)
server.document-root = var.basedir + "/htdocs"
server.upload-dirs = ( "/tmp" )
server.errorlog = var.logdir + "/error.log"
server.pid-file = "/run/lighttpd.pid"
server.username = "lighttpd"
server.groupname = "lighttpd"
server.port = 80
index-file.names = ( "index.php", "index.html", "index.htm" )
url.access-deny = ( "~", ".inc" )
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )
compress.cache-dir = var.statedir + "/cache/compress/"
compress.filetype = ( "application/javascript", "text/css", "text/html", "text/plain" )
# mimetype mapping
mimetype.assign = (
".pdf" => "application/pdf",
".sig" => "application/pgp-signature",
".spl" => "application/futuresplash",
".class" => "application/octet-stream",
".ps" => "application/postscript",
".torrent" => "application/x-bittorrent",
".dvi" => "application/x-dvi",
".gz" => "application/x-gzip",
".pac" => "application/x-ns-proxy-autoconfig",
".swf" => "application/x-shockwave-flash",
".tar.gz" => "application/x-tgz",
".tgz" => "application/x-tgz",
".tar" => "application/x-tar",
".zip" => "application/zip",
".mp3" => "audio/mpeg",
".m3u" => "audio/x-mpegurl",
".wma" => "audio/x-ms-wma",
".wax" => "audio/x-ms-wax",
".ogg" => "application/ogg",
".wav" => "audio/x-wav",
".gif" => "image/gif",
".jpg" => "image/jpeg",
".jpeg" => "image/jpeg",
".png" => "image/png",
".xbm" => "image/x-xbitmap",
".xpm" => "image/x-xpixmap",
".xwd" => "image/x-xwindowdump",
".css" => "text/css",
".html" => "text/html",
".htm" => "text/html",
".js" => "text/javascript",
".asc" => "text/plain",
".c" => "text/plain",
".cpp" => "text/plain",
".log" => "text/plain",
".conf" => "text/plain",
".text" => "text/plain",
".txt" => "text/plain",
".dtd" => "text/xml",
".xml" => "text/xml",
".mpeg" => "video/mpeg",
".mpg" => "video/mpeg",
".mov" => "video/quicktime",
".qt" => "video/quicktime",
".avi" => "video/x-msvideo",
".asf" => "video/x-ms-asf",
".asx" => "video/x-ms-asf",
".wmv" => "video/x-ms-wmv",
".bz2" => "application/x-bzip",
".tbz" => "application/x-bzip-compressed-tar",
".tar.bz2" => "application/x-bzip-compressed-tar",
"" => "application/octet-stream",
)
EOF
Creating Test Content
What we’re doing: Setting up a test page for Lighttpd.
mkdir -p /var/www/localhost/htdocs
echo '<h1>Lighttpd on Alpine Linux is working!</h1>' > /var/www/localhost/htdocs/index.html
chown -R lighttpd:lighttpd /var/www/localhost
Performance Optimization
System-level Optimizations
What we’re doing: Optimizing system settings for better web server performance.
# Increase file descriptor limits
echo '* soft nofile 65535' >> /etc/security/limits.conf
echo '* hard nofile 65535' >> /etc/security/limits.conf
# Optimize kernel parameters
cat >> /etc/sysctl.conf << 'EOF'
# Network optimizations
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 5000
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_fin_timeout = 10
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 60
net.ipv4.tcp_keepalive_probes = 3
EOF
sysctl -p
Log Rotation Configuration
What we’re doing: Setting up log rotation to prevent disk space issues.
# Create logrotate configuration for web servers
cat > /etc/logrotate.d/webservers << 'EOF'
/var/log/apache2/*.log {
daily
missingok
rotate 52
compress
delaycompress
notifempty
postrotate
/etc/init.d/apache2 reload > /dev/null 2>&1 || true
endscript
}
/var/log/nginx/*.log {
daily
missingok
rotate 52
compress
delaycompress
notifempty
postrotate
/etc/init.d/nginx reload > /dev/null 2>&1 || true
endscript
}
/var/log/lighttpd/*.log {
daily
missingok
rotate 52
compress
delaycompress
notifempty
postrotate
/etc/init.d/lighttpd reload > /dev/null 2>&1 || true
endscript
}
EOF
Firewall Configuration
What we’re doing: Configuring iptables to allow web traffic while maintaining security.
# Install iptables
apk add iptables
# Create basic firewall rules
cat > /etc/iptables/rules-save << 'EOF'
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
# Allow loopback traffic
-A INPUT -i lo -j ACCEPT
# Allow established connections
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# Allow SSH (change port if needed)
-A INPUT -p tcp --dport 22 -j ACCEPT
# Allow HTTP traffic
-A INPUT -p tcp --dport 80 -j ACCEPT
# Allow HTTPS traffic
-A INPUT -p tcp --dport 443 -j ACCEPT
# Allow ping
-A INPUT -p icmp --icmp-type echo-request -j ACCEPT
COMMIT
EOF
# Apply firewall rules
iptables-restore < /etc/iptables/rules-save
# Enable iptables service
rc-update add iptables default
Monitoring and Maintenance
Basic Monitoring Script
What we’re doing: Creating a script to monitor web server health.
cat > /usr/local/bin/webserver-monitor.sh << 'EOF'
#!/bin/sh
# Web server monitoring script
LOG_FILE="/var/log/webserver-monitor.log"
DATE=$(date '+%Y-%m-%d %H:%M:%S')
# Function to log messages
log_message() {
echo "[$DATE] $1" >> $LOG_FILE
}
# Check Apache
if pgrep httpd > /dev/null; then
if curl -s http://localhost > /dev/null; then
log_message "Apache: Running and responding"
else
log_message "Apache: Running but not responding"
service apache2 restart
fi
else
log_message "Apache: Not running"
fi
# Check Nginx
if pgrep nginx > /dev/null; then
if curl -s http://localhost:8080 > /dev/null; then
log_message "Nginx: Running and responding"
else
log_message "Nginx: Running but not responding"
service nginx restart
fi
else
log_message "Nginx: Not running"
fi
# Check Lighttpd
if pgrep lighttpd > /dev/null; then
if curl -s http://localhost:8081 > /dev/null; then
log_message "Lighttpd: Running and responding"
else
log_message "Lighttpd: Running but not responding"
service lighttpd restart
fi
else
log_message "Lighttpd: Not running"
fi
EOF
chmod +x /usr/local/bin/webserver-monitor.sh
# Add to crontab to run every 5 minutes
echo "*/5 * * * * /usr/local/bin/webserver-monitor.sh" | crontab -
Testing Your Installation
Apache Testing
# Test Apache configuration
apachectl configtest
# Check if Apache is listening
netstat -tlnp | grep :80
# Test HTTP response
curl -I http://localhost
Nginx Testing
# Test Nginx configuration
nginx -t
# Reload Nginx configuration
nginx -s reload
# Test HTTP response
curl -I http://localhost
Lighttpd Testing
# Test Lighttpd configuration
lighttpd -t -f /etc/lighttpd/lighttpd.conf
# Check if Lighttpd is listening
netstat -tlnp | grep lighttpd
# Test HTTP response
curl -I http://localhost
Troubleshooting Common Issues
Permission Problems
If you encounter permission issues:
# Fix web directory permissions
chown -R apache:apache /var/www # For Apache
chown -R nginx:nginx /var/www # For Nginx
chown -R lighttpd:lighttpd /var/www # For Lighttpd
# Set proper directory permissions
find /var/www -type d -exec chmod 755 {} \;
find /var/www -type f -exec chmod 644 {} \;
Port Conflicts
If multiple web servers are running:
# Check which services are using port 80
ss -tlnp | grep :80
# Configure different ports in respective configuration files
# Apache: Listen 8080
# Nginx: listen 8081
# Lighttpd: server.port = 8082
Log Analysis
# Monitor web server logs in real-time
tail -f /var/log/apache2/error.log
tail -f /var/log/nginx/error.log
tail -f /var/log/lighttpd/error.log
# Search for specific errors
grep "error" /var/log/*/error.log
Security Best Practices
Regular Updates
# Create update script
cat > /usr/local/bin/security-updates.sh << 'EOF'
#!/bin/sh
apk update
apk upgrade
apk audit --packages
EOF
chmod +x /usr/local/bin/security-updates.sh
# Schedule weekly security updates
echo "0 2 * * 0 /usr/local/bin/security-updates.sh" | crontab -
Access Control
Implement proper access controls:
# Deny access to sensitive directories
# Add to your web server configuration:
# Apache (.htaccess or virtual host)
<Directory "/var/www/admin">
Require ip 192.168.1.0/24
Require ip 10.0.0.0/8
</Directory>
# Nginx (in server block)
location /admin {
allow 192.168.1.0/24;
allow 10.0.0.0/8;
deny all;
}
Conclusion
You now have three different web servers installed and configured on Alpine Linux. Each serves different purposes:
- Apache: Best for complex configurations, extensive module support, and .htaccess files
- Nginx: Excellent for high-traffic sites, reverse proxy setups, and static content serving
- Lighttpd: Perfect for low-resource environments and simple static sites
Choose the web server that best fits your specific requirements. Remember to regularly update your system, monitor your logs, and follow security best practices to maintain a secure and efficient web server environment.
For production deployments, consider implementing SSL certificates from Let’s Encrypt, setting up proper backup procedures, and monitoring solutions to ensure optimal performance and security.