Debugging and Fixing “Errno::ENOSPC: No space left on device” in Ruby on Rails Production Environments


5 views

When your Rails application throws the Errno::ENOSPC error despite having ample storage (96GB in this case), the key insight comes from the df -h output revealing the root partition (/dev/xvda) is 97% full with only 325MB available. This explains why 200MB log files trigger the error.

The initial daily rotation setup wasn't sufficient for high-traffic applications. While moving to hourly rotation with size-based triggers is conceptually correct, we need to verify the cron execution:

# Check cron job status
sudo service cron status

# Verify logrotate execution logs
grep logrotate /var/log/syslog

Here's a more robust configuration for production environments:

/var/www/your_app/shared/log/*.log {
    size 100M
    missingok
    rotate 10
    compress
    delaycompress
    notifempty
    copytruncate
    dateext
    dateformat -%Y%m%d-%H%M%S
    create 644 deploy deploy
}

Immediate actions to reclaim space:

# Remove old log files
sudo find /var/log -type f -name "*.gz" -mtime +30 -delete

# Clear package manager cache
sudo apt-get clean
sudo apt-get autoclean

# Check for large files
sudo du -ahx / | sort -rh | head -20

For Linode VPS with separate root and data partitions:

# Resize the filesystem (if using ext4)
sudo lvextend -L +20G /dev/mapper/your-volume
sudo resize2fs /dev/mapper/your-volume

# Alternative: Mount additional storage
sudo mkdir /mnt/logstorage
sudo mount /dev/xvdb /mnt/logstorage
sudo ln -s /mnt/logstorage /var/log/your_app

Implement proactive monitoring with this sample Ruby script:

require 'sys/filesystem'

def check_disk_usage(threshold: 90)
  stat = Sys::Filesystem.stat('/')
  usage_percent = (stat.blocks.to_f - stat.blocks_available) / stat.blocks * 100
  
  if usage_percent > threshold
    system('/usr/sbin/logrotate -f /etc/logrotate.conf')
    system('echo "Disk cleanup triggered" | mail -s "Alert: Disk Space" admin@example.com')
  end
end

# Run every 15 minutes via cron
check_disk_usage(threshold: 85)

For high-volume applications, consider remote logging:

# config/environments/production.rb
config.logger = RemoteSyslogLogger.new(
  'logs.papertrailapp.com',
  12345,
  program: "myapp_#{Rails.env}",
  local_hostname: Socket.gethostname
)

When your Rails application throws the Errno::ENOSPC error despite having ample storage, it's crucial to examine multiple factors:

# Check disk usage
df -h
# Check inode usage
df -ih

In this case, the output reveals the actual issue:

/dev/xvda             9.5G  8.7G  325M  97% /

Your root partition only has 9.5GB allocated, with 97% already used. The 96GB storage you mentioned isn't properly allocated to your root filesystem.

Your current logrotate setup has several issues:

  1. rotate 1 keeps only one backup - too few for production
  2. Size-based rotation needs proper cron job timing
  3. copytruncate can cause log loss during high traffic

Here's an improved configuration:

/path/to/application/log/*.log {
  daily
  missingok
  rotate 7
  compress
  delaycompress
  notifempty
  dateext
  create 0644 deploy deploy
  size 100M
  postrotate
    /etc/init.d/apache2 reload > /dev/null
  endscript
}

To properly utilize your 96GB storage:

# For ext4 filesystem (common on Linode)
sudo lvextend -L +80G /dev/xvda
sudo resize2fs /dev/xvda

# Alternative if using LVM:
sudo lvextend -l +100%FREE /dev/your_vg/root
sudo resize2fs /dev/your_vg/root

For high-traffic applications, consider:

# Configure Rails to use syslog
config.logger = ActiveSupport::Logger.new(Syslog::Logger.new('myapp'))

# Or use lograge for condensed logging
# Gemfile:
gem 'lograge'

# config/environments/production.rb:
config.lograge.enabled = true
config.lograge.formatter = Lograge::Formatters::Json.new

Create a daily cleanup script in /etc/cron.daily/clean_logs:

#!/bin/bash
# Clean old logs
find /path/to/application/log -name "*.log.*" -mtime +30 -exec rm {} \;
# Clean tmp files
find /tmp -type f -mtime +7 -exec rm {} \;
# Clean old packages
apt-get autoremove -y
apt-get autoclean -y

Make it executable:

chmod +x /etc/cron.daily/clean_logs

Set up automated monitoring with this simple script:

#!/bin/bash
THRESHOLD=90
CURRENT=$(df / | grep / | awk '{ print $5}' | sed 's/%//g')
if [ "$CURRENT" -gt "$THRESHOLD" ]; then
  mail -s "Disk Space Alert" admin@example.com << EOF
Your root partition remaining free space is critically low. Used: $CURRENT%
EOF
fi