Configure high-performance caching with Redis integration, implement microcaching strategies, and optimize Lua scripts for memory management in production OpenResty deployments.
Prerequisites
- Root or sudo access
- Basic nginx/OpenResty knowledge
- Redis server
- Understanding of Lua scripting
What this solves
OpenResty combines nginx with LuaJIT to create a powerful web platform, but default configurations leave significant performance on the table. This tutorial shows you how to implement Redis-backed caching, configure advanced HTTP microcaching, and optimize Lua scripts for memory efficiency in high-traffic production environments.
Step-by-step configuration
Install OpenResty with performance modules
Install OpenResty with Redis and caching modules enabled for maximum performance capabilities.
wget -qO - https://openresty.org/package/pubkey.gpg | sudo apt-key add -
echo "deb http://openresty.org/package/ubuntu $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/openresty.list
sudo apt update
sudo apt install -y openresty openresty-opm redis-server luarocks
Install Lua Redis and caching libraries
Install essential Lua libraries for Redis connectivity and advanced caching functionality.
sudo /usr/local/openresty/bin/opm get ledgetech/lua-resty-redis-connector
sudo /usr/local/openresty/bin/opm get openresty/lua-resty-redis
sudo /usr/local/openresty/bin/opm get openresty/lua-resty-lrucache
sudo /usr/local/openresty/bin/opm get bungle/lua-resty-template
sudo luarocks install lua-resty-http
Configure Redis for OpenResty caching
Optimize Redis configuration for high-performance caching with OpenResty integration.
maxmemory 1gb
maxmemory-policy allkeys-lru
save ""
tcp-keepalive 300
timeout 0
tcp-backlog 511
maxclients 10000
notify-keyspace-events Ex
sudo systemctl restart redis-server
sudo systemctl enable redis-server
Create optimized OpenResty main configuration
Configure OpenResty with performance-tuned worker processes, connection pooling, and shared memory zones.
worker_processes auto;
worker_rlimit_nofile 65535;
worker_cpu_affinity auto;
error_log /var/log/openresty/error.log warn;
pid /var/run/openresty.pid;
events {
worker_connections 8192;
use epoll;
multi_accept on;
}
http {
include mime.types;
default_type application/octet-stream;
# Lua shared memory zones
lua_shared_dict cache_dict 100m;
lua_shared_dict locks 10m;
lua_shared_dict stats 10m;
# Redis connection pool
lua_socket_pool_size 100;
lua_socket_keepalive_timeout 60s;
# Performance optimizations
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 30;
keepalive_requests 1000;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript application/x-font-ttf font/opentype image/svg+xml;
# Rate limiting zones
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
include /usr/local/openresty/nginx/conf/conf.d/*.conf;
}
Create Lua Redis connection module
Build a reusable Lua module for efficient Redis connections with connection pooling and error handling.
sudo mkdir -p /usr/local/openresty/nginx/lua
local redis = require "resty.redis"
local cjson = require "cjson"
local _M = {}
-- Redis connection configuration
local redis_config = {
host = "127.0.0.1",
port = 6379,
timeout = 5000,
pool_size = 100,
keepalive = 60000
}
function _M.connect()
local red = redis:new()
red:set_timeout(redis_config.timeout)
local ok, err = red:connect(redis_config.host, redis_config.port)
if not ok then
ngx.log(ngx.ERR, "Failed to connect to Redis: ", err)
return nil, err
end
return red
end
function _M.set_keepalive(red)
if not red then
return
end
local ok, err = red:set_keepalive(redis_config.keepalive, redis_config.pool_size)
if not ok then
ngx.log(ngx.ERR, "Failed to set Redis keepalive: ", err)
end
end
function _M.get_cached(key)
local red, err = _M.connect()
if not red then
return nil, err
end
local res, err = red:get(key)
_M.set_keepalive(red)
if not res or res == ngx.null then
return nil, "cache miss"
end
local data = cjson.decode(res)
return data, nil
end
function _M.set_cached(key, value, ttl)
local red, err = _M.connect()
if not red then
return false, err
end
ttl = ttl or 3600
local json_value = cjson.encode(value)
local ok, err = red:setex(key, ttl, json_value)
_M.set_keepalive(red)
if not ok then
ngx.log(ngx.ERR, "Failed to cache data: ", err)
return false, err
end
return true, nil
end
function _M.invalidate(pattern)
local red, err = _M.connect()
if not red then
return false, err
end
local keys, err = red:keys(pattern)
if keys and #keys > 0 then
red:del(unpack(keys))
end
_M.set_keepalive(red)
return true, nil
end
return _M
Implement advanced microcaching strategy
Create a multi-layer caching system with shared memory, Redis, and intelligent cache warming.
local lrucache = require "resty.lrucache"
local redis_connector = require "redis_connector"
local cjson = require "cjson"
local _M = {}
-- L1 cache (in-memory LRU)
local l1_cache = lrucache.new(1000)
-- Cache configuration
local cache_config = {
l1_ttl = 60, -- 1 minute L1 cache
l2_ttl = 3600, -- 1 hour Redis cache
l3_ttl = 86400, -- 24 hour fallback cache
warm_threshold = 300 -- Warm cache when TTL < 5 minutes
}
function _M.generate_key(uri, args)
local key_parts = {"page", uri}
if args then
for k, v in pairs(args) do
if k ~= "_" then -- Skip cache buster
table.insert(key_parts, k .. "=" .. v)
end
end
end
return table.concat(key_parts, ":")
end
function _M.get_content(key)
-- Try L1 cache first
local l1_data = l1_cache:get(key)
if l1_data then
ngx.header["X-Cache-Status"] = "HIT-L1"
return l1_data.content, l1_data.headers
end
-- Try Redis cache
local l2_data, err = redis_connector.get_cached(key)
if l2_data then
-- Store in L1 cache
l1_cache:set(key, l2_data, cache_config.l1_ttl)
ngx.header["X-Cache-Status"] = "HIT-L2"
-- Check if we need to warm the cache
if l2_data.expires and (l2_data.expires - ngx.time()) < cache_config.warm_threshold then
_M.warm_cache_async(key)
end
return l2_data.content, l2_data.headers
end
ngx.header["X-Cache-Status"] = "MISS"
return nil, nil
end
function _M.store_content(key, content, headers, ttl)
ttl = ttl or cache_config.l2_ttl
local cache_data = {
content = content,
headers = headers or {},
created = ngx.time(),
expires = ngx.time() + ttl
}
-- Store in L1 cache
l1_cache:set(key, cache_data, cache_config.l1_ttl)
-- Store in Redis
redis_connector.set_cached(key, cache_data, ttl)
end
function _M.warm_cache_async(key)
-- Background cache warming using ngx.timer
local ok, err = ngx.timer.at(0, function()
ngx.log(ngx.INFO, "Warming cache for key: ", key)
-- Here you would regenerate the content
-- This is a placeholder for your content generation logic
end)
if not ok then
ngx.log(ngx.ERR, "Failed to create cache warming timer: ", err)
end
end
function _M.invalidate_pattern(pattern)
-- Clear L1 cache (simple approach - clear all)
l1_cache:flush_all()
-- Clear Redis pattern
return redis_connector.invalidate(pattern)
end
function _M.get_stats()
local stats_key = "cache_stats"
local stats = ngx.shared.stats:get(stats_key) or {
hits = 0,
misses = 0,
stores = 0
}
return stats
end
function _M.increment_stat(stat_name)
local stats_key = "cache_stats"
local stats = _M.get_stats()
stats[stat_name] = (stats[stat_name] or 0) + 1
ngx.shared.stats:set(stats_key, stats)
end
return _M
Configure virtual host with advanced caching
Set up a virtual host that implements the caching strategy with content-specific TTL and intelligent invalidation.
server {
listen 80;
server_name example.com;
access_log /var/log/openresty/access.log;
error_log /var/log/openresty/error.log;
# Rate limiting
limit_req zone=api burst=20 nodelay;
# Security headers
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
location / {
# Lua caching logic
access_by_lua_block {
local cache_manager = require "cache_manager"
local key = cache_manager.generate_key(ngx.var.uri, ngx.req.get_uri_args())
local content, headers = cache_manager.get_content(key)
if content then
-- Set cached headers
if headers then
for k, v in pairs(headers) do
ngx.header[k] = v
end
end
cache_manager.increment_stat("hits")
ngx.say(content)
ngx.exit(200)
else
cache_manager.increment_stat("misses")
ngx.ctx.cache_key = key
end
}
# Your application backend
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Cache response from backend
header_filter_by_lua_block {
if ngx.status == 200 then
ngx.ctx.should_cache = true
ngx.ctx.response_headers = ngx.resp.get_headers()
end
}
body_filter_by_lua_block {
if ngx.ctx.should_cache and not ngx.ctx.cached then
local cache_manager = require "cache_manager"
local body = ngx.arg[1]
if ngx.arg[2] then -- Last chunk
ngx.ctx.cached = true
-- Determine TTL based on content type
local ttl = 3600 -- default 1 hour
local content_type = ngx.header.content_type or ""
if string.match(content_type, "application/json") then
ttl = 300 -- 5 minutes for API responses
elseif string.match(content_type, "text/html") then
ttl = 1800 -- 30 minutes for HTML
elseif string.match(content_type, "image/") then
ttl = 86400 -- 24 hours for images
end
cache_manager.store_content(ngx.ctx.cache_key, body, ngx.ctx.response_headers, ttl)
cache_manager.increment_stat("stores")
end
end
}
}
# Cache invalidation endpoint
location ~ ^/admin/cache/invalidate/(.+)$ {
access_by_lua_block {
-- Simple IP-based access control
local allowed_ips = {"127.0.0.1", "203.0.113.10"}
local client_ip = ngx.var.remote_addr
local allowed = false
for _, ip in ipairs(allowed_ips) do
if ip == client_ip then
allowed = true
break
end
end
if not allowed then
ngx.status = 403
ngx.say("Forbidden")
ngx.exit(403)
end
local cache_manager = require "cache_manager"
local pattern = ngx.var[1]
local ok, err = cache_manager.invalidate_pattern(pattern)
if ok then
ngx.say('Cache invalidated for pattern: ' .. pattern)
else
ngx.status = 500
ngx.say('Error invalidating cache: ' .. (err or 'unknown error'))
end
ngx.exit(ngx.status)
}
}
# Cache statistics endpoint
location /admin/cache/stats {
access_by_lua_block {
local cache_manager = require "cache_manager"
local stats = cache_manager.get_stats()
ngx.header.content_type = "application/json"
local cjson = require "cjson"
ngx.say(cjson.encode(stats))
ngx.exit(200)
}
}
}
Optimize Lua memory management
Configure LuaJIT for optimal memory usage and garbage collection tuning for high-performance applications.
local _M = {}
-- Memory optimization settings
local gc_config = {
gc_step_size = 200,
gc_threshold = 1024 * 1024, -- 1MB
max_string_cache = 10000
}
function _M.init_worker()
-- Set LuaJIT memory limits
if jit then
jit.opt.start("maxtrace=10000", "maxrecord=20000", "minstitch=3")
end
-- Initialize periodic GC
local ok, err = ngx.timer.every(30, _M.periodic_gc)
if not ok then
ngx.log(ngx.ERR, "Failed to create GC timer: ", err)
end
end
function _M.periodic_gc()
local before = collectgarbage("count")
collectgarbage("step", gc_config.gc_step_size)
local after = collectgarbage("count")
if before > gc_config.gc_threshold then
collectgarbage("collect")
ngx.log(ngx.INFO, "Full GC executed, freed: ", (before - after), "KB")
end
end
function _M.optimize_table(t)
-- Pre-allocate table if we know the size
if type(t) == "table" then
local count = 0
for _ in pairs(t) do
count = count + 1
end
if count > 100 then
-- For large tables, consider using weak references
local mt = getmetatable(t) or {}
mt.__mode = "v" -- Weak values
setmetatable(t, mt)
end
end
return t
end
function _M.string_intern(str)
-- Simple string interning to reduce memory usage
local cache = ngx.shared.cache_dict
local cached_str = cache:get("str_" .. str)
if cached_str then
return cached_str
end
-- Only cache small strings to avoid memory bloat
if #str < 1000 then
cache:set("str_" .. str, str, 3600)
end
return str
end
function _M.pool_objects()
-- Object pooling for frequently created/destroyed objects
local pool = {}
return {
get = function()
local obj = table.remove(pool)
if not obj then
obj = {}
end
return obj
end,
put = function(obj)
-- Clear object data
for k in pairs(obj) do
obj[k] = nil
end
-- Return to pool if not too large
if #pool < 100 then
table.insert(pool, obj)
end
end
}
end
return _M
Create initialization script
Set up the init_worker_by_lua block to initialize memory optimization and connection pools.
-- Initialize memory optimizer
local memory_optimizer = require "memory_optimizer"
memory_optimizer.init_worker()
-- Pre-compile templates if using lua-resty-template
local template = require "resty.template"
template.caching(true)
template.load_lua_template = true
-- Initialize shared dictionaries
local cache_dict = ngx.shared.cache_dict
local stats = ngx.shared.stats
-- Set up global error handling
_G.handle_error = function(err)
ngx.log(ngx.ERR, "Lua error: ", err)
ngx.status = 500
ngx.say("Internal Server Error")
ngx.exit(500)
end
-- Initialize Redis connection pool
local redis_connector = require "redis_connector"
local red = redis_connector.connect()
if red then
redis_connector.set_keepalive(red)
ngx.log(ngx.INFO, "Redis connection pool initialized")
else
ngx.log(ngx.WARN, "Failed to initialize Redis connection pool")
end
Update main nginx configuration
Add the initialization script and performance directives to the main configuration.
worker_processes auto;
worker_rlimit_nofile 65535;
worker_cpu_affinity auto;
Add to existing config
init_worker_by_lua_file /usr/local/openresty/nginx/conf/init.lua;
Lua package path
lua_package_path '/usr/local/openresty/nginx/lua/?.lua;;';
Lua code cache (disable only in development)
lua_code_cache on;
DNS resolver for dynamic backends
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
Rest of existing configuration...
Enable and start services
Start OpenResty and Redis with the optimized configuration.
sudo systemctl enable openresty
sudo systemctl restart openresty
sudo systemctl status openresty
Verify your setup
Test the caching system and performance optimizations to ensure everything works correctly.
# Test basic functionality
curl -H "Host: example.com" http://localhost/
Check cache headers
curl -I -H "Host: example.com" http://localhost/
Test cache statistics
curl http://localhost/admin/cache/stats
Test cache invalidation
curl http://localhost/admin/cache/invalidate/page:*
Check Redis connection
redis-cli ping
Monitor OpenResty error log
sudo tail -f /var/log/openresty/error.log
ab or wrk to load test your cached endpoints. You should see significantly improved response times and reduced backend load with cache hit rates above 90%.Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Lua module not found | Incorrect lua_package_path | Check path in nginx.conf and verify module location |
| Redis connection failures | Redis not running or connection pool exhausted | sudo systemctl restart redis-server and check connection limits |
| Memory usage growing continuously | Memory leaks in Lua code | Enable lua_code_cache off temporarily and check for circular references |
| Cache not working | Shared memory zones too small | Increase shared dict sizes in nginx.conf |
| High CPU usage | LuaJIT optimization disabled | Ensure lua_code_cache on in production |
| 502 Bad Gateway | Lua runtime errors | Check error log: tail -f /var/log/openresty/error.log |
Next steps
- Configure OpenResty load balancing with upstream health checks for high availability
- Implement OpenResty rate limiting and API protection for security
- Configure NGINX monitoring with Prometheus for observability
- Set up OpenResty SSL termination with Let's Encrypt
- Implement OpenResty WebSocket load balancing
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 configuration
REDIS_MAXMEM="${REDIS_MAXMEM:-1gb}"
DOMAIN="${1:-example.com}"
# Usage message
usage() {
echo "Usage: $0 [DOMAIN]"
echo "Example: $0 api.example.com"
exit 1
}
# Logging functions
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# Cleanup on failure
cleanup() {
log_error "Installation failed. Cleaning up..."
systemctl stop openresty 2>/dev/null || true
systemctl stop redis-server redis 2>/dev/null || true
}
trap cleanup ERR
# Check prerequisites
if [[ $EUID -ne 0 ]]; then
log_error "This script must be run as root"
exit 1
fi
if [[ $# -gt 1 ]]; then
usage
fi
# Auto-detect distribution
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
PKG_UPDATE="apt update"
REDIS_SERVICE="redis-server"
REDIS_CONFIG="/etc/redis/redis.conf"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf makecache"
REDIS_SERVICE="redis"
REDIS_CONFIG="/etc/redis.conf"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PKG_UPDATE="yum makecache"
REDIS_SERVICE="redis"
REDIS_CONFIG="/etc/redis.conf"
;;
*)
log_error "Unsupported distro: $ID"
exit 1
;;
esac
else
log_error "Cannot detect distribution"
exit 1
fi
log_info "[1/8] Installing OpenResty and dependencies..."
$PKG_UPDATE
if [[ "$PKG_MGR" == "apt" ]]; then
wget -qO - https://openresty.org/package/pubkey.gpg | apt-key add -
echo "deb http://openresty.org/package/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/openresty.list
$PKG_UPDATE
$PKG_INSTALL openresty openresty-opm redis-server luarocks curl
else
$PKG_INSTALL dnf-plugins-core
dnf config-manager --add-repo https://openresty.org/package/centos/openresty.repo
$PKG_INSTALL openresty openresty-opm redis luarocks curl
fi
log_info "[2/8] Installing Lua Redis and caching libraries..."
/usr/local/openresty/bin/opm get ledgetech/lua-resty-redis-connector
/usr/local/openresty/bin/opm get openresty/lua-resty-redis
/usr/local/openresty/bin/opm get openresty/lua-resty-lrucache
/usr/local/openresty/bin/opm get bungle/lua-resty-template
luarocks install lua-resty-http
log_info "[3/8] Configuring Redis for high performance..."
cp "$REDIS_CONFIG" "${REDIS_CONFIG}.backup"
cat > "$REDIS_CONFIG" << 'EOF'
maxmemory 1gb
maxmemory-policy allkeys-lru
save ""
tcp-keepalive 300
timeout 0
tcp-backlog 511
maxclients 10000
notify-keyspace-events Ex
bind 127.0.0.1
port 6379
daemonize yes
EOF
log_info "[4/8] Creating OpenResty directories and configuration..."
mkdir -p /usr/local/openresty/nginx/conf/conf.d
mkdir -p /usr/local/openresty/nginx/lua
mkdir -p /var/log/openresty
chown -R nobody:nogroup /var/log/openresty 2>/dev/null || chown -R nobody:nobody /var/log/openresty
cat > /usr/local/openresty/nginx/conf/nginx.conf << 'EOF'
worker_processes auto;
worker_rlimit_nofile 65535;
worker_cpu_affinity auto;
error_log /var/log/openresty/error.log warn;
pid /var/run/openresty.pid;
events {
worker_connections 8192;
use epoll;
multi_accept on;
}
http {
include mime.types;
default_type application/octet-stream;
lua_shared_dict cache_dict 100m;
lua_shared_dict locks 10m;
lua_shared_dict stats 10m;
lua_socket_pool_size 100;
lua_socket_keepalive_timeout 60s;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 30;
keepalive_requests 1000;
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript application/x-font-ttf font/opentype image/svg+xml;
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
include /usr/local/openresty/nginx/conf/conf.d/*.conf;
}
EOF
log_info "[5/8] Creating Lua Redis connection module..."
cat > /usr/local/openresty/nginx/lua/redis_connector.lua << 'EOF'
local redis = require "resty.redis"
local cjson = require "cjson"
local _M = {}
local redis_config = {
host = "127.0.0.1",
port = 6379,
timeout = 5000,
pool_size = 100,
keepalive = 60000
}
function _M.connect()
local red = redis:new()
red:set_timeout(redis_config.timeout)
local ok, err = red:connect(redis_config.host, redis_config.port)
if not ok then
ngx.log(ngx.ERR, "Failed to connect to Redis: ", err)
return nil, err
end
return red
end
function _M.set_keepalive(red)
if not red then
return
end
local ok, err = red:set_keepalive(redis_config.keepalive, redis_config.pool_size)
if not ok then
ngx.log(ngx.ERR, "Failed to set keepalive: ", err)
end
end
function _M.get_cached(key, ttl, callback)
local red, err = _M.connect()
if not red then
return callback()
end
local cached, err = red:get(key)
_M.set_keepalive(red)
if cached and cached ~= ngx.null then
return cjson.decode(cached)
end
local data = callback()
if data then
local red2, err = _M.connect()
if red2 then
red2:setex(key, ttl or 300, cjson.encode(data))
_M.set_keepalive(red2)
end
end
return data
end
return _M
EOF
log_info "[6/8] Creating sample virtual host configuration..."
cat > /usr/local/openresty/nginx/conf/conf.d/default.conf << EOF
server {
listen 80;
server_name $DOMAIN;
location /api/ {
limit_req zone=api burst=20 nodelay;
content_by_lua_block {
local redis_connector = require "redis_connector"
local cache_key = "api:" .. ngx.var.request_uri
local result = redis_connector.get_cached(cache_key, 300, function()
return {message = "Hello from cached API", timestamp = ngx.time()}
end)
ngx.header.content_type = "application/json"
ngx.say(require("cjson").encode(result))
}
}
location /health {
access_log off;
return 200 "OK\n";
add_header Content-Type text/plain;
}
location / {
return 200 "OpenResty with Redis caching is running";
add_header Content-Type text/plain;
}
}
EOF
log_info "[7/8] Setting permissions and starting services..."
chmod 644 /usr/local/openresty/nginx/conf/nginx.conf
chmod 644 /usr/local/openresty/nginx/conf/conf.d/default.conf
chmod 644 /usr/local/openresty/nginx/lua/redis_connector.lua
chmod 644 "$REDIS_CONFIG"
systemctl enable $REDIS_SERVICE
systemctl start $REDIS_SERVICE
systemctl enable openresty
systemctl start openresty
log_info "[8/8] Verifying installation..."
sleep 2
if ! systemctl is-active --quiet $REDIS_SERVICE; then
log_error "Redis is not running"
exit 1
fi
if ! systemctl is-active --quiet openresty; then
log_error "OpenResty is not running"
exit 1
fi
# Test Redis connection
if ! redis-cli ping >/dev/null 2>&1; then
log_error "Cannot connect to Redis"
exit 1
fi
# Test OpenResty
if ! curl -s http://localhost/health | grep -q "OK"; then
log_error "OpenResty health check failed"
exit 1
fi
log_info "Installation completed successfully!"
echo
log_info "Services status:"
echo "Redis: $(systemctl is-active $REDIS_SERVICE)"
echo "OpenResty: $(systemctl is-active openresty)"
echo
log_info "Test URLs:"
echo "Health check: http://$DOMAIN/health"
echo "Cached API: http://$DOMAIN/api/test"
echo
log_info "Configuration files:"
echo "OpenResty: /usr/local/openresty/nginx/conf/nginx.conf"
echo "Virtual host: /usr/local/openresty/nginx/conf/conf.d/default.conf"
echo "Redis config: $REDIS_CONFIG"
echo "Lua modules: /usr/local/openresty/nginx/lua/"
Review the script before running. Execute with: bash install.sh