I have been running Nginx on a 1 vCPU / 2 GB RAM VPS for a while now. These are my notes on configuration choices that make sense at this scale.

The defaults are mostly fine

Nginx is efficient out of the box. For a small site serving static files, you can run the default configuration and it will handle far more traffic than your server will ever see. But there are a few things worth adjusting.

Worker processes

The worker_processes directive controls how many worker processes Nginx spawns. The common advice is to set it to the number of CPU cores:

worker_processes auto;

The auto value does exactly that. On a 1 vCPU machine, this gives you one worker, which is appropriate. There is no benefit to running multiple workers on a single core.

Each worker can handle thousands of concurrent connections, so one worker is not a bottleneck for a small site.

Worker connections

events {
    worker_connections 512;
}

The default is often 768 or 1024. For a small personal server, 512 is more than enough. Each connection uses a small amount of memory, so there is no harm in keeping this reasonable.

The maximum number of simultaneous connections your server can handle is worker_processes * worker_connections. With 1 worker and 512 connections, that is 512 concurrent connections. For a personal blog, you will never come close.

Gzip compression

Enabling gzip reduces the size of text-based responses significantly:

gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 4;
gzip_min_length 256;
gzip_types
    text/plain
    text/css
    text/javascript
    application/javascript
    application/json
    application/xml
    image/svg+xml;

A few notes on these settings:

  • gzip_comp_level 4 is a good balance between compression ratio and CPU usage. Level 6 or higher uses noticeably more CPU with diminishing returns.
  • gzip_min_length 256 avoids compressing tiny responses where the overhead is not worth it.
  • gzip_vary on ensures caches handle compressed and uncompressed versions correctly.

Static file caching

For a static site, telling browsers to cache assets saves bandwidth and makes repeat visits faster:

location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
    expires 30d;
    add_header Cache-Control "public, no-transform";
}

HTML files should not be cached as aggressively, since you want updates to appear immediately:

location ~* \.html$ {
    expires 1h;
    add_header Cache-Control "public, no-transform";
}

Logging

The default access log writes every request to disk. On a small VPS with an SSD, this is fine for low traffic. But if you want to reduce disk writes, you can buffer the logs:

access_log /var/log/nginx/access.log combined buffer=16k flush=5m;

This buffers log entries and writes them in batches. The flush=5m ensures logs are written at least every five minutes even if the buffer is not full.

For a personal site with minimal traffic, I actually keep the default logging. The disk I/O is negligible. But if you are running something busier, buffered logging helps.

Security headers

A few headers that are worth adding to every response:

add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

These are low-effort, high-value. They prevent content type sniffing, clickjacking, and excessive referrer information leakage.

Disabling server tokens

By default Nginx includes its version number in error pages and the Server response header. Disabling this reveals less information:

server_tokens off;

This goes in the http block of your main Nginx configuration.

Putting it together

Here is a condensed version of my Nginx configuration for reference:

user www-data;
worker_processes auto;
pid /run/nginx.pid;

events {
    worker_connections 512;
}

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

    sendfile on;
    tcp_nopush on;
    server_tokens off;
    keepalive_timeout 65;

    gzip on;
    gzip_vary on;
    gzip_comp_level 4;
    gzip_min_length 256;
    gzip_types text/plain text/css text/javascript
               application/javascript application/json
               application/xml image/svg+xml;

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

The site-specific server block lives in /etc/nginx/sites-available/ and is symlinked to sites-enabled/.

Monitoring

I keep an eye on Nginx with a few simple commands:

# Check active connections
curl -s http://127.0.0.1/nginx_status

# Watch the access log
tail -f /var/log/nginx/access.log

# Test configuration before reloading
nginx -t && systemctl reload nginx

The nginx_status endpoint requires the stub_status module, which is included in most Nginx packages. Add it to a server block restricted to localhost:

location /nginx_status {
    stub_status;
    allow 127.0.0.1;
    deny all;
}

Resource usage

On my server, Nginx uses about 3 MB of RAM for the master process and 5 MB per worker. With one worker, that is roughly 8 MB total. CPU usage for serving static files is effectively zero under normal load.

For a small VPS, Nginx is hard to beat. It does its job quietly and stays out of the way. The Nginx documentation is thorough if you need to dig deeper into any of these settings.