• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 9+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » The Ultimate DevOps Playbook: Tuning Nginx, Gunicorn/FPM, and Redis on DigitalOcean for Ruby

The Ultimate DevOps Playbook: Tuning Nginx, Gunicorn/FPM, and Redis on DigitalOcean for Ruby

Optimizing Nginx for High-Traffic Ruby Applications

When deploying Ruby applications on DigitalOcean, Nginx serves as the critical front-end, handling incoming HTTP requests, SSL termination, static file serving, and proxying to your application server. Proper Nginx tuning is paramount for achieving low latency and high throughput. This section focuses on key directives and strategies for production environments.

Nginx Configuration for Ruby Applications

The core of our Nginx configuration will reside within the http block, specifically in a server block that defines our application’s virtual host. We’ll focus on directives that impact performance and resource utilization.

Worker Processes and Connections

The worker_processes directive dictates how many worker processes Nginx will spawn. Setting this to auto is generally recommended, allowing Nginx to detect the number of CPU cores and utilize them efficiently. The worker_connections directive sets the maximum number of simultaneous connections that each worker process can handle. A common starting point is 1024, but this can be increased based on your server’s RAM and expected load.

Keep-Alive and Buffers

keepalive_timeout controls how long an idle HTTP connection will remain open. A shorter timeout (e.g., 65 seconds) can free up resources faster, while a longer one can improve performance for clients making multiple requests. client_body_buffer_size and client_header_buffer_size are important for handling large requests. Setting them appropriately can prevent Nginx from writing to disk for temporary storage.

Gzip Compression

Enabling Gzip compression significantly reduces the size of responses sent to the client, leading to faster load times. Ensure you’re only compressing text-based assets.

Static File Serving Optimization

Nginx excels at serving static files. Directives like expires and add_header Cache-Control instruct browsers to cache static assets, reducing the load on your application server and Nginx itself for subsequent requests.

Proxying to Application Server (Gunicorn/FPM)

The proxy_pass directive is fundamental. For Gunicorn (Python WSGI), it will point to the Gunicorn socket or IP:Port. For PHP-FPM, it will point to the FPM socket. Crucially, set appropriate proxy headers to ensure your application receives accurate client information.

Example Nginx Configuration Snippet

Here’s a robust Nginx configuration snippet tailored for a Ruby application proxied to Gunicorn, running on a DigitalOcean droplet. Adapt paths and ports as necessary.

# /etc/nginx/sites-available/your_app.conf

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
    worker_connections 4096; # Adjust based on server RAM and expected load
    multi_accept on;
}

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # SSL Settings
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';

    # Gzip Compression
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;

    # Buffers
    client_body_buffer_size 100K;
    client_header_buffer_size 1K;
    large_client_header_buffers 2 128K;

    # Logging
    access_log /var/log/nginx/your_app.access.log;
    error_log /var/log/nginx/your_app.error.log;

    # Static File Caching
    location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ {
        expires 30d;
        add_header Cache-Control "public";
        access_log off;
    }

    # Proxy to Gunicorn
    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_pass http://unix:/path/to/your/app/shared/app.sock; # Or http://127.0.0.1:8000;
    }

    # Optional: Serve static files directly if not handled by app framework
    # location /static/ {
    #     alias /path/to/your/app/static/;
    #     expires 30d;
    #     add_header Cache-Control "public";
    # }
}

Tuning Gunicorn for Production

Gunicorn (Green Unicorn) is a popular WSGI HTTP Server for Python. When deploying Ruby applications, you might be using a Ruby-specific server like Puma or Unicorn, but the principles of tuning worker processes and threads are similar. For this example, we’ll assume a Python context for Gunicorn, but the concepts translate. If you’re using Ruby’s Puma, adjust the worker/thread counts accordingly.

Worker Processes and Threads

Gunicorn’s performance is heavily influenced by its worker processes and threads. The --workers flag determines the number of worker processes. A common recommendation is (2 * number_of_cpu_cores) + 1. For I/O-bound applications, you might also leverage threads using the --threads flag. However, be mindful that threads share the same process memory, and excessive threads can lead to context-switching overhead.

Worker Class

Gunicorn supports different worker classes. The default is sync, which is a simple, single-threaded worker. For I/O-bound applications, gevent or eventlet (asynchronous workers) can significantly improve concurrency by using green threads. If your application is CPU-bound, the sync worker class with multiple processes might be more suitable.

Timeouts and Keep-Alive

--timeout specifies the number of seconds to wait for a worker to respond before considering it dead. Adjust this based on your application’s typical request processing time. --keep-alive controls the number of requests a worker can handle before it’s restarted, helping to prevent memory leaks.

Example Gunicorn Command Line

This command line demonstrates a production-ready Gunicorn setup. It uses the sync worker class, a reasonable number of workers, and a socket for Nginx to connect to.

# Example Gunicorn command (adjust paths and worker counts)
# Assuming your WSGI application is in 'wsgi.py' and named 'application'
# For Ruby, this would be your Rackup file and server (e.g., Puma)

gunicorn --workers 3 \
         --threads 2 \
         --worker-class sync \
         --bind unix:/path/to/your/app/shared/app.sock \
         --timeout 30 \
         --keep-alive 1000 \
         wsgi:application

Note for Ruby: If you are using Puma, your command might look like:

# Example Puma command (adjust paths and worker/thread counts)
# Assuming your Rackup file is 'config.ru'

bundle exec puma -w 3 -t 5 -b 'unix:/path/to/your/app/shared/app.sock' -e production

Redis Performance Tuning

Redis is an invaluable tool for caching, session management, and message queuing. Optimizing its configuration on DigitalOcean can drastically improve application responsiveness.

Memory Management

maxmemory is crucial. Set this to a value that leaves sufficient RAM for your operating system and other services. maxmemory-policy determines how Redis evicts keys when maxmemory is reached. allkeys-lru (Least Recently Used) is a common and effective policy for caching scenarios.

Persistence

For production, you’ll likely want some form of persistence. RDB (snapshotting) and AOF (Append Only File) are the two main options. RDB is generally faster for restores but can lose data between snapshots. AOF logs every write operation, offering better durability but potentially slower restores and larger file sizes. Tuning save directives for RDB and appendfsync for AOF is important.

Network and I/O

tcp-backlog can be increased to handle a higher number of incoming connections. tcp-keepalive helps detect and close stale connections.

Example Redis Configuration Snippet

This snippet provides a solid starting point for Redis configuration on a DigitalOcean droplet. Adjust memory limits and persistence settings based on your specific needs and droplet size.

# /etc/redis/redis.conf

# General
daemonize yes
pidfile /var/run/redis/redis-server.pid
port 6379
tcp-backlog 511 # Default is 511, can be increased if needed

# Memory Management
# Set maxmemory to a value that leaves ~25% of RAM for OS and other services
# Example for a 4GB RAM droplet:
maxmemory 3gb
maxmemory-policy allkeys-lru # Or volatile-lru if using TTLs extensively

# Persistence (Choose one or both, tune carefully)
# RDB Snapshotting
save 900 1    # Save if 1 key changed in 900 seconds
save 300 10   # Save if 10 keys changed in 300 seconds
save 60 10000 # Save if 10000 keys changed in 60 seconds
dbfilename dump.rdb

# AOF (Append Only File) - More durable, potentially slower restores
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec # 'always' is safer but slower, 'no' is fastest but riskiest

# Network
tcp-keepalive 300

# Logging
loglevel notice
logfile /var/log/redis/redis-server.log

Monitoring and Iterative Tuning

Performance tuning is not a one-time event. Continuous monitoring is essential. Utilize tools like:

  • Nginx: nginx -s reload for applying config changes, stub_status module for connection metrics, and access/error logs.
  • Gunicorn/Puma: Application logs, process monitoring tools (e.g., htop, ps), and potentially APM tools.
  • Redis: redis-cli INFO command provides a wealth of metrics (memory usage, connected clients, commands processed, etc.).
  • System-wide: top, htop, vmstat, iostat to monitor CPU, memory, and I/O.

Start with conservative settings and gradually increase them while observing performance metrics. Identify bottlenecks through monitoring and adjust configurations iteratively. For instance, if Nginx logs show frequent 502 Bad Gateway errors, investigate your application server (Gunicorn/Puma) and its connection to Nginx. If Redis commands are slow, check memory usage and eviction policies.

Primary Sidebar

A little about the Author

Having 9+ Years of Experience in Software Development.
Expertised in Php Development, WordPress Custom Theme Development (From scratch using underscores or Genesis Framework or using any blank theme or Premium Theme), Custom Plugin Development. Hands on Experience on 3rd Party Php Extension like Chilkat, nSoftware.

Recent Posts

  • Step-by-Step: Diagnosing indexing lock conflicts and high CPU during bulk stock updates on DigitalOcean Servers
  • How to Debug and Fix memory leaks and socket exhaustion in daemon processes in Modern C++ Applications
  • Infrastructure as Code: Provisioning Secure PHP Clusters on DigitalOcean Using Terraform
  • Fixing Slow Largest Contentful Paint (LCP) caused by unoptimized database queries in Legacy Laravel Codebases Without Breaking API Contracts
  • An Auditor’s Checklist for Securing Laravel Backends on Google Cloud

Copyright © 2026 · Vinay Vengala