Skip to content 99% OFF 🎉 Anniversary Sale 99% OFF Shared Hosting Use Code HURRYUP Claim Offer 99% OFF Hosting
99% OFF Hosting — Code HURRYUP
Products
AI Website Builder New VPS Hosting Cloud Servers Web Hosting cPanel Hosting Dedicated Servers Domains
Company
About Documentation Support Center Contact Get Started Call +91 75795 45488
Login
Hosting Panel — cPanel & Billing Console Panel — VPS Management
ALL SYSTEMS OPERATIONAL
VPS

VPS Timezone Wrong? Fix Cron, PHP & MySQL Time Mismatches

Getwebup 7 min read

Your VPS clock is accurate, NTP is synced, and timedatectl shows the right time - yet cron jobs fire at the wrong hour, WordPress post timestamps are off by five and a half hours, and MySQL's NOW() doesn't match what PHP just printed. That's not drift. That's timezone, and on a typical stack it's set in four or five different places that don't automatically agree with each other.

Symptom: the clock is right, but everything built on top of it isn't

This shows up in ways that don't immediately point at timezone, which is why it drags on:

  • A cron job scheduled for 2:00 AM runs at 7:30 AM (or 8:30 AM) instead
  • WordPress shows a post as "published" hours before or after you actually clicked Publish
  • MySQL's NOW() or CURRENT_TIMESTAMP disagrees with PHP's date() on the same server
  • Log timestamps in Nginx/Apache access logs don't line up with the app's own logs, making it hard to correlate an error with the request that caused it
  • A Docker container on the box reports a completely different time than the host
  • Scheduled reports or invoices land in a customer's inbox at 3 AM their time instead of 9 AM

Run date and timedatectl status and everything looks fine. The system clock isn't the problem - the timezone each layer is configured to use is.

Cause: your stack has 4-5 independent timezone settings

A "VPS" is really an OS, a cron daemon, a web server, a database, PHP, and usually an application - and every one of those keeps its own idea of what timezone to render times in. Setting one doesn't touch the others.

LayerWhere it's setDefault if untouched
Operating system/etc/timezone, timedatectlUTC on most fresh cloud images
PHPdate.timezone in php.iniUTC, or a warning if unset
MySQL/MariaDBtime_zone system variableSYSTEM (follows the OS, but only if named zone tables are loaded)
CronSystem tz by default, or a TZ= line in the crontabFollows /etc/timezone unless overridden
WordPressSettings → General → Timezone (stored in wp_options)UTC+0 on a fresh install
Docker containersContainer's own /etc/localtimeUTC, regardless of the host

Most of the confusing cases come from one of three patterns:

  • Migration or snapshot restore - a VPS cloned from a template inherits the template's timezone, not the one you actually want, and nobody rechecks it.
  • WordPress vs server disagreement - WP stores post dates in UTC internally (post_date_gmt) and converts for display using its own Settings → General value. If that value doesn't match the server's actual timezone, cron-triggered actions and displayed times drift apart even though both are "correct" by their own setting.
  • Cron running as a different user or in a minimal environment - some cron daemons don't inherit the full environment, so a TZ variable set in your shell profile never reaches the crontab.

Fix: check and align each layer explicitly

1. Confirm the OS timezone

timedatectl status

Check three fields: Local time (compare against your phone), Time zone (what it's actually set to), and System clock synchronized: yes. To change it:

timedatectl list-timezones | grep Kolkata
sudo timedatectl set-timezone Asia/Kolkata

For most production servers, set this to UTC and let the application layer handle local display - see Prevention below for why.

2. Fix PHP's timezone

php -i | grep date.timezone

If it's blank or wrong, edit php.ini directly, or use cPanel's MultiPHP INI Editor if you don't have root:

date.timezone = Asia/Kolkata

Avoid patching this with date_default_timezone_set() calls scattered through the codebase - it works, but it only fixes the one script that calls it, and the next developer won't know it's there. Fix it once in php.ini and restart PHP-FPM:

sudo systemctl restart php8.2-fpm

3. Fix MySQL's timezone

SELECT @@global.time_zone, @@session.time_zone;

If both show SYSTEM, MySQL is just following the OS setting from step 1 - which is fine as long as you've confirmed that's correct. To pin it explicitly instead (recommended, since it survives OS-level timezone changes without silently shifting your data):

SET GLOBAL time_zone = '+00:00';

Make it persistent by adding to my.cnf:

[mysqld]
default-time-zone = '+00:00'

If you want to use named zones like Asia/Kolkata instead of a raw offset, load the timezone tables first - a bare MySQL install often has them empty:

mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql

4. Fix cron's timezone

System cron normally follows /etc/timezone, but you can pin an individual crontab regardless of what the OS is set to - useful when one script genuinely needs to run on IST while the server itself runs on UTC:

crontab -e
# add this as the first line
TZ=Asia/Kolkata
0 2 * * * /usr/bin/php /home/user/scripts/nightly.php

Not every cron implementation honors a TZ= line the same way, so after adding it, confirm with a one-off test job that echoes date to a log file before you trust the real schedule.

5. Fix WordPress's timezone

Go to Settings → General → Timezone and pick your actual city (not just a UTC offset - city-based zones handle daylight saving automatically, offsets don't). Verify from the CLI if you have WP-CLI:

wp option get timezone_string
wp option update timezone_string "Asia/Kolkata"

This setting only affects display and scheduling logic in WP - the database still stores post_date_gmt in UTC, which is correct behavior. The mismatch happens when this value doesn't match what your server-side cron or reports assume.

6. Fix Docker containers

Containers default to UTC no matter what the host is set to. Either mount the host's timezone data in read-only, or set the TZ environment variable (simpler, no volume mount needed on most modern base images):

docker run -e TZ=Asia/Kolkata your-image
# or in docker-compose.yml
environment:
  - TZ=Asia/Kolkata

Prevention: standardize on UTC at the infrastructure layer

The fix that actually stops this from recurring isn't "set everything to IST" - it's the opposite. Keep the OS, MySQL, and PHP on UTC, and let the one layer that talks to humans - WordPress's timezone setting, your application's user-locale conversion, or your reporting tool - handle the conversion to local time for display. That way:

  • Log correlation across web server, PHP, and database always lines up, since every timestamp is in the same zone
  • Database replication and backups restored on a server in a different region don't silently shift your data
  • Daylight saving transitions can't cause a cron job to run twice or not at all, because UTC never observes DST

A few habits that catch this early instead of six months later:

  • Check timedatectl status as a standard step right after provisioning a new VPS or restoring a snapshot - don't assume it inherited the setting you expect
  • Put the timezone check in your deployment or provisioning script so it's enforced, not remembered
  • When something is "off by a fixed number of hours," check timezone before you check anything else - a fixed offset almost never means drift

If you're on Getwebup VPS hosting and want a hand auditing which layer is misconfigured, our support team can pull the actual values from your server rather than guessing from symptoms - open a ticket with the exact behavior you're seeing (which cron job, what time it ran vs. expected) and we'll trace it end to end.

Frequently asked questions

Should I set my VPS timezone to UTC or my local time zone?

UTC, for the server itself. Keep the OS, MySQL, and PHP on UTC and let the application layer (WordPress's timezone setting, your app's locale conversion) handle local display. This avoids daylight-saving edge cases and keeps logs across services aligned to the same clock.

Why does WordPress show the right time but my cron jobs run at the wrong hour?

WordPress's Settings > General timezone only controls how WP displays and schedules its own actions (like wp-cron). System cron jobs follow the OS timezone in /etc/timezone unless you set a TZ= line explicitly in the crontab. The two are independent, so it's normal for one to be right and the other wrong until you align both.

I set the system timezone but MySQL still shows wrong time - why?

MySQL's time_zone variable is SYSTEM by default, which should follow the OS - but if it was previously pinned to a fixed offset or named zone, it won't automatically pick up your OS change. Run SELECT @@global.time_zone; to check, and either reset it to SYSTEM or explicitly set the zone you want.

Does changing the VPS timezone affect existing log files or database timestamps?

No - it only changes how new timestamps are generated and how some tools display existing ones. Rows already written to the database keep the values they were stored with; changing time_zone doesn't retroactively rewrite stored data, it only changes interpretation for zone-aware types going forward.

Why does my Docker container show a different time than the host VPS?

Containers default to UTC internally regardless of the host's timezone setting, since they don't automatically inherit /etc/localtime. Set the TZ environment variable on the container (or mount the host's timezone files read-only) to match it to the host or to whatever zone the app actually needs.

#vps #timezone #cron-jobs #mysql #php-ini #linux

Keep reading

Chat with Support