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

Vengala Vinay

Having 12+ Years of Experience in Software Development

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

The Ultimate DevOps Playbook: Tuning Nginx, Gunicorn/FPM, and PostgreSQL on Google Cloud for Ruby

Nginx Configuration for High-Traffic Ruby Applications

Optimizing Nginx is crucial for serving Ruby web applications efficiently, especially when dealing with high concurrency. The primary goals are to minimize latency, maximize throughput, and ensure robust error handling. We’ll focus on key directives that impact performance and stability.

Worker Processes and Connections

The worker_processes directive determines 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 effectively. The worker_connections directive sets the maximum number of simultaneous connections that each worker process can handle. This value should be set high enough to accommodate peak traffic, but not so high that it exhausts system resources.

Example Nginx Configuration Snippet

worker_processes auto;
events {
    worker_connections 4096; # Adjust based on your server's RAM and expected load
    multi_accept on;
}

http {
    # ... other http directives ...

    server {
        listen 80;
        server_name your_domain.com;
        root /path/to/your/ruby/app/public; # For static files

        location / {
            proxy_pass http://unix:/path/to/your/app.sock; # Or http://127.0.0.1:PORT
            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;
            proxy_read_timeout 300s; # Increase for long-running requests
            proxy_connect_timeout 75s;
        }

        # Serve static assets directly from Nginx for better performance
        location ~ ^/(assets|images|javascripts|stylesheets)/ {
            expires 1y;
            add_header Cache-Control "public";
            try_files $uri =404;
        }

        # Deny access to hidden files
        location ~ /\. {
            deny all;
        }
    }
}

Buffering and Timeouts

Nginx uses buffers to handle requests and responses. Tuning client_body_buffer_size, client_max_body_size, and proxy_buffers can prevent issues with large uploads or slow clients. Timeouts like proxy_read_timeout and proxy_connect_timeout are critical for preventing worker processes from being held up by slow backend applications or database queries.

Gunicorn/Puma Tuning for Ruby Applications

Whether you’re using Gunicorn (Python WSGI HTTP Server, often used with Ruby via tools like rackup) or Puma (a popular Ruby web server), the principles of tuning remain similar: manage worker processes, threads, and timeouts effectively.

Worker and Thread Configuration

The number of worker processes and threads directly impacts concurrency. A common strategy is to set the number of workers based on CPU cores and then use threads to handle I/O-bound tasks within each worker. For Puma, this is controlled by --workers and --threads. For Gunicorn, it’s --workers and --threads (though Gunicorn’s threading model can be more complex due to the GIL).

Puma Configuration Example (via config/puma.rb)

# config/puma.rb
workers Integer(ENV.fetch("WEB_CONCURRENCY") { 2 }) # Number of workers
threads_count = Integer(ENV.fetch("RAILS_MAX_THREADS") { 5 }) # Threads per worker

threads threads_count, threads_count

preload_app!

rackup
environment ENV.fetch("RAILS_ENV") { "development" }

on_worker_boot do
  # Worker specific setup for Rails.
  ActiveRecord::Base.establish_connection if defined?(ActiveRecord::Base)
end

# Allow Puma to be restarted by `rails restart` command.
plugin :tmp_restart

# Bind to a Unix socket for Nginx to proxy to
bind "unix:///path/to/your/app.sock"

# Or bind to a TCP socket
# bind "tcp://0.0.0.0:3000"

# Increase timeout for long-running requests
# If Nginx has a higher proxy_read_timeout, this should be higher or equal.
# Puma's default is 30 seconds.
request_max_threads threads_count, threads_count
worker_timeout 60 # seconds

Gunicorn Configuration Example (Command Line)

gunicorn --workers 4 \
         --threads 2 \
         --bind unix:/path/to/your/app.sock \
         --timeout 120 \
         --log-level info \
         your_app.wsgi:application

Note: For Gunicorn with Python, the Global Interpreter Lock (GIL) means that threads are best suited for I/O-bound tasks. For CPU-bound tasks, multiple worker processes are more effective. If your Ruby app is heavily CPU-bound, consider using multiple Puma workers.

Timeouts and Graceful Restarts

Setting appropriate timeout values in your web server is crucial. If a request takes longer than this timeout, the worker process will be killed, potentially leading to errors. Ensure this aligns with Nginx’s proxy_read_timeout. Graceful restarts (plugin :tmp_restart for Puma, or specific Gunicorn signals) are essential for zero-downtime deployments.

PostgreSQL Tuning on Google Cloud (GCP)

Database performance is often the bottleneck. Tuning PostgreSQL on GCP, particularly with Cloud SQL, involves understanding its configuration parameters and leveraging GCP’s managed services effectively.

Key PostgreSQL Configuration Parameters

These parameters are typically set in postgresql.conf. On Cloud SQL, you manage these via the GCP Console under “Flags”.

Memory Allocation

# Shared memory buffer for caching data pages.
# Aim for 25% of total RAM, but not more than ~8GB for typical workloads.
shared_buffers = 2GB

# Write-ahead log buffer.
# Larger values can improve write performance but increase recovery time.
wal_buffers = 16MB

# Amount of memory for sorting and hashing operations per connection.
# Aim for 5-10% of total RAM per connection, capped by max_connections.
# If you have many connections, this can consume a lot of RAM.
# Consider setting it lower if you have many connections and tune per-query.
work_mem = 64MB

# Memory for vacuum operations.
# Useful for preventing bloat and improving performance.
autovacuum_work_mem = 1GB # Or set to -1 to use work_mem

# Maximum memory used by background writer.
bgwriter_lru_maxpages = 1000
bgwriter_lru_multiplier = 1.0

Connection Management

# Maximum number of concurrent connections.
# This is a critical parameter. Each connection consumes RAM.
# Set based on your application's needs and server RAM.
max_connections = 100

# Number of background worker processes.
# Useful for parallel vacuuming, etc.
max_worker_processes = 8 # Should be >= autovacuum_max_workers + max_parallel_workers_per_gather

# Number of autovacuum worker processes.
autovacuum_max_workers = 3

# Maximum number of parallel workers per query.
max_parallel_workers_per_gather = 4
max_parallel_workers = 4

Write-Ahead Log (WAL) Tuning

# Controls how often WAL is flushed to disk.
# 'on' (default) flushes at each commit. 'remote_write' is often a good balance.
# 'local' is faster but less durable.
wal_level = replica
wal_sync_method = fsync
wal_writer_delay = 200ms # Default is 200ms, can be tuned.

# Controls the size of WAL segments. Larger segments can improve performance
# for heavy write loads but increase recovery time.
# Default is 16MB. Consider 64MB or 128MB for very high write throughput.
wal_segment_size = 64MB

Cloud SQL Specific Optimizations

When using Cloud SQL for PostgreSQL, leverage its features:

  • Instance Sizing: Choose an instance size (CPU, RAM) that matches your workload. Start with a reasonable size and monitor performance.
  • Read Replicas: Offload read traffic to read replicas to reduce load on the primary instance.
  • Connection Pooling: Use a connection pooler like PgBouncer. Cloud SQL doesn’t manage this directly, so you’ll need to deploy it separately or within your application environment. This is critical for managing max_connections effectively.
  • Monitoring: Utilize Cloud Monitoring to track CPU utilization, memory usage, disk I/O, and PostgreSQL-specific metrics (e.g., cache hit ratio, active connections, query latency).

Example PgBouncer Configuration (pgbouncer.ini)

[databases]
# Format: database_name = connection_string
# Example:
mydb = host=YOUR_CLOUD_SQL_IP port=5432 dbname=your_db_name user=your_user password=your_password

[pgbouncer]
; Listen on a Unix socket or TCP port
listen_addr = /var/run/pgbouncer/pgbouncer.sock
# listen_addr = 0.0.0.0:6432

; Pool mode: session, transaction, or statement
pool_mode = session

; Maximum number of clients per server connection.
; Adjust based on your application's concurrency and server resources.
max_client_conn = 1000

; Maximum number of server connections per pool.
; This should be significantly lower than max_client_conn.
default_pool_size = 20

; Minimum number of server connections per pool.
min_pool_size = 5

; Connection timeout for clients.
client_idle_timeout = 60

; Connection timeout for servers.
server_idle_timeout = 60

; Maximum number of server connections to keep open per database.
; This is a hard limit.
max_db_connections = 500

; Log level
log_connections = 0
log_disconnections = 0
log_pooler_errors = 1
log_stats = 0
log_filename = pgbouncer
logfile = /var/log/pgbouncer/pgbouncer.log
pidfile = /var/run/pgbouncer/pgbouncer.pid
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt

In your application’s database configuration (e.g., database.yml for Rails), you would then point to the PgBouncer socket or port instead of directly to PostgreSQL.

Monitoring and Iteration

Performance tuning is an iterative process. Continuously monitor your application and infrastructure metrics. Use tools like pg_stat_statements to identify slow queries and optimize them. Benchmark changes before and after applying them to quantify improvements. For GCP, Cloud Logging and Cloud Monitoring are indispensable for this.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Go Goroutines vs. Node.js Event Loop: Scaling I/O-Bound Microservices Under High Load
  • Elixir Phoenix vs. Go Gin: Concurrency Models and Fault Tolerance Under Peak Request Volume
  • Python Celery vs. Go Channels: Distributed Task Queue Overhead and Memory Reliability
  • Scala Pekko vs. Go Goroutines: Actor Model vs. CSP for Event-Driven Reactive Systems
  • Java Loom Virtual Threads vs. Go Goroutines: Under-the-Hood Scheduler and Thread Overhead Comparison

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (584)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (806)
  • PHP (5)
  • PHP Development (21)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (19)
  • Ruby on Rails (1)
  • Security & Compliance (543)
  • SEO & Growth (491)
  • Server (23)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (7)
  • WordPress Theme Development (357)

Recent Posts

  • Go Goroutines vs. Node.js Event Loop: Scaling I/O-Bound Microservices Under High Load
  • Elixir Phoenix vs. Go Gin: Concurrency Models and Fault Tolerance Under Peak Request Volume
  • Python Celery vs. Go Channels: Distributed Task Queue Overhead and Memory Reliability

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (806)
  • Debugging & Troubleshooting (584)
  • Security & Compliance (543)
  • SEO & Growth (491)
  • Business & Monetization (390)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala