Set up a production-grade ELK stack with Elasticsearch 8, Logstash 8, and Kibana 8 for centralized log management. Configure authentication, SSL encryption, and log processing pipelines across multiple data sources.
Prerequisites
- 4GB RAM minimum
- 20GB disk space
- Java 17 compatible system
- Root or sudo access
What this solves
The ELK stack (Elasticsearch, Logstash, Kibana) provides centralized logging for distributed systems, allowing you to collect, process, and visualize logs from multiple sources in one place. This tutorial sets up a production-ready ELK stack with authentication, SSL encryption, and automated log processing pipelines.
Step-by-step installation
Install Java 17 for Elasticsearch and Logstash
The ELK stack requires Java 17 to run. Install OpenJDK from your distribution's package manager.
sudo apt update
sudo apt install -y openjdk-17-jdk
Add Elastic repository and GPG key
Configure the official Elastic repository to install Elasticsearch, Logstash, and Kibana packages.
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo gpg --dearmor -o /usr/share/keyrings/elasticsearch-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/elasticsearch-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 8
Install Elasticsearch and save the automatically generated password for the elastic user.
sudo apt install -y elasticsearch
Configure Elasticsearch cluster settings
Configure Elasticsearch for a single-node cluster with proper memory allocation and network settings.
cluster.name: elk-cluster
node.name: elk-node-1
network.host: 0.0.0.0
http.port: 9200
discovery.type: single-node
xpack.security.enabled: true
xpack.security.enrollment.enabled: true
xpack.security.http.ssl:
enabled: true
keystore.path: certs/http.p12
xpack.security.transport.ssl:
enabled: true
verification_mode: certificate
keystore.path: certs/transport.p12
truststore.path: certs/transport.p12
cluster.initial_master_nodes: ["elk-node-1"]
Set JVM heap size for Elasticsearch
Configure JVM memory allocation to use half of your available RAM, with a maximum of 32GB.
-Xms2g
-Xmx2g
Start and enable Elasticsearch
Enable Elasticsearch to start on boot and start the service.
sudo systemctl daemon-reload
sudo systemctl enable --now elasticsearch
Generate SSL certificates for Elasticsearch
Create SSL certificates for secure communication between ELK stack components.
sudo /usr/share/elasticsearch/bin/elasticsearch-certutil ca --out /etc/elasticsearch/elastic-stack-ca.p12 --pass ""
sudo /usr/share/elasticsearch/bin/elasticsearch-certutil cert --ca /etc/elasticsearch/elastic-stack-ca.p12 --out /etc/elasticsearch/certs/elastic-certificates.p12 --pass ""
sudo chown -R elasticsearch:elasticsearch /etc/elasticsearch/certs/
Reset Elasticsearch passwords
Generate new passwords for built-in users including the elastic superuser.
sudo /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic --batch
Install Logstash 8
Install Logstash to process and transform log data before sending to Elasticsearch.
sudo apt install -y logstash
Configure Logstash pipeline
Create a basic Logstash pipeline that accepts syslog input and outputs to Elasticsearch.
input {
beats {
port => 5044
}
syslog {
port => 5514
}
tcp {
port => 5000
codec => json_lines
}
}
filter {
if [type] == "syslog" {
grok {
match => { "message" => "%{SYSLOGTIMESTAMP:timestamp} %{IPORHOST:host} %{DATA:program}(?:\[%{POSINT:pid}\])?: %{GREEDYDATA:message}" }
}
date {
match => [ "timestamp", "MMM d HH:mm:ss", "MMM dd HH:mm:ss" ]
}
}
if [fields][log_type] == "nginx" {
grok {
match => { "message" => "%{NGINXACCESS}" }
}
}
mutate {
remove_field => [ "host", "agent" ]
}
}
output {
elasticsearch {
hosts => ["https://localhost:9200"]
user => "elastic"
password => "${ELASTIC_PASSWORD}"
ssl => true
ssl_certificate_verification => false
index => "logs-%{+YYYY.MM.dd}"
}
stdout {
codec => rubydebug
}
}
Set Elasticsearch password for Logstash
Store the Elasticsearch password as an environment variable for Logstash.
ELASTIC_PASSWORD="your_elastic_password_here"
Configure Logstash JVM settings
Optimize Logstash memory usage for your server capacity.
-Xms1g
-Xmx1g
-XX:+UseG1GC
-XX:+UseG1GCApplicationConcurrentTime
-XX:+UnlockExperimentalVMOptions
-XX:+UseCGroupMemoryLimitForHeap
Start and enable Logstash
Enable Logstash to start on boot and start the service.
sudo systemctl enable --now logstash
Install Kibana 8
Install Kibana for visualizing and analyzing log data stored in Elasticsearch.
sudo apt install -y kibana
Generate Kibana enrollment token
Create an enrollment token to securely connect Kibana to Elasticsearch.
sudo /usr/share/elasticsearch/bin/elasticsearch-create-enrollment-token -s kibana
Configure Kibana settings
Configure Kibana to connect to Elasticsearch with SSL and proper network settings.
server.port: 5601
server.host: "0.0.0.0"
server.name: "elk-kibana"
elasticsearch.hosts: ["https://localhost:9200"]
elasticsearch.username: "kibana_system"
elasticsearch.password: "kibana_system_password"
elasticsearch.ssl.certificateAuthorities: ["/etc/kibana/certs/ca.crt"]
server.ssl.enabled: true
server.ssl.certificate: "/etc/kibana/certs/kibana.crt"
server.ssl.key: "/etc/kibana/certs/kibana.key"
xpack.security.encryptionKey: "something_at_least_32_characters_long_for_encryption"
Set up Kibana SSL certificates
Extract SSL certificates from Elasticsearch for Kibana to use.
sudo mkdir -p /etc/kibana/certs
sudo /usr/share/elasticsearch/bin/elasticsearch-certutil ca --out /tmp/elastic-stack-ca.p12 --pass ""
sudo /usr/share/elasticsearch/bin/elasticsearch-certutil cert --name kibana --ca /tmp/elastic-stack-ca.p12 --out /tmp/kibana.p12 --pass ""
sudo openssl pkcs12 -in /tmp/kibana.p12 -out /etc/kibana/certs/kibana.crt -clcerts -nokeys -passin pass:""
sudo openssl pkcs12 -in /tmp/kibana.p12 -out /etc/kibana/certs/kibana.key -nocerts -nodes -passin pass:""
sudo openssl pkcs12 -in /tmp/elastic-stack-ca.p12 -out /etc/kibana/certs/ca.crt -clcerts -nokeys -passin pass:""
sudo chown -R kibana:kibana /etc/kibana/certs/
sudo chmod 600 /etc/kibana/certs/*
Reset kibana_system user password
Generate a new password for the kibana_system user that Kibana uses to authenticate with Elasticsearch.
sudo /usr/share/elasticsearch/bin/elasticsearch-reset-password -u kibana_system --batch
Start and enable Kibana
Enable Kibana to start on boot and start the service.
sudo systemctl enable --now kibana
Configure firewall rules
Open the necessary ports for ELK stack communication.
sudo ufw allow 9200/tcp comment "Elasticsearch"
sudo ufw allow 5601/tcp comment "Kibana"
sudo ufw allow 5044/tcp comment "Logstash Beats"
sudo ufw allow 5000/tcp comment "Logstash TCP"
sudo ufw allow 5514/tcp comment "Logstash Syslog"
Configure log sources
Set up rsyslog to forward system logs to Logstash for centralized collection.
# Forward all logs to Logstash
. @@localhost:5514
sudo systemctl restart rsyslog
Verify your setup
Check that all ELK stack components are running and communicating properly.
# Check service status
sudo systemctl status elasticsearch
sudo systemctl status logstash
sudo systemctl status kibana
Test Elasticsearch API
curl -k -u elastic:your_elastic_password https://localhost:9200
Check Elasticsearch cluster health
curl -k -u elastic:your_elastic_password https://localhost:9200/_cluster/health
Test log ingestion
echo '{"message": "Test log entry", "timestamp": "'$(date -Iseconds)'"}' | nc localhost 5000
Check for indices
curl -k -u elastic:your_elastic_password https://localhost:9200/_cat/indices
Access Kibana web interface at https://your-server-ip:5601 and login with username elastic and the password you generated.
Configure index patterns and dashboards
Create index pattern in Kibana
Set up an index pattern to view your logs in Kibana.
- Open Kibana at
https://your-server-ip:5601 - Navigate to Management > Stack Management > Index Patterns
- Click "Create index pattern"
- Enter
logs-*as the index pattern - Select
@timestampas the time field - Click "Create index pattern"
Configure log retention policy
Set up Index Lifecycle Management to automatically delete old logs and manage storage.
curl -k -u elastic:your_elastic_password -X PUT "https://localhost:9200/_ilm/policy/logs-policy" -H "Content-Type: application/json" -d'
{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": {
"max_size": "10GB",
"max_age": "7d"
},
"set_priority": {
"priority": 100
}
}
},
"warm": {
"min_age": "7d",
"actions": {
"set_priority": {
"priority": 50
},
"allocate": {
"number_of_replicas": 0
}
}
},
"delete": {
"min_age": "30d",
"actions": {
"delete": {}
}
}
}
}
}'
Set up centralized log collection
Install Filebeat on client servers
Install Filebeat on servers you want to collect logs from.
sudo apt install -y filebeat
Configure Filebeat
Configure Filebeat to collect system and application logs and send them to Logstash.
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/*.log
- /var/log/syslog
- /var/log/auth.log
fields:
log_type: system
fields_under_root: true
- type: log
enabled: true
paths:
- /var/log/nginx/access.log
- /var/log/nginx/error.log
fields:
log_type: nginx
fields_under_root: true
output.logstash:
hosts: ["your-elk-server-ip:5044"]
logging.level: info
logging.to_files: true
logging.files:
path: /var/log/filebeat
name: filebeat
keepfiles: 7
permissions: 0644
Enable and start Filebeat
Start Filebeat to begin sending logs to your ELK stack.
sudo systemctl enable --now filebeat
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Elasticsearch won't start | Insufficient heap memory or wrong Java version | Check sudo journalctl -u elasticsearch -f and adjust heap size in jvm.options |
| Kibana shows "Kibana server not ready" | Cannot connect to Elasticsearch or wrong credentials | Verify Elasticsearch is running and check kibana_system password |
| No logs appearing in Kibana | Logstash configuration error or pipeline not running | Check sudo journalctl -u logstash -f and test pipeline with echo "test" | nc localhost 5000 |
| SSL certificate errors | Certificate path incorrect or permissions wrong | Verify certificate files exist and have correct ownership with ls -la /etc/kibana/certs/ |
| High memory usage | JVM heap size too large for system RAM | Reduce heap size to max 50% of system RAM in jvm.options files |
| Filebeat connection refused | Logstash not listening on beats port or firewall blocking | Test with telnet elk-server-ip 5044 and check firewall rules |
Security hardening
Create custom Kibana user
Create a dedicated user for Kibana access instead of using the elastic superuser.
curl -k -u elastic:your_elastic_password -X POST "https://localhost:9200/_security/user/kibana_admin" -H "Content-Type: application/json" -d'
{
"password": "secure_kibana_password",
"roles": ["kibana_admin", "monitoring_user"],
"full_name": "Kibana Administrator"
}'
Configure IP restrictions
Restrict access to ELK stack components by IP address.
# Allow only specific IP ranges
sudo ufw delete allow 5601/tcp
sudo ufw allow from 203.0.113.0/24 to any port 5601
sudo ufw delete allow 9200/tcp
sudo ufw allow from 203.0.113.0/24 to any port 9200
Performance optimization
Configure Elasticsearch indices optimization
Optimize index settings for better search performance and storage efficiency.
curl -k -u elastic:your_elastic_password -X PUT "https://localhost:9200/_template/logs-template" -H "Content-Type: application/json" -d'
{
"index_patterns": ["logs-*"],
"settings": {
"number_of_shards": 2,
"number_of_replicas": 0,
"refresh_interval": "30s",
"index.codec": "best_compression"
},
"mappings": {
"properties": {
"@timestamp": {
"type": "date"
},
"message": {
"type": "text",
"analyzer": "standard"
},
"level": {
"type": "keyword"
}
}
}
}'
This ELK stack setup provides a solid foundation for centralized logging. You can extend it by adding more data sources, creating custom dashboards, and implementing alerting rules. For integration with existing monitoring systems, consider configuring rsyslog for additional log forwarding or implementing security hardening measures to protect your logging infrastructure.
Next steps
- Configure automated backups with S3 storage for disaster recovery
- Set up hot-warm-cold architecture for cost optimization
- Implement cross-cluster replication for high availability
- Configure alerting with Watcher for proactive monitoring
- Add distributed tracing with OpenTelemetry integration
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'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Global variables
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ELASTIC_PASSWORD=""
KIBANA_TOKEN=""
# Print colored output
print_status() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
# Usage function
usage() {
echo "Usage: $0 [OPTIONS]"
echo "Install and configure ELK Stack (Elasticsearch 8, Logstash 8, Kibana 8)"
echo ""
echo "Options:"
echo " -h, --help Show this help message"
echo ""
echo "Example:"
echo " sudo $0"
exit 1
}
# Cleanup function
cleanup() {
print_error "Installation failed. Cleaning up..."
systemctl stop elasticsearch kibana logstash 2>/dev/null || true
systemctl disable elasticsearch kibana logstash 2>/dev/null || true
exit 1
}
# Set up error handling
trap cleanup ERR
# Check if running as root
check_root() {
if [[ $EUID -ne 0 ]]; then
print_error "This script must be run as root or with sudo"
exit 1
fi
}
# Detect Linux distribution
detect_distro() {
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"
FIREWALL_CMD="ufw"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
FIREWALL_CMD="firewall-cmd"
# Check if dnf exists, fallback to yum
if ! command -v dnf &> /dev/null; then
PKG_MGR="yum"
PKG_UPDATE="yum update -y"
PKG_INSTALL="yum install -y"
fi
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum update -y"
PKG_INSTALL="yum install -y"
FIREWALL_CMD="firewall-cmd"
;;
*)
print_error "Unsupported distribution: $ID"
exit 1
;;
esac
else
print_error "Cannot detect Linux distribution"
exit 1
fi
print_success "Detected distribution: $ID"
}
# Parse command line arguments
parse_args() {
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
usage
;;
*)
print_error "Unknown option: $1"
usage
;;
esac
done
}
# Install Java 17
install_java() {
print_status "[1/8] Installing Java 17..."
if command -v java &> /dev/null; then
JAVA_VERSION=$(java -version 2>&1 | head -n1 | cut -d'"' -f2 | cut -d'.' -f1)
if [[ "$JAVA_VERSION" == "17" ]]; then
print_success "Java 17 already installed"
return
fi
fi
case "$PKG_MGR" in
apt)
$PKG_INSTALL openjdk-17-jdk
;;
dnf|yum)
$PKG_INSTALL java-17-openjdk java-17-openjdk-devel
;;
esac
print_success "Java 17 installed successfully"
}
# Add Elastic repository
add_elastic_repo() {
print_status "[2/8] Adding Elastic repository..."
case "$PKG_MGR" in
apt)
# Install required packages
$PKG_INSTALL wget gnupg
# Add GPG key
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | gpg --dearmor -o /usr/share/keyrings/elasticsearch-keyring.gpg
# Add repository
echo "deb [signed-by=/usr/share/keyrings/elasticsearch-keyring.gpg] https://artifacts.elastic.co/packages/8.x/apt stable main" > /etc/apt/sources.list.d/elastic-8.x.list
$PKG_UPDATE
;;
dnf|yum)
# Import GPG key
rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch
# Add repository
cat > /etc/yum.repos.d/elasticsearch.repo << EOF
[elasticsearch]
name=Elasticsearch repository for 8.x packages
baseurl=https://artifacts.elastic.co/packages/8.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md
EOF
;;
esac
print_success "Elastic repository added successfully"
}
# Install Elasticsearch
install_elasticsearch() {
print_status "[3/8] Installing Elasticsearch..."
$PKG_INSTALL elasticsearch
# Enable and start Elasticsearch
systemctl daemon-reload
systemctl enable elasticsearch
systemctl start elasticsearch
# Wait for Elasticsearch to start and get the elastic password
sleep 10
# Reset elastic password and capture it
ELASTIC_PASSWORD=$(echo "y" | /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic -s 2>/dev/null)
print_success "Elasticsearch installed successfully"
print_status "Elastic user password: $ELASTIC_PASSWORD"
}
# Install and configure Kibana
install_kibana() {
print_status "[4/8] Installing and configuring Kibana..."
$PKG_INSTALL kibana
# Generate enrollment token for Kibana
KIBANA_TOKEN=$(/usr/share/elasticsearch/bin/elasticsearch-create-enrollment-token -s kibana)
# Configure Kibana with enrollment token
echo "$KIBANA_TOKEN" | /usr/share/kibana/bin/kibana-setup --enrollment-token
# Configure Kibana
cat > /etc/kibana/kibana.yml << EOF
server.host: "0.0.0.0"
server.port: 5601
elasticsearch.hosts: ["https://localhost:9200"]
EOF
chown root:kibana /etc/kibana/kibana.yml
chmod 644 /etc/kibana/kibana.yml
# Enable and start Kibana
systemctl daemon-reload
systemctl enable kibana
systemctl start kibana
print_success "Kibana installed and configured successfully"
}
# Install and configure Logstash
install_logstash() {
print_status "[5/8] Installing and configuring Logstash..."
$PKG_INSTALL logstash
# Create basic Logstash configuration
mkdir -p /etc/logstash/conf.d
cat > /etc/logstash/conf.d/01-beats-input.conf << EOF
input {
beats {
port => 5044
}
}
EOF
cat > /etc/logstash/conf.d/02-syslog-filter.conf << EOF
filter {
if [fileset][module] == "system" {
if [fileset][name] == "auth" {
grok {
match => { "message" => ["%{SYSLOGTIMESTAMP:[system][auth][timestamp]} %{IPORHOST:[system][auth][hostname]} sshd(?:\[%{POSINT:[system][auth][pid]}\])?: %{DATA:[system][auth][ssh][event]} %{DATA:[system][auth][ssh][method]} for (invalid user )?%{DATA:[system][auth][user]} from %{IPORHOST:[system][auth][ssh][ip]} port %{INT:[system][auth][ssh][port]} ssh2(: %{GREEDYDATA:[system][auth][ssh][signature]})?"] }
remove_field => "message"
}
}
}
}
EOF
cat > /etc/logstash/conf.d/30-elasticsearch-output.conf << EOF
output {
elasticsearch {
hosts => ["localhost:9200"]
manage_template => false
index => "%{[@metadata][beat]}-%{[@metadata][version]}-%{+YYYY.MM.dd}"
user => "elastic"
password => "$ELASTIC_PASSWORD"
ssl => true
ssl_certificate_verification => false
}
}
EOF
chown -R root:logstash /etc/logstash/
chmod 644 /etc/logstash/conf.d/*.conf
# Enable and start Logstash
systemctl daemon-reload
systemctl enable logstash
systemctl start logstash
print_success "Logstash installed and configured successfully"
}
# Configure firewall
configure_firewall() {
print_status "[6/8] Configuring firewall..."
case "$FIREWALL_CMD" in
ufw)
if command -v ufw &> /dev/null; then
ufw allow 9200/tcp comment "Elasticsearch"
ufw allow 5601/tcp comment "Kibana"
ufw allow 5044/tcp comment "Logstash Beats"
print_success "UFW rules added"
fi
;;
firewall-cmd)
if command -v firewall-cmd &> /dev/null && systemctl is-active firewalld &> /dev/null; then
firewall-cmd --permanent --add-port=9200/tcp
firewall-cmd --permanent --add-port=5601/tcp
firewall-cmd --permanent --add-port=5044/tcp
firewall-cmd --reload
print_success "Firewalld rules added"
fi
;;
esac
}
# Verify installation
verify_installation() {
print_status "[7/8] Verifying installation..."
# Check if services are running
for service in elasticsearch kibana logstash; do
if systemctl is-active --quiet "$service"; then
print_success "$service is running"
else
print_error "$service is not running"
return 1
fi
done
# Test Elasticsearch
sleep 5
if curl -k -u "elastic:$ELASTIC_PASSWORD" https://localhost:9200 &> /dev/null; then
print_success "Elasticsearch is responding"
else
print_warning "Elasticsearch may still be starting up"
fi
print_success "All services verified"
}
# Print final information
print_final_info() {
print_status "[8/8] Installation completed!"
echo ""
print_success "ELK Stack has been installed successfully!"
echo ""
echo "Access Information:"
echo " Kibana Web UI: http://$(hostname -I | awk '{print $1}'):5601"
echo " Elasticsearch: https://$(hostname -I | awk '{print $1}'):9200"
echo " Logstash Beats input: $(hostname -I | awk '{print $1}'):5044"
echo ""
echo "Credentials:"
echo " Username: elastic"
echo " Password: $ELASTIC_PASSWORD"
echo ""
echo "Important files:"
echo " Elasticsearch config: /etc/elasticsearch/elasticsearch.yml"
echo " Kibana config: /etc/kibana/kibana.yml"
echo " Logstash config: /etc/logstash/conf.d/"
echo ""
print_warning "Please save the elastic password in a secure location!"
}
# Main function
main() {
parse_args "$@"
check_root
detect_distro
$PKG_UPDATE
install_java
add_elastic_repo
install_elasticsearch
install_kibana
install_logstash
configure_firewall
verify_installation
print_final_info
print_success "ELK Stack installation completed successfully!"
}
main "$@"
Review the script before running. Execute with: bash install.sh