<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: bigdomain host</title>
    <description>The latest articles on DEV Community by bigdomain host (@bigdomainhost).</description>
    <link>https://dev.to/bigdomainhost</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4215664%2Fe71c6022-73f3-4b83-8b15-b7f15249a744.png</url>
      <title>DEV Community: bigdomain host</title>
      <link>https://dev.to/bigdomainhost</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bigdomainhost"/>
    <language>en</language>
    <item>
      <title>Cron jobs, and the four reasons yours isn't running</title>
      <dc:creator>bigdomain host</dc:creator>
      <pubDate>Fri, 30 Oct 2026 19:18:19 +0000</pubDate>
      <link>https://dev.to/bigdomainhost/cron-jobs-and-the-four-reasons-yours-isnt-running-37j8</link>
      <guid>https://dev.to/bigdomainhost/cron-jobs-and-the-four-reasons-yours-isnt-running-37j8</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure: I run Bigdomainhost, an Indian hosting company. This is adapted from a guide on our site.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Cron takes a minute to understand and, sometimes, a week to debug. The idea is trivial: a table of commands, each with a schedule, and a daemon that runs them on time. It has been part of Unix since the 1970s.&lt;/p&gt;

&lt;p&gt;The trouble is that a cron job which looks right will often just not run, and cron won't tell you. When that happens it's almost always one of four things. Syntax first, then those four.&lt;/p&gt;

&lt;h2&gt;
  
  
  The five fields
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;┌───────────── &lt;span class="n"&gt;minute&lt;/span&gt;        (&lt;span class="m"&gt;0&lt;/span&gt; - &lt;span class="m"&gt;59&lt;/span&gt;)
│ ┌─────────── &lt;span class="n"&gt;hour&lt;/span&gt;          (&lt;span class="m"&gt;0&lt;/span&gt; - &lt;span class="m"&gt;23&lt;/span&gt;)
│ │ ┌───────── &lt;span class="n"&gt;day&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt;  (&lt;span class="m"&gt;1&lt;/span&gt; - &lt;span class="m"&gt;31&lt;/span&gt;)
│ │ │ ┌─────── &lt;span class="n"&gt;month&lt;/span&gt;         (&lt;span class="m"&gt;1&lt;/span&gt; - &lt;span class="m"&gt;12&lt;/span&gt;)
│ │ │ │ ┌───── &lt;span class="n"&gt;day&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;week&lt;/span&gt;   (&lt;span class="m"&gt;0&lt;/span&gt; - &lt;span class="m"&gt;6&lt;/span&gt;, &lt;span class="n"&gt;Sunday&lt;/span&gt; = &lt;span class="m"&gt;0&lt;/span&gt;)
│ │ │ │ │
* * * * *  /&lt;span class="n"&gt;usr&lt;/span&gt;/&lt;span class="n"&gt;bin&lt;/span&gt;/&lt;span class="n"&gt;php&lt;/span&gt; /&lt;span class="n"&gt;home&lt;/span&gt;/&lt;span class="n"&gt;user&lt;/span&gt;/&lt;span class="n"&gt;script&lt;/span&gt;.&lt;span class="n"&gt;php&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are only four operators:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;*&lt;/code&gt; means every value. &lt;code&gt;* * * * *&lt;/code&gt; is every minute.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;,&lt;/code&gt; is a list. &lt;code&gt;0 9,17 * * *&lt;/code&gt; is 9am and 5pm.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-&lt;/code&gt; is a range. &lt;code&gt;0 9-17 * * *&lt;/code&gt; is hourly from 9am to 5pm.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/&lt;/code&gt; is a step. &lt;code&gt;*/15 * * * *&lt;/code&gt; is every fifteen minutes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's the whole language. Anything that looks more complicated is a combination of these.&lt;/p&gt;

&lt;h2&gt;
  
  
  Schedules you'll actually use
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Expression&lt;/th&gt;
&lt;th&gt;Runs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*/5 * * * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every five minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*/15 * * * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every fifteen minutes (the usual WP-Cron replacement)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0 * * * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every hour, on the hour&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;30 2 * * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2:30am daily, the classic backup slot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0 3 * * 0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3am every Sunday&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0 0 1 * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Midnight on the first of every month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0 9 * * 1-5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;9am on weekdays only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One thing people forget: cron runs in the &lt;strong&gt;server's&lt;/strong&gt; timezone. On our servers that's IST, so &lt;code&gt;30 2 * * *&lt;/code&gt; means 2:30am in India. On a cloud VM somewhere else it might be UTC, and your "2:30am backup" quietly runs at 8am IST in the middle of business hours. Run &lt;code&gt;date&lt;/code&gt; on the box before you assume anything.&lt;/p&gt;

&lt;p&gt;The smallest interval standard cron gives you is one minute. If you need something finer, you need a long-running process or a loop with &lt;code&gt;sleep&lt;/code&gt;, not cron. On shared hosting the practical minimum is often five or fifteen minutes, to protect everyone else on the server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting it up
&lt;/h2&gt;

&lt;h3&gt;
  
  
  In cPanel
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;cPanel → &lt;strong&gt;Cron Jobs&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Put a real address in &lt;strong&gt;Cron Email&lt;/strong&gt;. Cron mails you anything the command prints. This is how you find out a job has been failing for three weeks.&lt;/li&gt;
&lt;li&gt;Pick a Common Setting, or type the five fields yourself.&lt;/li&gt;
&lt;li&gt;Enter the command with absolute paths:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/usr/local/bin/php /home/username/public_html/cron.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Add it, wait for the first run, and check the mail.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once you've seen it succeed and you're tired of the emails, append &lt;code&gt;&amp;gt;/dev/null 2&amp;gt;&amp;amp;1&lt;/code&gt;. Not before. Silencing a job you haven't watched work is how jobs die unnoticed.&lt;/p&gt;

&lt;h3&gt;
  
  
  On a VPS
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# edit your own crontab&lt;/span&gt;
crontab &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# list what is scheduled&lt;/span&gt;
crontab &lt;span class="nt"&gt;-l&lt;/span&gt;

&lt;span class="c"&gt;# edit another user's crontab&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;crontab &lt;span class="nt"&gt;-u&lt;/span&gt; www-data &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;System-wide jobs go in &lt;code&gt;/etc/cron.d/&lt;/code&gt;, and those files take an extra field: the user to run as, between the schedule and the command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# /etc/cron.d/backup
&lt;/span&gt;&lt;span class="m"&gt;30&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; * * * &lt;span class="n"&gt;root&lt;/span&gt; /&lt;span class="n"&gt;usr&lt;/span&gt;/&lt;span class="n"&gt;local&lt;/span&gt;/&lt;span class="n"&gt;bin&lt;/span&gt;/&lt;span class="n"&gt;backup&lt;/span&gt;.&lt;span class="n"&gt;sh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy a line from &lt;code&gt;/etc/cron.d/&lt;/code&gt; into &lt;code&gt;crontab -e&lt;/code&gt; (or the other way round) and the user field gets treated as the command, or the command loses its first word. This catches people out constantly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four reasons it doesn't run
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Relative paths
&lt;/h3&gt;

&lt;p&gt;Cron doesn't start in your home directory and doesn't have your shell's &lt;code&gt;PATH&lt;/code&gt;. So:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php script.php                              &lt;span class="c"&gt;# fails&lt;/span&gt;
/usr/local/bin/php /home/user/script.php    &lt;span class="c"&gt;# works&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Find the real binary with &lt;code&gt;which php&lt;/code&gt; and paste exactly what it prints. Same for &lt;code&gt;mysqldump&lt;/code&gt;, &lt;code&gt;node&lt;/code&gt;, &lt;code&gt;python3&lt;/code&gt;, anything.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. A missing environment
&lt;/h3&gt;

&lt;p&gt;Your interactive shell loads &lt;code&gt;.bashrc&lt;/code&gt;. Cron doesn't. If your script depends on an environment variable, it needs to be set in the crontab itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/bin:/usr/bin:/bin
30 2 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /home/user/backup.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. The script isn't executable
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /home/user/backup.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And make sure the first line is a shebang, &lt;code&gt;#!/bin/bash&lt;/code&gt;. Without it, cron may not know how to run the file.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. An unescaped percent sign
&lt;/h3&gt;

&lt;p&gt;This is the one nobody guesses. In a crontab, &lt;code&gt;%&lt;/code&gt; means newline. Everything after the first &lt;code&gt;%&lt;/code&gt; gets passed as stdin, not as part of the command. So this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;30 2 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;tar &lt;/span&gt;czf /backups/site-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y-%m-%d&lt;span class="si"&gt;)&lt;/span&gt;.tar.gz /home/user/public_html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;gets cut off at &lt;code&gt;date +&lt;/code&gt;, and you get a broken command with no obvious error. Escape every one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;30 2 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;tar &lt;/span&gt;czf /backups/site-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +&lt;span class="se"&gt;\%&lt;/span&gt;Y-&lt;span class="se"&gt;\%&lt;/span&gt;m-&lt;span class="se"&gt;\%&lt;/span&gt;d&lt;span class="si"&gt;)&lt;/span&gt;.tar.gz /home/user/public_html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, honestly, put the logic in a shell script and call the script from cron. Then the &lt;code&gt;%&lt;/code&gt; problem goes away entirely and you can test the script by hand.&lt;/p&gt;

&lt;h2&gt;
  
  
  WordPress: replace WP-Cron
&lt;/h2&gt;

&lt;p&gt;WordPress has its own scheduler called WP-Cron, and it isn't cron. It fires when someone loads a page. On a quiet site that means scheduled posts don't publish and backup plugins don't run. On a busy site it fires on every page load, which is its own problem.&lt;/p&gt;

&lt;p&gt;You fix both in five minutes. In &lt;code&gt;wp-config.php&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nb"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DISABLE_WP_CRON'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add a real cron job every fifteen minutes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;*&lt;/span&gt;/15 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /usr/local/bin/php /home/user/public_html/wp-cron.php &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For five minutes of work, few WordPress changes pay off more. We go into it, and the rest of the WordPress tuning list, in our &lt;a href="https://bigdomainhost.com/guide-wordpress-speed-optimization-india.html" rel="noopener noreferrer"&gt;WordPress speed guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging a job that's gone quiet
&lt;/h2&gt;

&lt;p&gt;In the order I'd actually do it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Run the exact command by hand.&lt;/strong&gt; Copy it out of the crontab, paste it into a shell. If it fails interactively, cron was never the problem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the cron log.&lt;/strong&gt; &lt;code&gt;/var/log/syslog&lt;/code&gt; on Debian and Ubuntu, &lt;code&gt;/var/log/cron&lt;/code&gt; on AlmaLinux and Rocky. If there's no line for your job at the expected time, the schedule or the crontab file is wrong. If there is a line, the command is wrong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capture the output.&lt;/strong&gt; Replace &lt;code&gt;&amp;gt;/dev/null 2&amp;gt;&amp;amp;1&lt;/code&gt; with &lt;code&gt;&amp;gt;&amp;gt; /home/user/cron.log 2&amp;gt;&amp;amp;1&lt;/code&gt; and read the file after the next run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test with &lt;code&gt;* * * * *&lt;/code&gt;.&lt;/strong&gt; You find out in a minute instead of tomorrow. Then set the real schedule back.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A quick way to check the log on Ubuntu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep &lt;/span&gt;CRON /var/log/syslog | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;

&lt;span class="c"&gt;# if there's no syslog file, ask journald instead&lt;/span&gt;
journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;--since&lt;/span&gt; today
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On AlmaLinux and Rocky the unit is &lt;code&gt;crond&lt;/code&gt;, not &lt;code&gt;cron&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you see the job firing there but nothing happens, go back to the four reasons above. Paths and &lt;code&gt;%&lt;/code&gt; are the two I'd check first.&lt;/p&gt;

&lt;p&gt;Cron is available on every plan we sell, from &lt;a href="https://bigdomainhost.com/cpanel-hosting.html" rel="noopener noreferrer"&gt;shared cPanel hosting&lt;/a&gt; upwards, and on a &lt;a href="https://bigdomainhost.com/vps-hosting.html" rel="noopener noreferrer"&gt;VPS&lt;/a&gt; you get the full &lt;code&gt;crontab&lt;/code&gt; and &lt;code&gt;/etc/cron.d/&lt;/code&gt;. The original guide, with a short FAQ, is &lt;a href="https://bigdomainhost.com/guide-cron-jobs.html" rel="noopener noreferrer"&gt;on our site&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Posted by the Bigdomainhost team. The original version of this guide is on our site.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>cron</category>
      <category>devops</category>
      <category>wordpress</category>
    </item>
    <item>
      <title>How much RAM does your VPS actually need? Numbers by workload</title>
      <dc:creator>bigdomain host</dc:creator>
      <pubDate>Thu, 29 Oct 2026 16:38:27 +0000</pubDate>
      <link>https://dev.to/bigdomainhost/how-much-ram-does-your-vps-actually-need-numbers-by-workload-1f7p</link>
      <guid>https://dev.to/bigdomainhost/how-much-ram-does-your-vps-actually-need-numbers-by-workload-1f7p</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure: I'm Parth, I run Bigdomainhost, an Indian hosting company, and the price column below is our own plan list. The RAM numbers apply to any host.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Almost every VPS gets bought one size too big or one size too small. Too small looks like WordPress, MySQL and a page builder squeezed into 2 GB, throwing "Error establishing a database connection" every few days. Too big looks like a 32 GB box running a 3 GB workload.&lt;/p&gt;

&lt;p&gt;Both are fixable with some simple arithmetic. This post is that arithmetic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with what uses the memory
&lt;/h2&gt;

&lt;p&gt;On a normal LAMP or LEMP server, the RAM goes to a handful of things. Typical ranges:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Typical usage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Minimal Ubuntu / AlmaLinux, no desktop&lt;/td&gt;
&lt;td&gt;200-400 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MySQL / MariaDB&lt;/td&gt;
&lt;td&gt;500 MB to 2 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PHP-FPM&lt;/td&gt;
&lt;td&gt;40-120 MB &lt;strong&gt;per worker&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nginx&lt;/td&gt;
&lt;td&gt;50-100 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apache with mod_php&lt;/td&gt;
&lt;td&gt;a lot more than Nginx&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LiteSpeed&lt;/td&gt;
&lt;td&gt;somewhere between the two&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redis&lt;/td&gt;
&lt;td&gt;whatever you cap it at, usually 256 MB to 2 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cPanel&lt;/td&gt;
&lt;td&gt;about 1 GB before you host a single site&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two lines in that table do most of the damage.&lt;/p&gt;

&lt;p&gt;The first is PHP-FPM. People look at "40-120 MB" and think it's small. It's per worker. A WordPress site with Elementor or similar sits at the top of that range, so ten workers at 100 MB is a full gigabyte on its own.&lt;/p&gt;

&lt;p&gt;The second is the MySQL InnoDB buffer pool. Ideally it holds your entire working data set in memory. If it can't, every query that misses the pool goes to disk and your "slow site" problem is actually a RAM problem wearing a disguise.&lt;/p&gt;

&lt;p&gt;Add up the components you actually run, then put 25-30% on top. A server that is full at idle has nothing left for your busiest hour of the day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I size RAM generously and CPU tightly
&lt;/h2&gt;

&lt;p&gt;This is the one opinion I'd defend hardest in this whole post.&lt;/p&gt;

&lt;p&gt;When a server runs short of CPU, it gets slow. Requests queue, pages take longer, but they come back.&lt;/p&gt;

&lt;p&gt;When it runs short of RAM, it breaks. Linux first starts swapping to disk, which is orders of magnitude slower than memory. When swap is used up too, the kernel's out-of-memory killer picks the largest process and kills it. On a web server the largest process is almost always MySQL. So the visible symptom isn't "slow", it's "Error establishing a database connection" until someone or something restarts mysqld.&lt;/p&gt;

&lt;p&gt;CPU pressure degrades. RAM pressure breaks. So if the budget forces a trade-off, take the extra RAM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rules of thumb that have held up for us
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WordPress, cached:&lt;/strong&gt; roughly 1 GB per busy site, including its share of MySQL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WooCommerce:&lt;/strong&gt; double that. Cart, checkout and My Account can't be page-cached, so every one of those requests hits PHP and MySQL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minecraft:&lt;/strong&gt; about 1 GB per ten vanilla players, plus whatever the modpack says it needs, rounded up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tally on Cloud:&lt;/strong&gt; Windows Server wants about 4 GB before Tally even starts. 8 GB is comfortable for one or two concurrent users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node and Python APIs:&lt;/strong&gt; measure. They vary more than anything else here, and any number I give you will be wrong for your app.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The cheat sheet
&lt;/h2&gt;

&lt;p&gt;Here's how that maps to plan sizes. Prices are our own plans in rupees, excluding 18% GST, because that's what I know. The RAM column is the part that transfers to any host.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Workload&lt;/th&gt;
&lt;th&gt;RAM&lt;/th&gt;
&lt;th&gt;Our plan (per month)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Static site, small API, dev box&lt;/td&gt;
&lt;td&gt;2 GB&lt;/td&gt;
&lt;td&gt;₹674&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1-3 WordPress sites, cached&lt;/td&gt;
&lt;td&gt;4 GB&lt;/td&gt;
&lt;td&gt;₹674&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production WordPress + MySQL&lt;/td&gt;
&lt;td&gt;8 GB&lt;/td&gt;
&lt;td&gt;₹1,348&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WooCommerce store&lt;/td&gt;
&lt;td&gt;8-16 GB&lt;/td&gt;
&lt;td&gt;₹1,348-₹2,696&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10-20 sites behind cPanel&lt;/td&gt;
&lt;td&gt;16 GB&lt;/td&gt;
&lt;td&gt;₹2,696&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redis or Elasticsearch node&lt;/td&gt;
&lt;td&gt;16 GB+&lt;/td&gt;
&lt;td&gt;₹2,696&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tally on Cloud, 1-2 users&lt;/td&gt;
&lt;td&gt;8 GB&lt;/td&gt;
&lt;td&gt;₹1,899&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Modded Minecraft, 20-40 players&lt;/td&gt;
&lt;td&gt;16 GB&lt;/td&gt;
&lt;td&gt;₹2,696&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CI runners, build servers&lt;/td&gt;
&lt;td&gt;16-32 GB&lt;/td&gt;
&lt;td&gt;₹2,696-₹5,017&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Measure what you're using right now
&lt;/h2&gt;

&lt;p&gt;If you already have a server, don't guess. Three commands tell you most of what you need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# free memory, human readable&lt;/span&gt;
free &lt;span class="nt"&gt;-h&lt;/span&gt;

&lt;span class="c"&gt;# the ten biggest processes by memory&lt;/span&gt;
ps aux &lt;span class="nt"&gt;--sort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;-%mem | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-11&lt;/span&gt;

&lt;span class="c"&gt;# is it swapping? (5 samples, 1 second apart)&lt;/span&gt;
vmstat 1 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things to read correctly.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;free -h&lt;/code&gt;, look at the &lt;strong&gt;available&lt;/strong&gt; column, not &lt;strong&gt;free&lt;/strong&gt;. Linux uses spare memory as disk cache and gives it back on demand. A low "free" number is normal and healthy. People panic over it all the time and it means nothing.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;vmstat&lt;/code&gt;, watch the &lt;code&gt;si&lt;/code&gt; and &lt;code&gt;so&lt;/code&gt; columns (swap in, swap out). If they're non-zero across samples, the box is actively swapping. That's your signal to add memory, not to tune something.&lt;/p&gt;

&lt;p&gt;If you want to see how much each PHP-FPM worker is actually taking on your site, rather than trusting my 40-120 MB range:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ps &lt;span class="nt"&gt;-o&lt;/span&gt; rss,cmd &lt;span class="nt"&gt;-C&lt;/span&gt; php-fpm &lt;span class="nt"&gt;--sort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-rss&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;RSS is in KB. Multiply your typical worker size by &lt;code&gt;pm.max_children&lt;/code&gt; in your pool config and you'll know whether your FPM settings can even fit in the RAM you have. (The process name may be &lt;code&gt;php-fpm8.2&lt;/code&gt; or similar depending on the distro, so adjust the &lt;code&gt;-C&lt;/code&gt; value.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Should you add swap?
&lt;/h2&gt;

&lt;p&gt;Yes, 1-2 GB of it. Treat it as an emergency buffer that turns a hard crash into a slow patch. It is not a substitute for RAM. If you're swapping routinely, buy more memory.&lt;/p&gt;

&lt;p&gt;On a box without swap, the usual setup is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;fallocate &lt;span class="nt"&gt;-l&lt;/span&gt; 2G /swapfile
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 /swapfile
&lt;span class="nb"&gt;sudo &lt;/span&gt;mkswap /swapfile
&lt;span class="nb"&gt;sudo &lt;/span&gt;swapon /swapfile
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s1"&gt;'^/swapfile '&lt;/span&gt; /etc/fstab &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'/swapfile none swap sw 0 0'&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/fstab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;grep&lt;/code&gt; guard stops you adding a duplicate fstab line if you run it twice. Then &lt;code&gt;free -h&lt;/code&gt; again to confirm the Swap row shows 2.0Gi. (On some filesystems &lt;code&gt;fallocate&lt;/code&gt; swap files fail &lt;code&gt;swapon&lt;/code&gt;; if that happens, create the file with &lt;code&gt;dd&lt;/code&gt; instead.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Is 1 GB ever enough?
&lt;/h2&gt;

&lt;p&gt;For a static site, a small API or a personal project, yes. For WordPress with MySQL on the same machine, it's tight enough that one traffic spike will wake up the OOM killer. If the site matters to anyone, start at 2 GB. For a production WordPress site with the database on the same box, 4 GB is the sensible starting point, and 8 GB once WooCommerce or a dozen client sites are involved.&lt;/p&gt;

&lt;h2&gt;
  
  
  When CPU does matter more
&lt;/h2&gt;

&lt;p&gt;There are workloads where I'd reverse my advice: video encoding, heavy cron jobs, and game servers. For game servers, single-core speed beats core count. A Minecraft server's main loop mostly runs on one thread, so extra cores don't help it the way they help a pool of PHP workers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't over-buy either
&lt;/h2&gt;

&lt;p&gt;A 32 GB server running 3 GB of workload is money spent on nothing. Our plans resize in place: vCPU, RAM and disk grow with a single reboot and nothing gets reinstalled. If your host can do the same, the right move is to start one size down and move up when &lt;code&gt;free -h&lt;/code&gt; and &lt;code&gt;vmstat&lt;/code&gt; tell you to, not when a sales page does.&lt;/p&gt;

&lt;p&gt;If your host can't resize in place and a move means a full rebuild, that changes the maths. Then I'd buy one size up and accept some waste, because a migration under pressure costs more than the extra RAM.&lt;/p&gt;

&lt;p&gt;The full VPS ladder we sell, with Mumbai (BOM-1) and Delhi NCR (DEL-1) locations, is on the &lt;a href="https://bigdomainhost.com/vps-hosting.html" rel="noopener noreferrer"&gt;KVM VPS page&lt;/a&gt;. If you're still deciding whether you need a VPS at all, we also wrote up &lt;a href="https://bigdomainhost.com/guide-vps-vs-shared-hosting.html" rel="noopener noreferrer"&gt;VPS vs shared hosting&lt;/a&gt;, and the original version of this guide is &lt;a href="https://bigdomainhost.com/guide-how-much-ram-does-a-vps-need.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Posted by Parth from Bigdomainhost. The original version of this guide is on our site.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>devops</category>
      <category>webdev</category>
      <category>wordpress</category>
    </item>
  </channel>
</rss>
