Set up comprehensive Apache log monitoring with GoAccess for real-time analysis and ELK stack for centralized log processing, creating powerful Kibana dashboards for web server insights and automated alerting.
Prerequisites
- Apache HTTP server installed and running
- Minimum 4GB RAM for ELK stack
- Root or sudo access
- At least 20GB free disk space
What this solves
Apache web servers generate massive amounts of log data that contain valuable insights about traffic patterns, errors, and performance issues. This tutorial shows you how to implement a complete log analysis pipeline using GoAccess for real-time terminal-based analysis and the ELK stack (Elasticsearch, Logstash, Kibana) for centralized processing and visualization. You'll create professional dashboards and set up automated alerting for proactive monitoring.
Step-by-step installation
Update system packages
Start by updating your package manager to ensure you get the latest versions of all components.
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl wget gnupg2 software-properties-common
Install GoAccess for real-time log analysis
GoAccess provides immediate insights into Apache logs with terminal-based and HTML reports.
sudo apt install -y goaccess
Configure GoAccess for Apache logs
Create a configuration file that matches Apache's default log format and enables real-time updates.
time-format %H:%M:%S
date-format %d/%b/%Y
log-format %h %^[%d:%t %^] "%r" %s %b "%R" "%u"
real-time-html true
port 7890
ws-url wss://example.com:7890
html-prefs {"theme":"bright","perPage":10}
html-report-title Apache Log Analysis
ignore-panel REFERRERS
ignore-panel KEYPHRASES
ignore-panel GEOLOCATION
output /var/www/html/goaccess-report.html
Set up Java repository for Elasticsearch
The ELK stack requires Java. Add the official repositories for the latest versions.
sudo apt install -y openjdk-11-jdk
echo 'JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64' | sudo tee -a /etc/environment
source /etc/environment
Add Elastic Stack repository
Add the official Elastic repository to install Elasticsearch, Logstash, and Kibana.
curl -fsSL https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo gpg --dearmor -o /usr/share/keyrings/elastic-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/elastic-keyring.gpg] https://artifacts.elastic.co/packages/8.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-8.x.list
sudo apt update
Install Elasticsearch
Install Elasticsearch for storing and indexing Apache log data.
sudo apt install -y elasticsearch
Configure Elasticsearch
Configure Elasticsearch for Apache log storage with appropriate memory settings and network binding.
cluster.name: apache-logs
node.name: apache-logs-node-1
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
network.host: localhost
http.port: 9200
cluster.initial_master_nodes: ["apache-logs-node-1"]
xpack.security.enabled: false
xpack.security.enrollment.enabled: false
xpack.security.http.ssl.enabled: false
xpack.security.transport.ssl.enabled: false
Configure Elasticsearch JVM heap
Set appropriate heap size based on your server memory. Use 50% of available RAM, maximum 32GB.
-Xms2g
-Xmx2g
Install Logstash
Install Logstash to process and transform Apache logs before sending to Elasticsearch.
sudo apt install -y logstash
Configure Logstash for Apache logs
Create a Logstash pipeline configuration to parse Apache access logs and error logs.
input {
file {
path => "/var/log/apache2/access.log"
type => "apache_access"
start_position => "beginning"
}
file {
path => "/var/log/apache2/error.log"
type => "apache_error"
start_position => "beginning"
}
}
filter {
if [type] == "apache_access" {
grok {
match => { "message" => "%{COMBINEDAPACHELOG}" }
}
date {
match => [ "timestamp" , "dd/MMM/yyyy:HH:mm:ss Z" ]
}
mutate {
convert => { "response" => "integer" }
convert => { "bytes" => "integer" }
}
if [clientip] {
geoip {
source => "clientip"
}
}
}
if [type] == "apache_error" {
grok {
match => { "message" => "\[%{HTTPDATE:timestamp}\] \[%{WORD:level}\] \[pid %{POSINT:pid}\] %{GREEDYDATA:error_message}" }
}
date {
match => [ "timestamp" , "EEE MMM dd HH:mm:ss.SSSSSS yyyy" ]
}
}
}
output {
elasticsearch {
hosts => ["localhost:9200"]
index => "apache-logs-%{+YYYY.MM.dd}"
}
stdout { codec => rubydebug }
}
Install Kibana
Install Kibana for visualizing and creating dashboards from the Apache log data.
sudo apt install -y kibana
Configure Kibana
Configure Kibana to connect to Elasticsearch and bind to the appropriate network interface.
server.port: 5601
server.host: "0.0.0.0"
server.name: "apache-logs-kibana"
elasticsearch.hosts: ["http://localhost:9200"]
elasticsearch.requestTimeout: 90000
logging.appenders.file.type: file
logging.appenders.file.fileName: /var/log/kibana/kibana.log
logging.appenders.file.layout.type: json
logging.root.appenders: [default, file]
Start and enable ELK services
Start all ELK stack services and enable them to start automatically on boot.
sudo systemctl enable elasticsearch
sudo systemctl start elasticsearch
sudo systemctl enable logstash
sudo systemctl start logstash
sudo systemctl enable kibana
sudo systemctl start kibana
Configure Apache log format
Ensure Apache is configured with the combined log format for better parsing. This configuration works with the Logstash grok pattern we defined.
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent
CustomLog /var/log/apache2/access.log combined
ErrorLog /var/log/apache2/error.log
Set up log file permissions
Configure proper permissions so Logstash can read Apache log files without compromising security.
sudo usermod -a -G adm logstash
sudo chmod 644 /var/log/apache2/access.log
sudo chmod 644 /var/log/apache2/error.log
sudo chown root:adm /var/log/apache2/access.log
sudo chown root:adm /var/log/apache2/error.log
Create GoAccess systemd service
Set up GoAccess as a systemd service for continuous real-time log monitoring.
[Unit]
Description=GoAccess Apache Log Analysis
After=apache2.service
[Service]
Type=simple
User=www-data
Group=www-data
ExecStart=/usr/bin/goaccess /var/log/apache2/access.log -c /etc/goaccess/goaccess.conf --real-time-html --daemonize
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Enable and start GoAccess service
Start the GoAccess service to begin real-time log analysis.
sudo systemctl daemon-reload
sudo systemctl enable goaccess
sudo systemctl start goaccess
Configure firewall rules
Open necessary ports for Kibana web interface and GoAccess real-time updates.
sudo ufw allow 5601/tcp comment 'Kibana'
sudo ufw allow 7890/tcp comment 'GoAccess WebSocket'
sudo ufw reload
Create Kibana dashboards
Create index pattern in Kibana
Access Kibana at http://your-server-ip:5601 and create an index pattern for Apache logs.
- Open Kibana in your browser
- Go to Management → Stack Management → Index Patterns
- Click "Create index pattern"
- Enter
apache-logs-*as the index pattern - Select
@timestampas the time field - Click "Create index pattern"
Import dashboard configuration
Create a comprehensive dashboard with key Apache metrics and visualizations.
{
"objects": [
{
"attributes": {
"title": "Apache Access Logs Overview",
"type": "dashboard",
"description": "Comprehensive Apache log analysis dashboard",
"panelsJSON": "[{\"version\":\"8.0.0\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15},\"panelIndex\":\"1\",\"embeddableConfig\":{},\"panelRefName\":\"panel_1\"}]",
"refreshInterval": {
"pause": false,
"value": 30000
},
"timeRestore": true,
"timeTo": "now",
"timeFrom": "now-24h"
},
"id": "apache-overview",
"type": "dashboard"
}
]
}
Set up real-time monitoring alerts
Create Elasticsearch watcher for error monitoring
Set up automated alerts for Apache error patterns and high traffic spikes.
curl -X PUT "localhost:9200/_watcher/watch/apache_errors" -H 'Content-Type: application/json' -d'
{
"trigger": {
"schedule": {
"interval": "5m"
}
},
"input": {
"search": {
"request": {
"search_type": "query_then_fetch",
"indices": ["apache-logs-*"],
"body": {
"query": {
"bool": {
"filter": [
{
"range": {
"@timestamp": {
"gte": "now-5m"
}
}
},
{
"terms": {
"response": [500, 502, 503, 504]
}
}
]
}
}
}
}
}
},
"condition": {
"compare": {
"ctx.payload.hits.total": {
"gt": 10
}
}
},
"actions": {
"log_error": {
"logging": {
"level": "error",
"text": "High number of Apache server errors detected: {{ctx.payload.hits.total}} errors in the last 5 minutes"
}
}
}
}'
Configure log rotation for Apache logs
Set up log rotation to prevent disk space issues while maintaining log history for analysis. The existing log rotation configuration may need adjustment for higher traffic sites.
/var/log/apache2/*.log {
daily
missingok
rotate 52
compress
delaycompress
notifempty
sharedscripts
postrotate
if /bin/systemctl status apache2 > /dev/null ; then \
/bin/systemctl reload apache2 > /dev/null; \
fi;
if /bin/systemctl status logstash > /dev/null ; then \
/bin/systemctl restart logstash > /dev/null; \
fi;
endscript
create 644 root adm
}
Verify your setup
Check that all services are running correctly and processing logs:
sudo systemctl status elasticsearch
sudo systemctl status logstash
sudo systemctl status kibana
sudo systemctl status goaccess
Verify Elasticsearch is receiving data:
curl -X GET "localhost:9200/_cat/indices?v"
curl -X GET "localhost:9200/apache-logs-*/_search?size=5&pretty"
Test GoAccess HTML report generation:
goaccess /var/log/apache2/access.log -c /etc/goaccess/goaccess.conf --html-report-title="Test Report" -o /tmp/test-report.html
Generate some test traffic and verify logs are being processed:
curl -I http://localhost/
curl -I http://localhost/nonexistent-page
tail -f /var/log/apache2/access.log
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Logstash not reading logs | Permission denied on log files | sudo usermod -a -G adm logstash && sudo systemctl restart logstash |
| Kibana dashboard empty | No data in Elasticsearch | Check Logstash status: sudo systemctl status logstash and log parsing errors |
| GoAccess real-time not working | WebSocket connection failed | Verify port 7890 is open and ws-url matches your domain in goaccess.conf |
| Elasticsearch service fails to start | Insufficient heap memory | Reduce heap size in /etc/elasticsearch/jvm.options.d/heap.options |
| High disk usage | Log files growing rapidly | Configure index lifecycle management or adjust log rotation frequency |
| Grok parsing failures in Logstash | Custom Apache log format | Update grok pattern in /etc/logstash/conf.d/apache.conf to match your log format |
Next steps
- Configure ELK stack for centralized logging with Elasticsearch 8, Logstash 8, and Kibana 8 for advanced ELK configurations
- Monitor nginx performance with Prometheus and Grafana using nginx-prometheus-exporter for additional web server monitoring
- Set up Elasticsearch SSL encryption and security hardening to secure your ELK deployment
- Configure Apache log analysis with machine learning anomaly detection for advanced threat detection
- Implement ELK stack alerting automation with PagerDuty integration for enterprise alerting
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'
# Variables
DOMAIN="${1:-localhost}"
ELASTIC_VERSION="8.x"
GOACCESS_PORT="7890"
KIBANA_PORT="5601"
# Usage
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
echo "Usage: $0 [domain_name]"
echo "Example: $0 example.com"
exit 0
fi
# Cleanup function
cleanup() {
echo -e "${RED}[ERROR] Installation failed. Cleaning up...${NC}"
systemctl stop elasticsearch logstash kibana 2>/dev/null || true
}
trap cleanup ERR
# Check if running as root
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}This script must be run as root${NC}"
exit 1
fi
# Detect distribution
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
APACHE_LOG_DIR="/var/log/apache2"
APACHE_CONFIG_DIR="/etc/apache2"
JAVA_HOME="/usr/lib/jvm/java-11-openjdk-amd64"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
APACHE_LOG_DIR="/var/log/httpd"
APACHE_CONFIG_DIR="/etc/httpd"
JAVA_HOME="/usr/lib/jvm/java-11-openjdk"
if [[ "$VERSION_ID" =~ ^7 ]]; then
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
fi
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
APACHE_LOG_DIR="/var/log/httpd"
APACHE_CONFIG_DIR="/etc/httpd"
JAVA_HOME="/usr/lib/jvm/java-11-openjdk"
;;
*)
echo -e "${RED}Unsupported distribution: $ID${NC}"
exit 1
;;
esac
else
echo -e "${RED}Cannot detect distribution${NC}"
exit 1
fi
echo -e "${GREEN}Installing Apache log analysis stack on $ID...${NC}"
# Update system
echo -e "${GREEN}[1/8] Updating system packages...${NC}"
if [[ "$PKG_MGR" == "apt" ]]; then
apt update && apt upgrade -y
$PKG_INSTALL curl wget gnupg2 software-properties-common
else
$PKG_INSTALL curl wget gnupg2
fi
# Install Java
echo -e "${GREEN}[2/8] Installing Java 11...${NC}"
if [[ "$PKG_MGR" == "apt" ]]; then
$PKG_INSTALL openjdk-11-jdk
else
$PKG_INSTALL java-11-openjdk java-11-openjdk-devel
if [[ "$ID" =~ ^(centos|rhel)$ && "$VERSION_ID" =~ ^7 ]]; then
$PKG_INSTALL epel-release
fi
fi
echo "JAVA_HOME=$JAVA_HOME" >> /etc/environment
export JAVA_HOME
# Install GoAccess
echo -e "${GREEN}[3/8] Installing GoAccess...${NC}"
if [[ "$PKG_MGR" == "apt" ]]; then
$PKG_INSTALL goaccess
else
if ! rpm -qa | grep -q epel-release; then
$PKG_INSTALL epel-release
fi
$PKG_INSTALL goaccess
fi
# Configure GoAccess
echo -e "${GREEN}[4/8] Configuring GoAccess...${NC}"
mkdir -p /var/www/html
cat > /etc/goaccess/goaccess.conf << EOF
time-format %H:%M:%S
date-format %d/%b/%Y
log-format %h %^[%d:%t %^] "%r" %s %b "%R" "%u"
real-time-html true
port $GOACCESS_PORT
ws-url wss://$DOMAIN:$GOACCESS_PORT
html-prefs {"theme":"bright","perPage":10}
html-report-title Apache Log Analysis
ignore-panel REFERRERS
ignore-panel KEYPHRASES
ignore-panel GEOLOCATION
output /var/www/html/goaccess-report.html
EOF
chown -R www-data:www-data /var/www/html 2>/dev/null || chown -R apache:apache /var/www/html
# Add Elastic repository
echo -e "${GREEN}[5/8] Adding Elastic Stack repository...${NC}"
if [[ "$PKG_MGR" == "apt" ]]; then
curl -fsSL https://artifacts.elastic.co/GPG-KEY-elasticsearch | gpg --dearmor -o /usr/share/keyrings/elastic-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/elastic-keyring.gpg] https://artifacts.elastic.co/packages/$ELASTIC_VERSION/apt stable main" > /etc/apt/sources.list.d/elastic-$ELASTIC_VERSION.list
apt update
else
rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch
cat > /etc/yum.repos.d/elastic.repo << EOF
[elasticsearch]
name=Elasticsearch repository for $ELASTIC_VERSION packages
baseurl=https://artifacts.elastic.co/packages/$ELASTIC_VERSION/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=0
autorefresh=1
type=rpm-md
EOF
fi
# Install Elasticsearch
echo -e "${GREEN}[6/8] Installing Elasticsearch...${NC}"
if [[ "$PKG_MGR" == "apt" ]]; then
$PKG_INSTALL elasticsearch
else
$PKG_MGR install -y --enablerepo=elasticsearch elasticsearch
fi
# Configure Elasticsearch
cat > /etc/elasticsearch/elasticsearch.yml << EOF
cluster.name: apache-logs
node.name: node-1
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
network.host: 127.0.0.1
http.port: 9200
discovery.type: single-node
xpack.security.enabled: false
EOF
systemctl enable elasticsearch
systemctl start elasticsearch
# Install Logstash
echo -e "${GREEN}[7/8] Installing and configuring Logstash...${NC}"
if [[ "$PKG_MGR" == "apt" ]]; then
$PKG_INSTALL logstash
else
$PKG_MGR install -y --enablerepo=elasticsearch logstash
fi
# Create Logstash configuration
cat > /etc/logstash/conf.d/apache.conf << EOF
input {
file {
path => "$APACHE_LOG_DIR/access.log"
type => "apache_access"
start_position => "beginning"
}
file {
path => "$APACHE_LOG_DIR/error.log"
type => "apache_error"
start_position => "beginning"
}
}
filter {
if [type] == "apache_access" {
grok {
match => { "message" => "%{COMBINEDAPACHELOG}" }
}
date {
match => [ "timestamp" , "dd/MMM/yyyy:HH:mm:ss Z" ]
}
mutate {
convert => { "response" => "integer" }
convert => { "bytes" => "integer" }
}
if [clientip] {
geoip {
source => "clientip"
}
}
}
if [type] == "apache_error" {
grok {
match => { "message" => "\[%{HTTPDATE:timestamp}\] \[%{WORD:level}\] \[pid %{POSINT:pid}\] %{GREEDYDATA:error_message}" }
}
}
}
output {
elasticsearch {
hosts => ["localhost:9200"]
index => "apache-logs-%{+YYYY.MM.dd}"
}
}
EOF
systemctl enable logstash
systemctl start logstash
# Install and configure Kibana
echo -e "${GREEN}[8/8] Installing and configuring Kibana...${NC}"
if [[ "$PKG_MGR" == "apt" ]]; then
$PKG_INSTALL kibana
else
$PKG_MGR install -y --enablerepo=elasticsearch kibana
fi
cat > /etc/kibana/kibana.yml << EOF
server.port: $KIBANA_PORT
server.host: "0.0.0.0"
elasticsearch.hosts: ["http://localhost:9200"]
server.name: "apache-logs-kibana"
EOF
systemctl enable kibana
systemctl start kibana
# Configure firewall
echo -e "${YELLOW}Configuring firewall...${NC}"
if command -v ufw >/dev/null 2>&1; then
ufw allow $GOACCESS_PORT
ufw allow $KIBANA_PORT
elif command -v firewall-cmd >/dev/null 2>&1; then
firewall-cmd --permanent --add-port=$GOACCESS_PORT/tcp
firewall-cmd --permanent --add-port=$KIBANA_PORT/tcp
firewall-cmd --reload
fi
# Set permissions
chown -R elasticsearch:elasticsearch /var/lib/elasticsearch /var/log/elasticsearch
chown -R logstash:logstash /etc/logstash
chown -R kibana:kibana /etc/kibana
chmod 750 /etc/elasticsearch /etc/logstash /etc/kibana
chmod 644 /etc/elasticsearch/elasticsearch.yml /etc/logstash/conf.d/apache.conf /etc/kibana/kibana.yml
# Wait for services to start
echo -e "${YELLOW}Waiting for services to start...${NC}"
sleep 30
# Verification
echo -e "${GREEN}Verifying installation...${NC}"
if systemctl is-active --quiet elasticsearch; then
echo -e "${GREEN}✓ Elasticsearch is running${NC}"
else
echo -e "${RED}✗ Elasticsearch failed to start${NC}"
fi
if systemctl is-active --quiet logstash; then
echo -e "${GREEN}✓ Logstash is running${NC}"
else
echo -e "${RED}✗ Logstash failed to start${NC}"
fi
if systemctl is-active --quiet kibana; then
echo -e "${GREEN}✓ Kibana is running${NC}"
else
echo -e "${RED}✗ Kibana failed to start${NC}"
fi
echo -e "${GREEN}Installation completed successfully!${NC}"
echo -e "${YELLOW}Access points:${NC}"
echo "- GoAccess HTML report: http://$DOMAIN/goaccess-report.html"
echo "- Kibana dashboard: http://$DOMAIN:$KIBANA_PORT"
echo ""
echo -e "${YELLOW}To generate GoAccess real-time report:${NC}"
echo "goaccess $APACHE_LOG_DIR/access.log -o /var/www/html/goaccess-report.html --log-format=COMBINED --real-time-html"
Review the script before running. Execute with: bash install.sh