<?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: lifeissoup</title>
    <description>The latest articles on DEV Community by lifeissoup (@_4e8d848ae45b3c9c6eef9).</description>
    <link>https://dev.to/_4e8d848ae45b3c9c6eef9</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%2F4201536%2Fbce33f49-d09f-469f-866a-1a41edaf81f1.png</url>
      <title>DEV Community: lifeissoup</title>
      <link>https://dev.to/_4e8d848ae45b3c9c6eef9</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/_4e8d848ae45b3c9c6eef9"/>
    <language>en</language>
    <item>
      <title>Cron Expression for Every Day at Midnight (0 0 * * *)</title>
      <dc:creator>lifeissoup</dc:creator>
      <pubDate>Sun, 15 Nov 2026 07:20:10 +0000</pubDate>
      <link>https://dev.to/_4e8d848ae45b3c9c6eef9/cron-expression-for-every-day-at-midnight-0-0--48i6</link>
      <guid>https://dev.to/_4e8d848ae45b3c9c6eef9/cron-expression-for-every-day-at-midnight-0-0--48i6</guid>
      <description>&lt;p&gt;To run a cron job every day at midnight, use &lt;code&gt;0 0 * * *&lt;/code&gt;. That means minute 0, hour 0, every day of the month, every month, every day of the week. If you want to check a variation before deploying it, the &lt;a href="https://devrivettools.com/cron-generator" rel="noopener noreferrer"&gt;Cron Expression Generator&lt;/a&gt; explains any 5-field expression in plain English and previews the next run times.&lt;/p&gt;

&lt;h2&gt;
  
  
  The midnight expression, field by field
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0 0 * * *
| | | | |
| | | | +-- day of week (0-7, Sunday is 0 or 7)
| | | +---- month (1-12)
| | +------ day of month (1-31)
| +-------- hour (0-23)
+---------- minute (0-59)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hours use a 24-hour clock, so midnight is hour &lt;code&gt;0&lt;/code&gt;, not &lt;code&gt;12&lt;/code&gt; and not &lt;code&gt;24&lt;/code&gt;. A full crontab line looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;0 0 * * * /usr/local/bin/backup.sh &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /var/log/backup.log 2&lt;span class="o"&gt;&amp;gt;&amp;amp;&lt;/span&gt;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit your crontab with &lt;code&gt;crontab -e&lt;/code&gt;. The &lt;code&gt;&amp;gt;&amp;gt; ... 2&amp;gt;&amp;amp;1&lt;/code&gt; part appends both stdout and stderr to a log file. Without it, cron tries to email the output to the local user, which usually goes nowhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  The @daily and &lt;a class="mentioned-user" href="https://dev.to/midnight"&gt;@midnight&lt;/a&gt; shortcuts
&lt;/h2&gt;

&lt;p&gt;Many cron implementations (Vixie cron and its descendants) accept aliases. &lt;code&gt;@daily&lt;/code&gt; and &lt;code&gt;@midnight&lt;/code&gt; both mean &lt;code&gt;0 0 * * *&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;@daily /usr/local/bin/backup.sh
@midnight /usr/local/bin/backup.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are not part of POSIX, so don't assume they work everywhere. Schedulers like GitHub Actions expect plain 5-field expressions. Check your platform's docs, or just use &lt;code&gt;0 0 * * *&lt;/code&gt;, which works almost everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Midnight cron in GitHub Actions, Kubernetes and Node.js
&lt;/h2&gt;

&lt;p&gt;The expression stays &lt;code&gt;0 0 * * *&lt;/code&gt; on every platform that uses 5-field cron. What changes is where you write it and which time zone "midnight" is measured in.&lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub Actions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nightly&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'0 0 * * *'&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;cleanup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./scripts/nightly.sh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, GitHub Actions evaluates &lt;code&gt;schedule&lt;/code&gt; in UTC, so &lt;code&gt;0 0 * * *&lt;/code&gt; is midnight UTC: 9:00 AM in Seoul, and 7:00 PM (EST) or 8:00 PM (EDT) the previous evening in New York. For midnight in a UTC+9 zone, shift the hour back 9 hours: &lt;code&gt;0 15 * * *&lt;/code&gt;. Scheduled runs use the workflow file on the default branch and can start several minutes late when GitHub is busy. The &lt;code&gt;workflow_dispatch&lt;/code&gt; trigger lets you run the job by hand to test it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Kubernetes CronJob
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;batch/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CronJob&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nightly-cleanup&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"0 0 * * *"&lt;/span&gt;
  &lt;span class="na"&gt;timeZone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Asia/Seoul"&lt;/span&gt;
  &lt;span class="na"&gt;concurrencyPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Forbid&lt;/span&gt;
  &lt;span class="na"&gt;jobTemplate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;restartPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OnFailure&lt;/span&gt;
          &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cleanup&lt;/span&gt;
              &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;busybox:1.36&lt;/span&gt;
              &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"-c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"echo running nightly cleanup"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;timeZone&lt;/code&gt; takes an IANA zone name and is stable since Kubernetes 1.27. Without it, the schedule follows the time zone of the kube-controller-manager, which is usually UTC. Don't put &lt;code&gt;CRON_TZ=&lt;/code&gt; or &lt;code&gt;TZ=&lt;/code&gt; inside &lt;code&gt;schedule&lt;/code&gt;: Kubernetes doesn't support it and newer versions reject it. &lt;code&gt;concurrencyPolicy: Forbid&lt;/code&gt; skips a night if the previous run is still going.&lt;/p&gt;

&lt;h3&gt;
  
  
  Node.js
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cron&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;'node-cron'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;cron&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'0 0 * * *'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'nightly job'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'America/New_York'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;node-cron runs inside your Node.js process, so the job only fires while that process is running. It suits a long-running server, not serverless functions. The &lt;code&gt;timezone&lt;/code&gt; option takes an IANA zone name; without it the process's local time zone is used. For work that must survive restarts and deploys, use system cron, a Kubernetes CronJob or your platform's scheduler instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common daily schedules
&lt;/h2&gt;

&lt;p&gt;Only the minute and hour fields change for most daily jobs. Remember that minute comes first, which trips people up.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;0 0 * * *&lt;/code&gt; — every day at 12:00 AM (midnight)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;30 1 * * *&lt;/code&gt; — every day at 1:30 AM&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0 6 * * *&lt;/code&gt; — every day at 6:00 AM&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0 12 * * *&lt;/code&gt; — every day at noon&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0 18 * * *&lt;/code&gt; — every day at 6:00 PM&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;59 23 * * *&lt;/code&gt; — every day at 11:59 PM&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0 6,18 * * *&lt;/code&gt; — twice a day, at 6:00 AM and 6:00 PM&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0 */12 * * *&lt;/code&gt; — every 12 hours, at midnight and noon&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0 9 * * 1-5&lt;/code&gt; — weekdays at 9:00 AM&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0 0 * * 1-5&lt;/code&gt; — weekdays at midnight&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0 0 * * 0,6&lt;/code&gt; — Saturdays and Sundays at midnight&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0 0 1 * *&lt;/code&gt; — midnight on the first day of every month&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For jobs that run more often than daily, see &lt;a href="https://devrivettools.com/blog/cron-every-5-minutes" rel="noopener noreferrer"&gt;cron every 5 minutes&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pitfalls that cause real bugs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;* 0 * * *&lt;/code&gt; is not midnight
&lt;/h3&gt;

&lt;p&gt;Leaving the minute as &lt;code&gt;*&lt;/code&gt; runs the job every minute of hour 0, which is 60 times between 12:00 and 12:59 AM. Always set the minute explicitly: &lt;code&gt;0 0 * * *&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cron uses the server's timezone
&lt;/h3&gt;

&lt;p&gt;Standard cron runs in the system timezone of the machine (or the daemon's configured timezone). Many servers and containers are set to UTC, so "midnight" may be 7 or 8 PM in New York. Check with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;date
timedatectl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you need midnight in a specific zone, either set the server timezone, or use your scheduler's timezone option if it has one. Some cron implementations support a &lt;code&gt;CRON_TZ=&lt;/code&gt; line in the crontab, but this is not universal, so confirm it with your cron's man page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Daylight saving time
&lt;/h3&gt;

&lt;p&gt;In zones that change clocks, a job scheduled during the skipped or repeated hour can be skipped or run twice, depending on the implementation. Midnight is unaffected in zones that switch at 1 or 2 AM, but a &lt;code&gt;30 2 * * *&lt;/code&gt; job commonly hits the spring-forward gap in the US. If a daily job must run exactly once, schedule it outside your region's DST transition hour or run the server in UTC.&lt;/p&gt;

&lt;h3&gt;
  
  
  Day-of-month and day-of-week are OR, not AND
&lt;/h3&gt;

&lt;p&gt;If you restrict both fields, standard cron runs when either matches. &lt;code&gt;0 0 1 * 1&lt;/code&gt; runs at midnight on the 1st of the month and also every Monday, not only on Mondays that fall on the 1st. For "first Monday" you need a guard in the script itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Percent signs in crontab lines
&lt;/h3&gt;

&lt;p&gt;In a crontab, an unescaped &lt;code&gt;%&lt;/code&gt; is treated as a newline. This breaks commands that use &lt;code&gt;date&lt;/code&gt; formatting:&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;# Broken: everything after the first % is sent as stdin&lt;/span&gt;
0 0 * * * tar czf /backups/site-&lt;span class="nv"&gt;$(&lt;/span&gt;date +%F&lt;span class="o"&gt;)&lt;/span&gt;.tgz /var/www

&lt;span class="c"&gt;# Fixed: escape the percent sign&lt;/span&gt;
0 0 * * * tar czf /backups/site-&lt;span class="nv"&gt;$(&lt;/span&gt;date +\%F&lt;span class="o"&gt;)&lt;/span&gt;.tgz /var/www
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Everyone picks midnight
&lt;/h3&gt;

&lt;p&gt;Lots of jobs on a shared machine, or lots of scheduled tasks hitting the same API, all fire at &lt;code&gt;0 0&lt;/code&gt;. If exact midnight doesn't matter, offset it: &lt;code&gt;7 0 * * *&lt;/code&gt; or &lt;code&gt;43 2 * * *&lt;/code&gt; spreads the load.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cron's environment is minimal
&lt;/h3&gt;

&lt;p&gt;Cron jobs run with a stripped-down &lt;code&gt;PATH&lt;/code&gt; and no shell profile. Use absolute paths to scripts and binaries, and set variables at the top of the crontab if needed:&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
0 0 * * * /usr/local/bin/node /srv/app/cleanup.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6-field cron: Quartz, Spring and others
&lt;/h2&gt;

&lt;p&gt;Some schedulers use a different format with a leading seconds field. Midnight there looks different:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Spring @Scheduled (seconds minute hour day month weekday)
0 0 0 * * *

# Quartz (day-of-month or day-of-week must be ?)
0 0 0 * * ?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you paste a 5-field expression into a 6-field scheduler, everything shifts one position and the job runs at the wrong time or fails to parse. Know which dialect your tool expects. The &lt;a href="https://devrivettools.com/cron-generator" rel="noopener noreferrer"&gt;Cron Expression Generator&lt;/a&gt; works with the standard 5-field format.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test before you trust it
&lt;/h2&gt;

&lt;p&gt;Before deploying, confirm the next few run times in the timezone your server actually uses. For &lt;code&gt;0 0 * * *&lt;/code&gt;, consecutive runs should be 24 hours apart in local clock time, each at 00:00. The exception is a DST-observing zone, where the real elapsed time between two runs can be 23 or 25 hours around the transition dates. If you log timestamps from the job, you can convert raw epoch values with the &lt;a href="https://devrivettools.com/timestamp-converter" rel="noopener noreferrer"&gt;Unix Timestamp Converter&lt;/a&gt; to check what really ran.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is midnight 0 0 or 0 24 in cron?
&lt;/h3&gt;

&lt;p&gt;It's &lt;code&gt;0 0&lt;/code&gt;. The hour field accepts 0 through 23, so &lt;code&gt;24&lt;/code&gt; is invalid. Midnight at the start of a day is hour 0.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I run a cron job at 11:59 PM every day?
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;59 23 * * *&lt;/code&gt;. That runs at the end of each day, one minute before midnight.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why didn't my midnight cron job run?
&lt;/h3&gt;

&lt;p&gt;The usual causes are a timezone mismatch (the server's midnight isn't yours), a relative path or missing environment variable in the command, an unescaped &lt;code&gt;%&lt;/code&gt;, or a syntax error in the crontab. Redirect output to a log file and check &lt;code&gt;grep CRON /var/log/syslog&lt;/code&gt; on Debian and Ubuntu, or &lt;code&gt;journalctl -u cron&lt;/code&gt; on systems using journald.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's the difference between @daily and &lt;a class="mentioned-user" href="https://dev.to/midnight"&gt;@midnight&lt;/a&gt;?
&lt;/h3&gt;

&lt;p&gt;Nothing. In implementations that support them, both are aliases for &lt;code&gt;0 0 * * *&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the cron expression for midnight in GitHub Actions?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;0 0 * * *&lt;/code&gt; is midnight UTC. For midnight in your own zone, shift the hour by your UTC offset: &lt;code&gt;0 15 * * *&lt;/code&gt; for midnight in Seoul (UTC+9), or &lt;code&gt;0 5 * * *&lt;/code&gt; for midnight EST in New York (UTC-5), which becomes 1:00 AM local time during daylight saving time. Check any expression in the &lt;a href="https://devrivettools.com/cron-generator" rel="noopener noreferrer"&gt;Cron Expression Generator&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://devrivettools.com/blog/cron-every-day-at-midnight" rel="noopener noreferrer"&gt;DevRivet&lt;/a&gt;. You can test any cron expression in the browser with the free &lt;a href="https://devrivettools.com/cron-generator" rel="noopener noreferrer"&gt;Cron Expression Generator&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>devops</category>
      <category>github</category>
      <category>cron</category>
    </item>
    <item>
      <title>Why Your Cron Job Runs at the Wrong Time (Even When the Expression Is Correct)</title>
      <dc:creator>lifeissoup</dc:creator>
      <pubDate>Sat, 24 Oct 2026 05:19:41 +0000</pubDate>
      <link>https://dev.to/_4e8d848ae45b3c9c6eef9/why-your-cron-job-runs-at-the-wrong-time-even-when-the-expression-is-correct-51f9</link>
      <guid>https://dev.to/_4e8d848ae45b3c9c6eef9/why-your-cron-job-runs-at-the-wrong-time-even-when-the-expression-is-correct-51f9</guid>
      <description>&lt;p&gt;You write a cron expression, deploy your application, and expect a task to run at 9 AM.&lt;/p&gt;

&lt;p&gt;But it runs at 6 PM.&lt;/p&gt;

&lt;p&gt;The expression is valid. The scheduler is working.&lt;/p&gt;

&lt;p&gt;So what went wrong?&lt;/p&gt;

&lt;p&gt;Often, the problem isn't the cron syntax. It's the assumptions around it.&lt;/p&gt;

&lt;p&gt;Here are three common scheduling mistakes.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A cron expression doesn't specify a timezone&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Consider this expression:&lt;/p&gt;

&lt;p&gt;0 9 * * *&lt;/p&gt;

&lt;p&gt;In a traditional five-field cron format, this means:&lt;/p&gt;

&lt;p&gt;Run every day at 09:00.&lt;/p&gt;

&lt;p&gt;But 09:00 in which timezone?&lt;/p&gt;

&lt;p&gt;That depends on the environment and scheduler configuration.&lt;/p&gt;

&lt;p&gt;If your server interprets the schedule in UTC, the task runs at 09:00 UTC.&lt;/p&gt;

&lt;p&gt;For users in South Korea, that's 18:00 KST.&lt;/p&gt;

&lt;p&gt;The cron expression itself isn't wrong. It just doesn't contain the timezone information you expected.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Every 15 minutes doesn't mean 15 minutes after deployment&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Consider:&lt;/p&gt;

&lt;p&gt;*/15 * * * *&lt;/p&gt;

&lt;p&gt;This means the job runs at minute 0, 15, 30, and 45 of each hour.&lt;/p&gt;

&lt;p&gt;If you deploy at 10:07, it doesn't wait until 10:22.&lt;/p&gt;

&lt;p&gt;The next scheduled execution is 10:15.&lt;/p&gt;

&lt;p&gt;This difference matters when developers confuse fixed schedules with interval timers.&lt;/p&gt;

&lt;p&gt;Cron describes matching clock times, not elapsed time since an application started.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Daylight saving time can change the result&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Imagine a daily task scheduled for 02:30 in a timezone that observes daylight saving time.&lt;/p&gt;

&lt;p&gt;On the spring transition, that local time may not exist.&lt;/p&gt;

&lt;p&gt;During the autumn transition, some local times occur twice.&lt;/p&gt;

&lt;p&gt;Different schedulers handle these situations differently.&lt;/p&gt;

&lt;p&gt;Depending on the implementation and configuration, a job can be skipped, repeated, or adjusted.&lt;/p&gt;

&lt;p&gt;For time-sensitive operations, never assume that every local day contains exactly 24 hours.&lt;/p&gt;

&lt;p&gt;How to avoid surprises&lt;/p&gt;

&lt;p&gt;Before deploying a cron schedule:&lt;/p&gt;

&lt;p&gt;Confirm whether your scheduler uses UTC, server-local time, or a configured timezone.&lt;/p&gt;

&lt;p&gt;Check the next several execution times.&lt;/p&gt;

&lt;p&gt;Verify whether your scheduler uses five-field or six-field expressions.&lt;/p&gt;

&lt;p&gt;Document your intended timezone alongside the schedule.&lt;/p&gt;

&lt;p&gt;Test daylight saving transitions if the job operates in affected regions.&lt;/p&gt;

&lt;p&gt;For example, this JavaScript snippet shows how to display a UTC timestamp in Seoul time:&lt;/p&gt;

&lt;p&gt;const date = new Date("2026-10-23T09:00:00Z");&lt;/p&gt;

&lt;p&gt;const formatted = new Intl.DateTimeFormat("en-GB", {&lt;br&gt;
  timeZone: "Asia/Seoul",&lt;br&gt;
  dateStyle: "medium",&lt;br&gt;
  timeStyle: "short"&lt;br&gt;
}).format(date);&lt;/p&gt;

&lt;p&gt;console.log(formatted);&lt;/p&gt;

&lt;p&gt;It displays 18:00 in Seoul.&lt;/p&gt;

&lt;p&gt;When debugging a scheduled task, always separate two questions:&lt;/p&gt;

&lt;p&gt;What does the cron expression mean?&lt;/p&gt;

&lt;p&gt;Which timezone does the scheduler use to interpret it?&lt;/p&gt;

&lt;p&gt;Both need to be correct.&lt;/p&gt;

&lt;p&gt;Preview your cron schedule&lt;/p&gt;

&lt;p&gt;I built a browser-based Cron Generator as part of DevRivet to help create expressions, understand their meaning, and preview upcoming execution times.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://devrivettools.com/cron-generator" rel="noopener noreferrer"&gt;https://devrivettools.com/cron-generator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's a convenient way to check expressions before using them in your application. Always verify the actual scheduler's timezone and cron dialect before deployment.&lt;/p&gt;

&lt;p&gt;What's the most confusing cron scheduling issue you've encountered?&lt;/p&gt;

</description>
      <category>devops</category>
      <category>programming</category>
      <category>webdev</category>
      <category>cron</category>
    </item>
    <item>
      <title>Generating 1,000 UUIDs for Test Data? Check These 4 Things First</title>
      <dc:creator>lifeissoup</dc:creator>
      <pubDate>Sat, 24 Oct 2026 05:18:45 +0000</pubDate>
      <link>https://dev.to/_4e8d848ae45b3c9c6eef9/generating-1000-uuids-for-test-data-check-these-4-things-first-2bji</link>
      <guid>https://dev.to/_4e8d848ae45b3c9c6eef9/generating-1000-uuids-for-test-data-check-these-4-things-first-2bji</guid>
      <description>&lt;p&gt;Sometimes you don't need another test-data library. You just need 1,000 unique identifiers to populate a database, test an API, or build a realistic mock dataset.&lt;/p&gt;

&lt;p&gt;Generating them is easy. Making sure they're suitable for your tests takes a little more thought.&lt;/p&gt;

&lt;p&gt;Here are four things worth checking.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Choose the right UUID version&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;UUID v4 and UUID v7 are both 128-bit identifiers, but they have different characteristics.&lt;/p&gt;

&lt;p&gt;UUID v4 is random-based.&lt;/p&gt;

&lt;p&gt;It's a straightforward choice when you need independent identifiers without encoding creation time.&lt;/p&gt;

&lt;p&gt;UUID v7 includes a Unix timestamp in milliseconds, followed by additional bits.&lt;/p&gt;

&lt;p&gt;This makes v7 identifiers naturally group by generation time, which may improve index locality in certain database workloads.&lt;/p&gt;

&lt;p&gt;However, v7 doesn't guarantee exact generation order for identifiers created within the same millisecond unless the generator implements additional monotonic behavior.&lt;/p&gt;

&lt;p&gt;For disposable test data, either version can work. Choose based on what your application actually expects.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check for duplicates&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Even if your UUID generator is designed to produce unique values, verifying the output is useful when preparing a test fixture.&lt;/p&gt;

&lt;p&gt;In JavaScript, you can check a generated list like this:&lt;/p&gt;

&lt;p&gt;const ids = [&lt;br&gt;
  // Paste your generated UUID strings here&lt;br&gt;
];&lt;/p&gt;

&lt;p&gt;const uniqueCount = new Set(&lt;br&gt;
  ids.map(id =&amp;gt; id.toLowerCase())&lt;br&gt;
).size;&lt;/p&gt;

&lt;p&gt;console.log({&lt;br&gt;
  total: ids.length,&lt;br&gt;
  unique: uniqueCount,&lt;br&gt;
  duplicates: ids.length - uniqueCount&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;A proper test fixture should contain the number of unique identifiers you expected.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Validate the UUID format&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A string containing hexadecimal characters isn't automatically a valid UUID.&lt;/p&gt;

&lt;p&gt;For a simple format check supporting UUID v4 and v7:&lt;/p&gt;

&lt;p&gt;const uuidPattern =&lt;br&gt;
  /^[0-9a-f]{8}-[0-9a-f]{4}-[47][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;&lt;/p&gt;

&lt;p&gt;const invalidIds = ids.filter(&lt;br&gt;
  id =&amp;gt; !uuidPattern.test(id)&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;console.log("Invalid IDs:", invalidIds);&lt;/p&gt;

&lt;p&gt;This checks the canonical structure, version, and variant bits. It doesn't prove that an identifier was generated securely.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Don't confuse UUIDs with security tokens&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;UUIDs are identifiers, not a replacement for authorization.&lt;/p&gt;

&lt;p&gt;Even if an identifier is difficult to guess, your application still needs proper access controls.&lt;/p&gt;

&lt;p&gt;UUID v7 also reveals an approximate creation timestamp, which can matter if creation time is sensitive.&lt;/p&gt;

&lt;p&gt;For password reset tokens or API secrets, use a security-specific token-generation approach instead of treating ordinary database IDs as secrets.&lt;/p&gt;

&lt;p&gt;Generating test IDs quickly&lt;/p&gt;

&lt;p&gt;For UUID v4, modern JavaScript environments provide:&lt;/p&gt;

&lt;p&gt;const ids = Array.from(&lt;br&gt;
  { length: 1000 },&lt;br&gt;
  () =&amp;gt; crypto.randomUUID()&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;If you specifically need UUID v7, use a generator that implements RFC 9562.&lt;/p&gt;

&lt;p&gt;I built a small browser-based tool that supports UUID v4, UUID v7, bulk generation, and validation without requiring a signup:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://devrivettools.com/uuid-generator" rel="noopener noreferrer"&gt;https://devrivettools.com/uuid-generator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's useful when you want to prepare sample IDs without setting up a separate script.&lt;/p&gt;

&lt;p&gt;What do you normally use for database IDs: integers, UUID v4, or UUID v7?&lt;/p&gt;

</description>
      <category>uuid</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Why a Regex Tester Can Freeze Your Browser Tab</title>
      <dc:creator>lifeissoup</dc:creator>
      <pubDate>Fri, 23 Oct 2026 03:39:45 +0000</pubDate>
      <link>https://dev.to/_4e8d848ae45b3c9c6eef9/why-a-regex-tester-can-freeze-your-browser-tab-12el</link>
      <guid>https://dev.to/_4e8d848ae45b3c9c6eef9/why-a-regex-tester-can-freeze-your-browser-tab-12el</guid>
      <description>&lt;p&gt;nnot interrupt a synchronous regex operation that is already blocking the same thread.&lt;/p&gt;

&lt;p&gt;A practical approach: Web Workers&lt;/p&gt;

&lt;p&gt;One way to reduce the risk of freezing the interface is to move regex execution into a dedicated Web Worker.&lt;/p&gt;

&lt;p&gt;The main page sends the pattern, flags, and test text to the worker. The worker evaluates the expression and returns the matches.&lt;/p&gt;

&lt;p&gt;If execution takes too long, the main page can terminate the worker.&lt;/p&gt;

&lt;p&gt;For example, a regex tester can enforce a 1.5-second limit, then show a timeout message rather than leaving the interface frozen.&lt;/p&gt;

&lt;p&gt;This approach has limitations. A timeout does not prove that a regex is efficient or safe to use with arbitrary production input. It only provides a useful execution boundary for the testing interface.&lt;/p&gt;

&lt;p&gt;Other useful safeguards&lt;/p&gt;

&lt;p&gt;A practical regex tester should also handle invalid expressions, zero-length matches, and large result sets.&lt;/p&gt;

&lt;p&gt;Capping the displayed match count prevents an unexpectedly large match list from overwhelming the interface.&lt;/p&gt;

&lt;p&gt;Capture groups and match positions are especially useful when debugging expressions against realistic sample data.&lt;/p&gt;

&lt;p&gt;Try it in a browser&lt;/p&gt;

&lt;p&gt;I recently built a small browser-based Regex Tester as part of DevRivet. It runs JavaScript regex tests in a Web Worker with a 1.5-second timeout, match highlighting, and capture-group inspection.&lt;/p&gt;

&lt;p&gt;You can try it here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://devrivettools.com/regex-tester" rel="noopener noreferrer"&gt;https://devrivettools.com/regex-tester&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'd be interested in hearing what safeguards or debugging features you find most useful in regex testing tools.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>devtools</category>
    </item>
  </channel>
</rss>
