App_servers
Chapter 70: Application Servers
Section titled “Chapter 70: Application Servers”Overview
Section titled “Overview”Application servers are runtime environments that execute application code and provide services like connection pooling, session management, and security. This chapter covers major application servers used in production: Tomcat and WildFly for Java, Gunicorn and uWSGI for Python, and PM2 for Node.js. Understanding application server configuration, deployment, and optimization is essential for DevOps and SRE engineers managing web applications.
70.1 Tomcat (Java Application Server)
Section titled “70.1 Tomcat (Java Application Server)”Tomcat Architecture
Section titled “Tomcat Architecture”┌─────────────────────────────────────────────────────────────────────────┐│ TOMCAT ARCHITECTURE │├─────────────────────────────────────────────────────────────────────────┤│ ││ ┌─────────────────────────────────────────────────────────────────┐ ││ │ Tomcat Server │ ││ │ ┌─────────────────────────────────────────────────────────┐ │ ││ │ │ Service (Catalina) │ │ ││ │ ├─────────────────────────────────────────────────────────┤ │ ││ │ │ │ │ ││ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ ││ │ │ │ Engine │ │ Engine │ │ Engine │ │ │ ││ │ │ │ (Catalina) │ │ (Catalina) │ │ (Catalina) │ │ │ ││ │ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │ ││ │ │ │ │ │ │ │ ││ │ │ ▼ ▼ ▼ │ │ ││ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ ││ │ │ │ Host │ │ Host │ │ Host │ │ │ ││ │ │ │ (localhost) │ │ (example) │ │ (app1) │ │ │ ││ │ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │ ││ │ │ │ │ │ │ │ ││ │ │ ▼ ▼ ▼ │ │ ││ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ ││ │ │ │ Context │ │ Context │ │ Context │ │ │ ││ │ │ │ (app1) │ │ (app2) │ │ (app3) │ │ │ ││ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ ││ │ │ │ │ ││ │ └─────────────────────────────────────────────────────────┘ │ ││ │ │ ││ └─────────────────────────────────────────────────────────────────┘ ││ ││ Components: ││ ┌─────────────────────────────────────────────────────────────────┐ ││ │ Server - Top-level container, defines single JVM │ ││ │ Service - Collection of Connectors + one Engine │ ││ │ Engine - Processes requests, Catalina │ ││ │ Host - Virtual host (domain-based) │ ││ │ Context - Web application (WAR file or directory) │ ││ │ Connector - Protocol handler (HTTP, AJP) │ ││ │ Realm - User/role database │ ││ │ Valve - Request processing component │ ││ └─────────────────────────────────────────────────────────────────┘ ││ │└─────────────────────────────────────────────────────────────────────────┘Tomcat Configuration
Section titled “Tomcat Configuration”<!-- ============================================================ --><!-- server.xml - Main Tomcat Configuration --><!-- ============================================================ -->
<Server port="8005" shutdown="SHUTDOWN">
<Service name="Catalina">
<!-- HTTP Connector --> <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" maxThreads="200" minSpareThreads="10" acceptCount="100" enableLookups="false" URIEncoding="UTF-8" compression="on" compressionMinSize="2048" noCompressionUserAgents="gozilla, traviata" compressableMimeType="text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json"/>
<!-- AJP Connector (for Apache/mod_jk) --> <!-- <Connector protocol="AJP/1.3" address="::1" port="8009" redirectPort="8443" maxThreads="200"/> -->
<!-- Engine --> <Engine name="Catalina" defaultHost="localhost">
<!-- Host for main application --> <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
<!-- Access log valve --> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b %D"/>
<!-- Error page configuration --> <Valve className="org.apache.catalina.valves.ErrorReportValve" showReport="false" showServerInfo="false"/>
<!-- Context for specific app --> <Context path="/api" docBase="/var/lib/tomcat9/webapps/api" reloadable="true" cookies="true"> <Manager pathname="" /> </Context>
</Host>
<!-- Virtual host example --> <!-- <Host name="app.example.com" appBase="/var/www/apps" unpackWARs="true" autoDeploy="true"> <Alias>www.example.com</Alias> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="/var/log/tomcat" prefix="example_access_log" suffix=".txt"/> </Host> -->
</Engine> </Service></Server>Tomcat Memory and Performance Tuning
Section titled “Tomcat Memory and Performance Tuning”# ============================================================# Tomcat Memory Configuration - setenv.sh# ============================================================
# Create or edit /etc/tomcat9/setenv.sh (Debian/Ubuntu)# or $CATALINA_HOME/bin/setenv.sh
#!/bin/bash
# Heap memory settings# For a 4GB RAM server, allocate 2-3GB for TomcatCATALINA_OPTS="$CATALINA_OPTS -Xms2g"CATALINA_OPTS="$CATALINA_OPTS -Xmx2g"
# Heap dump on OOMCATALINA_OPTS="$CATALINA_OPTS -XX:+HeapDumpOnOutOfMemoryError"CATALINA_OPTS="$CATALINA_OPTS -XX:HeapDumpPath=/var/log/tomcat/heapdump.hprof"
# G1GC (default in Java 11+, good for most workloads)CATALINA_OPTS="$CATALINA_OPTS -XX:+UseG1GC"CATALINA_OPTS="$CATALINA_OPTS -XX:MaxGCPauseMillis=200"CATALINA_OPTS="$CATALINA_OPTS -XX:+ParallelRefProcEnabled"
# GC loggingCATALINA_OPTS="$CATALINA_OPTS -Xlog:gc*:file=/var/log/tomcat/gc.log:time,uptime,level,tags:filecount=10,filesize=100m"
# JMX for monitoringCATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote"CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.port=9010"CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.ssl=false"CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.authenticate=true"
# Application settingsCATALINA_OPTS="$CATALINA_OPTS -Djava.security.egd=file:/dev/./urandom"CATALINA_OPTS="$CATALINA_OPTS -Dfile.encoding=UTF-8"CATALINA_OPTS="$CATALINA_OPTS -Duser.timezone=UTC"
# PermGen/Metaspace (Java 8 and earlier)# CATALINA_OPTS="$CATALINA_OPTS -XX:PermSize=256m"# CATALINA_OPTS="$CATALINA_OPTS -XX:MaxPermSize=512m"
# For Java 9+CATALINA_OPTS="$CATALINA_OPTS -XX:MetaspaceSize=256m"CATALINA_OPTS="$CATALINA_OPTS -XX:MaxMetaspaceSize=512m"
export CATALINA_OPTSTomcat Deployment
Section titled “Tomcat Deployment”# ============================================================# Tomcat Deployment# ============================================================
# Method 1: Deploy WAR filecp myapp.war /var/lib/tomcat9/webapps/# Tomcat auto-deploys (if autoDeploy=true)
# Method 2: Directory deploymentmkdir -p /var/lib/tomcat9/webapps/myapp# Extract WAR or deploy exploded directory# Configure context in META-INF/context.xml
# Method 3: Manager web interface# Access: http://localhost:8080/manager/html# Configure credentials in tomcat-users.xml
# Method 4: Deploy via scriptcurl -T myapp.war 'http://localhost:8080/manager/deploy?path=/myapp&update=true'
# Check deploymentls -la /var/lib/tomcat9/webapps/tail -f /var/log/tomcat9/catalina.out
# Undeployrm /var/lib/tomcat9/webapps/myapp.war# Or: curl 'http://localhost:8080/manager/undeploy?path=/myapp'70.2 Gunicorn (Python WSGI Server)
Section titled “70.2 Gunicorn (Python WSGI Server)”Gunicorn Configuration
Section titled “Gunicorn Configuration”# ============================================================# gunicorn.conf.py - Production Configuration# ============================================================
# Server socketbind = "127.0.0.1:8000"backlog = 2048
# Worker processesworkers = 4 # CPU cores * 2 + 1 (for CPU-bound)worker_class = "sync" # sync, gevent, eventlet, tornadoworkers_per_core = 2 # Workers per CPU core
# Worker settingstimeout = 120 # Request timeout in secondskeepalive = 5 # Keep-alive connectionsgraceful_timeout = 30 # Time to finish requests on shutdownmax_requests = 1000 # Restart worker after N requestsmax_requests_jitter = 50 # Random jitter for max_requests
# Loggingaccesslog = "/var/log/gunicorn/access.log"errorlog = "/var/log/gunicorn/error.log"loglevel = "info"access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(D)s'
# Process namingproc_name = "myapp"
# Server mechanicsdaemon = False # Daemonize (use systemd instead)pidfile = "/var/run/gunicorn.pid"umask = 0user = "www-data"group = "www-data"tmp_upload_dir = None
# SSL (or use nginx for SSL)# keyfile = "/etc/ssl/private/server.key"# certfile = "/etc/ssl/certs/server.crt"
# Preloading app (reduces memory usage with multiple workers)preload_app = True
# Worker hooksdef on_starting(server): """Called just before the master process is initialized.""" pass
def on_reload(server): """Called to recycle workers during a reload via SIGHUP.""" pass
def when_ready(server): """Called just after the server is started.""" pass
def pre_fork(server, worker): """Called just before a worker is forked.""" pass
def post_fork(server, worker): """Called just after a worker has been forked.""" pass
def worker_int(worker): """Called just after a worker exited on SIGINT or SIGQUIT.""" pass
def worker_abort(worker): """Called when a worker received the SIGABRT signal.""" passGunicorn Deployment
Section titled “Gunicorn Deployment”# ============================================================# Running Gunicorn# ============================================================
# Basic usagegunicorn -w 4 -b 127.0.0.1:8000 myapp:appgunicorn -w 4 -b 127.0.0.1:8000 --config gunicorn.conf.py myapp:app
# With virtual environmentsource venv/bin/activategunicorn -w 4 -b 127.0.0.1:8000 myapp:app
# Using systemd service# /etc/systemd/system/myapp.service[Unit]Description=My Python ApplicationAfter=network.target
[Service]Type=notifyUser=www-dataGroup=www-dataWorkingDirectory=/opt/myappEnvironment="PATH=/opt/myapp/venv/bin"ExecStart=/opt/myapp/venv/bin/gunicorn -c /opt/myapp/gunicorn.conf.py myapp:appExecReload=/bin/kill -s HUP $MAINPIDKillMode=mixedTimeoutStopSec=5PrivateTmp=trueRestart=on-failureRestartSec=5s
[Install]WantedBy=multi-user.target
# Start servicesudo systemctl daemon-reloadsudo systemctl enable myappsudo systemctl start myapp70.3 uWSGI (Python Application Server)
Section titled “70.3 uWSGI (Python Application Server)”uWSGI Configuration
Section titled “uWSGI Configuration”# ============================================================# uwsgi.ini - Production Configuration# ============================================================
[uwsgi]# Applicationmodule = myapp:appmaster = true
# Socketsocket = /run/uwsgi/myapp.sockchmod-socket = 660vacuum = true
# Processesprocesses = 4threads = 2cheaper = 2 # Dynamic scaling (overcommit)cheaper-overload = 60 # Seconds before adding workerscheaper-initial = 2 # Initial workerscheaper-step = 1 # Workers to add at a time
# Timeoutsharakiri = 120 # Request timeout (kill slow requests)harakiri-verbose = truevacuum = truedie-on-term = true
# Bufferingbuffer-size = 32768
# Loggingdaemonize = /var/log/uwsgi/myapp.loglog-4xx = truelog-5xx = truelog-slow = trueslowlog = /var/log/uwsgi/slow.log
# Memoryreload-on-rss = 512 # Restart worker if RSS > 512MBreload-on-as = 1024 # Restart worker if AS > 1024MB
# Persistencepy-autoreload = 0 # Disable in production
# Virtual environmenthome = /opt/myapp/venv
# Python settingspythonpath = /opt/myapppython-autoreload = 0wsgi-file = /opt/myapp/wsgi.pycallable = app
# Performancethunder-lock = true # Prevent thundering herdcheaper-algo = busyness # Scale based on busynessuWSGI Emperor Mode
Section titled “uWSGI Emperor Mode”# ============================================================# Emperor Mode - Manage Multiple Apps# ============================================================
[uwsgi]# Emperor mode - monitor vassals directoryemperor = /etc/uwsgi/vassalsemperor-type = xml
# Statsstats = /run/uwsgi/stats.sockstats-http = true
# Loggingdaemonize = /var/log/uwsgi/emperor.loglog-4xx = truelog-5xx = true
# Restart on configuration changeemperor-on-demand-dir = /run/uwsgitouch-reload = /etc/uwsgi/vassals/%( vassal )
# ============================================================# Individual app config in /etc/uwsgi/vassals/myapp.ini# ============================================================
[uwsgi]module = myapp:appsocket = /run/uwsgi/myapp.sockchmod-socket = 660processes = 4threads = 2die-on-term = truevacuum = true70.4 Node.js with PM2
Section titled “70.4 Node.js with PM2”PM2 Process Management
Section titled “PM2 Process Management”┌─────────────────────────────────────────────────────────────────────────┐│ PM2 ARCHITECTURE │├─────────────────────────────────────────────────────────────────────────┤│ ││ ┌─────────────────────────────────────────────────────────────────┐ ││ │ PM2 Daemon │ ││ │ ┌──────────────────────────────────────────────────────────┐ │ ││ │ │ Process Manager │ │ ││ │ ├──────────────────────────────────────────────────────────┤ │ ││ │ │ - Process spawning │ │ ││ │ │ - Health monitoring │ │ ││ │ │ - Log management │ │ ││ │ │ - Cluster management │ │ ││ │ │ - Deployments │ │ ││ │ │ - Auto-restart │ │ ││ │ └──────────────────────────────────────────────────────────┘ │ ││ └─────────────────────────────────────────────────────────────────┘ ││ ││ ┌─────────────────────────────────────────────────────────────────┐ ││ │ Application Instances │ ││ ├─────────────────────────────────────────────────────────────────┤ ││ │ │ ││ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ ││ │ │ Node.js │ │ Node.js │ │ Node.js │ │ Node.js │ │ ││ │ │ App │ │ App │ │ App │ │ App │ │ ││ │ │ #1 │ │ #2 │ │ #3 │ │ #4 │ │ ││ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ ││ │ │ ││ └─────────────────────────────────────────────────────────────────┘ ││ ││ Features: ││ - Load balancing (cluster mode) ││ - Zero-downtime deployments ││ - Log aggregation ││ - Metrics and monitoring ││ - Memory limit with auto-restart ││ - Container integration (Docker/Kubernetes) ││ │└─────────────────────────────────────────────────────────────────────────┘PM2 Configuration
Section titled “PM2 Configuration”// ============================================================// ecosystem.config.js - PM2 Configuration// ============================================================
module.exports = { apps: [{ name: 'myapp', script: './index.js',
// Instances: 0 = auto (CPU cores), or specific number instances: 'max',
// Execution exec_mode: 'cluster', // 'fork' or 'cluster' cwd: '/var/www/myapp',
// Environment env: { NODE_ENV: 'development', PORT: 3000 }, env_production: { NODE_ENV: 'production', PORT: 3000 },
// Arguments to script args: '--inspect=0.0.0.0:9229',
// Scaling instance_var: 'INSTANCE_ID',
// Memory limit - restart if exceeded max_memory_restart: '1G',
// Restart policy max_restarts: 10, min_uptime: '10s', restart_delay: 4000,
// Source map support source_map_support: true,
// Log files log_file: '/var/log/pm2/myapp.log', out_file: '/var/log/pm2/out.log', error_file: '/var/log/pm2/err.log', log_date_format: 'YYYY-MM-DD HH:mm:ss Z', merge_logs: true,
// Monitoring monitor: true,
// Graceful shutdown kill_timeout: 5000, wait_ready: true, listen_timeout: 3000,
// Process identification instance_name: 'myapp', env_production: { NODE_ENV: 'production' } }],
// Deploy configuration deploy: { production: { user: 'deploy', host: 'server.example.com', ref: 'origin/main', repo: 'git@github.com:user/repo.git', path: '/var/www/production', 'post-deploy': 'npm install && pm2 reload ecosystem.config.js --env production' } }}PM2 Commands
Section titled “PM2 Commands”# ============================================================# PM2 Common Commands# ============================================================
# Start applicationpm2 start ecosystem.config.jspm2 start index.js -i 4 # 4 instancespm2 start index.js --name myapp # Named
# Process managementpm2 list # List processespm2 statuspm2 info myapp # Detailed infopm2 monit # Real-time monitoringpm2 logs myapp # View logspm2 logs myapp --err # Error logs only
# Process controlpm2 stop myapp # Stoppm2 restart myapp # Restartpm2 reload myapp # Zero-downtime reloadpm2 delete myapp # Remove
# Scalingpm2 scale myapp +3 # Add 3 instancespm2 scale myapp 8 # Scale to 8 instances
# Save/restorepm2 save # Save current process listpm2 resurrect # Restore saved processes
# Startup scriptspm2 startup # Generate startup scriptpm2 startup ubuntu # For specific init system
# Cluster mode (load balancing)pm2 start index.js -i 4 # Start 4 instancespm2 reload myapp # Zero-downtime reload
# Performance monitoringpm2 perf monitor # Performance metrics
# Keymetrics integrationpm2 link <secret> <public> # Connect to keymetrics.io70.5 Application Server Best Practices
Section titled “70.5 Application Server Best Practices”Security Configuration
Section titled “Security Configuration”# ============================================================# Application Server Security Checklist# ============================================================
# 1. Run with minimal privileges# - Use dedicated user/groupuseradd -r -s /sbin/nologin tomcatchown -R tomcat:tomcat /opt/tomcat
# 2. Disable default accounts# - Change default passwords# - Remove admin/manager interfaces if not needed
# 3. Use SSL/TLS# - Configure HTTPS only# - Use strong ciphers
# 4. Rate limiting# - Configure connection limits# - Implement request throttling
# 5. Headers# - X-Frame-Options: DENY# - X-Content-Type-Options: nosniff# - X-XSS-Protection# - Content-Security-Policy
# 6. Session security# - Secure cookies# - HttpOnly flag# - Session timeout
# 7. File upload restrictions# - Limit file types# - Scan uploads for malware# - Store outside web rootPerformance Optimization
Section titled “Performance Optimization”# ============================================================# Application Server Performance Checklist# ============================================================
# 1. Connection pooling# - Configure database connection pool# - Appropriate pool size
# 2. Caching# - Enable application-level caching# - Use Redis/Memcached# - Configure HTTP caching
# 3. Compression# - Enable gzip compression# - Compress static assets
# 4. Static files# - Serve via CDN or separate server# - Use appropriate headers
# 5. Monitoring# - Application Performance Monitoring (APM)# - Custom metrics# - Health check endpoints
# 6. Resource limits# - Configure memory limits# - Set request timeouts# - Limit concurrent connections70.6 Interview Questions
Section titled “70.6 Interview Questions”┌─────────────────────────────────────────────────────────────────────────┐│ APPLICATION SERVER INTERVIEW QUESTIONS │├─────────────────────────────────────────────────────────────────────────┤ │Q1: What is the difference between Gunicorn and uWSGI? │ │A1: │| Feature | Gunicorn | uWSGI | ||----------------|----------------------|----------------------------| || Language | Python only | Multi-language (C) | || Configuration | Python/command | INI/command | || Emperor mode | No | Yes (multiple apps) | || Performance | Good | Very good | || Features | Simpler | More features | || Community | Large | Large | || Workers | Pre-fork only | Pre-fork + threads | | │Both are production-ready WSGI servers. uWSGI offers more options │for complex deployments. Gunicorn is simpler and integrates well │with Django. │ │─────────────────────────────────────────────────────────────────────────┤ │Q2: How do you configure Tomcat memory settings? │ │A2: │Set in setenv.sh or CATALINA_OPTS: │- -Xms: Initial heap size (e.g., -Xms2g) │- -Xmx: Maximum heap size (e.g., -Xmx2g) │- -XX:+UseG1GC: G1 garbage collector │- -XX:MaxGCPauseMillis: Target pause time │- -XX:+HeapDumpOnOutOfMemoryError: For debugging │- -XX:HeapDumpPath: Where to save heap dump │- For Java 8: -XX:PermSize, -XX:MaxPermSize │- For Java 9+: -XX:MetaspaceSize, -XX:MaxMetaspaceSize │ │General rule: Set Xms=Xmx for consistent memory, allocate 70-80% │of available RAM to the heap. │ │─────────────────────────────────────────────────────────────────────────┤ │Q3: What is PM2 and why would you use it? │ │A3: │PM2 is a Node.js process manager: │- Process clustering and load balancing │- Zero-downtime deployments │- Automatic restarts on crashes │- Log management and aggregation │- Memory/CPU monitoring │- Cluster mode for parallel processing │- Container integration │- Useful for production Node.js applications │ │─────────────────────────────────────────────────────────────────────────┤ │Q4: How do you deploy a Java web application to Tomcat? │ │A4: │1. Build WAR file: mvn package or gradle build │2. Deploy: │ a. Copy WAR to webapps/ (auto-deploy) │ b. Use Manager web interface │ c. Deploy via curl/Ant script │3. Configure context in server.xml or context.xml │4. Set up database connections in context.xml or JNDI │5. Configure logging │6. Test in staging first │7. Monitor logs for startup errors │ │─────────────────────────────────────────────────────────────────────────┤ │Q5: What is the difference between sync and async workers in Gunicorn?│ │A5: │- sync: One request per worker at a time (blocking) │- gevent/eventlet: Async using greenlets (non-blocking I/O) │- tornado: Async HTTP client/server │- gthread: Threads within sync workers │ │For I/O-bound apps, async workers can handle more connections. │For CPU-bound apps, sync workers are better. │ │─────────────────────────────────────────────────────────────────────────┤ │Q6: How do you configure connection pooling for Java applications? │ │A6: │Tomcat provides JNDI DataSource: │<Resource name="jdbc/mydb" │ auth="Container" │ type="javax.sql.DataSource" │ maxTotal="100" │ maxIdle="30" │ maxWaitMillis="10000" │ username="user" │ password="pass" │ driverClassName="org.postgresql.Driver"│ url="jdbc:postgresql://localhost/mydb"/> │ │Or use HikariCP in the application for better performance. │ │─────────────────────────────────────────────────────────────────────────┤ │Q7: How do you implement zero-downtime deployments? │ │A7: │1. Load balancer: Remove old server from pool │2. Application server: │ - Gunicorn: gunicorn -HUP (graceful reload) │ - PM2: pm2 reload │ - Tomcat: Deploy new version, don't stop old │3. Health check: Verify new version is ready │4. Add back to pool │5. Remove old server │ │Tools: Kubernetes rolling updates, Ansible, Capistrano │ │─────────────────────────────────────────────────────────────────────────┤ │Q8: What is the difference between fork and cluster mode in PM2? │ │A8: │- fork mode (default): │ - Single process, single instance │ - Good for microservices │ - Manual scaling │ │- cluster mode: │ - Node.js cluster module for load balancing │ - Multiple instances (CPUs) │ - Zero-downtime reload │ - Shared port │ - Better for high-traffic apps │ │─────────────────────────────────────────────────────────────────────────┤ │Q9: How do you monitor application server health? │ │A9: │- JVM: JConsole, VisualVM, Java Mission Control │- Metrics: Prometheus + Grafana │- APM: New Relic, Datadog, Elastic APM │- Logs: ELK stack │- Health endpoints: /health, /metrics │- Custom metrics in application │- PM2: pm2 monit │- Tomcat: JMX + manager app │ │─────────────────────────────────────────────────────────────────────────┤ │Q10: What is the purpose of a reverse proxy in front of application │ servers? │ │A10: │- SSL termination │- Load balancing │- Static file serving │- Caching │- Compression │- Request filtering │- IP anonymization │- DDoS protection │- Common: Nginx, HAProxy, Apache │ │└─────────────────────────────────────────────────────────────────────────┘Quick Reference
Section titled “Quick Reference”# Tomcatcatalina.sh start # Startcatalina.sh stop # Stop# Set memory: $CATALINA_HOME/bin/setenv.sh
# Gunicorngunicorn -w 4 -b 127.0.0.1:8000 app:app# Config: gunicorn.conf.py
# uWSGIuwsgi --ini uwsgi.ini# Emperor: uwsgi --emperor /etc/uwsgi/vassals
# PM2pm2 start ecosystem.config.jspm2 listpm2 logspm2 restart myappSummary
Section titled “Summary”- Tomcat: Java application server, configure memory in setenv.sh
- Gunicorn: Python WSGI server, workers = CPU × 2 + 1
- uWSGI: Python with more features, Emperor mode for multi-app
- PM2: Node.js process manager, cluster mode for scaling
- Security: Run with minimal privileges, use SSL, configure headers
- Performance: Connection pooling, caching, compression
Next Chapter
Section titled “Next Chapter”Chapter 71: Web Servers Summary
Last Updated: February 2026