ตั้ง Cron Job รัน PHP Script บน DirectAdmin

Cron Job คือระบบที่ช่วยให้เซิร์ฟเวอร์รันสคริปต์อัตโนมัติตามเวลาที่กำหนด ไม่ว่าจะเป็นการส่งอีเมลรายวัน ล้าง Cache ทุกคืน สำรองข้อมูล หรือรัน PHP Script ตามกำหนดการ บทความนี้จะสอนวิธีตั้งค่า Cron Job บน DirectAdmin ทีละขั้นตอน

Cron Job คืออะไร

Cron เป็น Time-based Job Scheduler บน Linux ที่รัน Task ตามเวลาที่กำหนด การกำหนดเวลาใช้รูปแบบ Cron Expression 5 ฟิลด์:

* * * * * command
│ │ │ │ └── วันในสัปดาห์ (0-7, 0/7 = อาทิตย์)
│ │ │ └──── เดือน (1-12)
│ │ └────── วันในเดือน (1-31)
│ └──────── ชั่วโมง (0-23)
└────────── นาที (0-59)

ตัวอย่าง Cron Expression ที่ใช้บ่อย

Cron Job สำคัญอย่างไรต่อการพัฒนาเว็บมืออาชีพ

หลายคนเริ่มพัฒนาเว็บโดยทำทุกอย่างแบบ Manual คือกด Export ข้อมูล ส่งอีเมล หรือล้าง Cache ด้วยตนเอง แต่เมื่อเว็บโตขึ้นมีผู้ใช้มากขึ้น การทำงานแบบ Manual ทำได้ยากและเกิดข้อผิดพลาดบ่อย Cron Job คือสิ่งที่เปลี่ยน "งานซ้ำๆ ที่ต้องทำด้วยมือ" ให้กลายเป็น "ระบบที่ดูแลตัวเอง"

ตัวอย่างงานที่ควรเปลี่ยนมาใช้ Cron Job ในโปรเจกต์ไทย เช่น ส่ง SMS หรืออีเมลเตือนการชำระเงินที่ใกล้ครบกำหนด สำรองข้อมูลลูกค้าก่อน server maintenance รีเฟรชอัตราแลกเปลี่ยนหรือราคาสินค้าจาก API ภายนอกตามรอบเวลา ส่งรายงาน Google Analytics หรือ Sales Report ให้ผู้บริหารทุกเช้า หรือ Archive log ไฟล์ที่เก่าเกิน 30 วันเพื่อประหยัดพื้นที่ disk

การมี Cron Job ที่ทำงานถูกต้องยังช่วยเพิ่มความน่าเชื่อถือของระบบ เพราะลูกค้าได้รับการแจ้งเตือนตรงเวลา ข้อมูลถูกอัพเดตสม่ำเสมอ และระบบไม่ขึ้นกับการกระทำของคนดูแล ทำให้ดูเป็นมืออาชีพและลดภาระงานของทีมได้อย่างมาก

เปรียบเทียบ Cron Expression รูปแบบต่างๆ ที่ใช้บ่อย

นอกจาก 5 ฟิลด์มาตรฐานแล้ว DirectAdmin ยังรองรับ Cron Expression แบบขยายที่ช่วยให้กำหนดเวลาได้ยืดหยุ่นขึ้น ตารางด้านล่างรวม Pattern ที่นักพัฒนาไทยใช้บ่อยในงาน Hosting จริง

Expression ความหมาย Use Case
*/5 * * * * ทุก 5 นาที Queue processing, WP-Cron แทน
0 */6 * * * ทุก 6 ชั่วโมง Sync ข้อมูล, ล้าง Cache
0 2 * * * ทุกวันเวลา 02:00 Backup ฐานข้อมูลรายคืน
0 9 * * 1-5 จันทร์-ศุกร์ เวลา 09:00 รายงาน Daily Business
0 0 1,15 * * วันที่ 1 และ 15 ของเดือน ออก Invoice รายปักษ์
0 0 L * * วันสุดท้ายของเดือน สรุปรายงานสิ้นเดือน

เครื่องมือช่วยสร้าง Cron Expression: ถ้าไม่แน่ใจ syntax ลองใช้ Cron Expression Generator ของ AsiaGB — กรอกเวลาที่ต้องการแล้วระบบสร้าง expression ให้อัตโนมัติ

วิธีตั้ง Cron Job ใน DirectAdmin

  1. เข้า DirectAdmin Panel
  2. ไปที่ Advanced Features → Cron Jobs
  3. กรอก Schedule ในช่อง Minute / Hour / Day / Month / Weekday
  4. ใส่ Command ที่ต้องการ
  5. คลิก Add

Command สำหรับรัน PHP Script

รูปแบบ 1 — PHP Binary โดยตรง

/usr/local/php83/bin/php /home/username/public_html/cron/script.php

รูปแบบ 2 — php-cli

/usr/bin/php /home/username/public_html/cron/script.php

รูปแบบ 3 — wget (HTTP-based)

wget -q -O /dev/null "https://yourdomain.com/cron/run.php" > /dev/null 2>&1

รูปแบบ 4 — curl

curl -s "https://yourdomain.com/cron/run.php" > /dev/null 2>&1

เคล็ดลับ: ใช้ Path เต็มเสมอสำหรับ PHP binary และไฟล์ Script เพราะ Cron ไม่มี PATH environment เหมือน Shell ปกติ ถ้าไม่แน่ใจ PHP path ให้รัน which php ใน SSH

สร้าง PHP Script ที่เหมาะสำหรับ Cron

Script ที่ถูกออกแบบมาเพื่อรันผ่าน Cron ควรมีลักษณะดังนี้ เพื่อป้องกันปัญหาที่พบบ่อยบน Shared Hosting

ป้องกัน Script รันซ้อนกัน (Locking)

ถ้า Cron รันทุก 5 นาทีแต่ script ทำงานนานกว่า 5 นาที จะเกิดการรันซ้อนกันและทำให้เซิร์ฟเวอร์ช้าลง ใช้ lock file แก้ปัญหานี้

<?php
// ป้องกัน Cron รันซ้อนกัน
$lockFile = '/home/username/cron.lock';
if (file_exists($lockFile)) {
    // ตรวจสอบว่า lock ค้างนานเกิน 10 นาทีหรือไม่
    if (time() - filemtime($lockFile) < 600) {
        exit("Script is already running\n");
    }
}
file_put_contents($lockFile, getmypid());

// โค้ด main
try {
    // ... งานหลัก ...
} finally {
    unlink($lockFile); // ลบ lock เสมอแม้เกิด error
}

จัดการ Error และส่ง Notification

Cron ที่ดีควรแจ้งเตือนเมื่อเกิดข้อผิดพลาด ไม่ใช่เงียบหาย

<?php
set_error_handler(function($errno, $errstr, $errfile, $errline) {
    $msg = date('Y-m-d H:i:s')." ERROR [$errno] $errstr in $errfile:$errline\n";
    file_put_contents('/home/username/cron-error.log', $msg, FILE_APPEND);
    // ส่งอีเมลแจ้งเตือน (ถ้าต้องการ)
    mail('[email protected]', 'Cron Error', $msg);
});

register_shutdown_function(function() {
    $error = error_get_last();
    if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR])) {
        $msg = date('Y-m-d H:i:s')." FATAL: ".$error['message']."\n";
        file_put_contents('/home/username/cron-error.log', $msg, FILE_APPEND);
    }
});

กำหนด Timezone ให้ถูกต้อง

Cron ใช้ Timezone ของ server (มักเป็น UTC) ถ้าต้องการให้สคริปต์รันตามเวลาไทยต้องตั้งค่าอย่างชัดเจน

<?php
date_default_timezone_set('Asia/Bangkok');
echo date('Y-m-d H:i:s'); // แสดงเวลาไทย (UTC+7)

// สำหรับ MySQL — ตั้ง timezone ของ connection ด้วย
$pdo = new PDO('mysql:host=localhost;dbname=mydb', 'user', 'pass');
$pdo->exec("SET time_zone = '+07:00'")

ตัวอย่าง Cron Job จริงที่ใช้งาน

ส่งอีเมลรายวันเวลา 08:00 น.

0 8 * * * /usr/bin/php /home/username/public_html/send-email.php

ล้าง Cache ทุกคืนเที่ยงคืน

0 0 * * * /usr/bin/php /home/username/public_html/clear-cache.php

ใช้ Real Cron แทน WP-Cron ของ WordPress

เพิ่มใน wp-config.php: define('DISABLE_WP_CRON', true);

จากนั้นตั้ง Cron Job:

*/5 * * * * /usr/bin/php /home/username/public_html/wp-cron.php > /dev/null 2>&1

ตัวอย่าง PHP Script สำหรับงาน Cron ที่ใช้บ่อยในเว็บไทย

ต่อไปนี้คือตัวอย่าง Script ที่นักพัฒนาเว็บไทยใช้งานจริงผ่าน Cron Job บน Shared Hosting เขียนขึ้นเพื่อรองรับการทำงานอัตโนมัติโดยไม่ต้องมีคนคอยกด

Script Backup ฐานข้อมูล MySQL รายวัน

สำรองข้อมูลฐานข้อมูลทุกคืนโดยอัตโนมัติ พร้อมลบ Backup เก่าที่เกิน 7 วันเพื่อประหยัดพื้นที่

<?php
date_default_timezone_set('Asia/Bangkok');
$date    = date('Y-m-d');
$host    = 'localhost';
$user    = 'db_user';
$pass    = 'db_pass';
$dbname  = 'mydb';
$outFile = "/home/username/backups/backup_{$date}.sql";

// สร้างโฟลเดอร์ถ้ายังไม่มี
if (!is_dir('/home/username/backups')) {
    mkdir('/home/username/backups', 0755, true);
}

// mysqldump ผ่าน shell_exec
$cmd = "mysqldump -h{$host} -u{$user} -p{$pass} {$dbname} > {$outFile} 2>&1";
shell_exec($cmd);

// ลบ Backup เก่าเกิน 7 วัน
foreach (glob('/home/username/backups/backup_*.sql') as $file) {
    if (filemtime($file) < strtotime('-7 days')) {
        unlink($file);
    }
}
file_put_contents('/home/username/backup.log',
    date('Y-m-d H:i:s')." - Backup สำเร็จ: $outFile\n", FILE_APPEND);

Script ส่ง Daily Report ทาง Email

รวบรวมข้อมูลยอดขายหรือ statistics จากฐานข้อมูลแล้วส่งรายงานทางอีเมลให้ admin ทุกเช้า

<?php
date_default_timezone_set('Asia/Bangkok');
require_once __DIR__ . '/vendor/autoload.php';

use PHPMailer\PHPMailer\PHPMailer;

// ดึงข้อมูลจาก Database
$pdo = new PDO('mysql:host=localhost;dbname=shop', 'user', 'pass');
$pdo->exec("SET time_zone = '+07:00'");

$stmt = $pdo->query("
    SELECT COUNT(*) as orders, SUM(total) as revenue
    FROM orders
    WHERE DATE(created_at) = CURDATE() - INTERVAL 1 DAY
");
$row = $stmt->fetch(PDO::FETCH_ASSOC);

// ส่งอีเมลรายงาน
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host     = 'mail.yourdomain.com';
$mail->Username = '[email protected]';
$mail->Password = 'password';
$mail->CharSet  = 'UTF-8';
$mail->setFrom('[email protected]', 'ระบบรายงาน');
$mail->addAddress('[email protected]');
$mail->Subject = 'รายงานยอดขายประจำวัน '.date('d/m/Y');
$mail->Body    = "<h2>สรุปยอดขายเมื่อวาน</h2>
<p>คำสั่งซื้อ: <strong>{$row['orders']} รายการ</strong></p>
<p>รายได้รวม: <strong>".number_format($row['revenue'])." บาท</strong></p>";
$mail->isHTML(true);
$mail->send();

Script อัพเดต Sitemap.xml อัตโนมัติ

สร้างและอัพเดต sitemap.xml ใหม่ทุกวันเพื่อให้ Search Engine รับรู้บทความใหม่ได้เร็วขึ้น

<?php
date_default_timezone_set('Asia/Bangkok');
$pdo = new PDO('mysql:host=localhost;dbname=blog', 'user', 'pass');
$stmt = $pdo->query("SELECT slug, updated_at FROM posts WHERE published = 1 ORDER BY updated_at DESC");
$posts = $stmt->fetchAll(PDO::FETCH_ASSOC);

$xml = '<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL;
$xml .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . PHP_EOL;
foreach ($posts as $post) {
    $lastmod = date('Y-m-d', strtotime($post['updated_at']));
    $xml .= "  <url><loc>https://yourdomain.com/blog/{$post['slug']}</loc>"
          . "<lastmod>{$lastmod}</lastmod></url>" . PHP_EOL;
}
$xml .= '</urlset>';
file_put_contents('/home/username/public_html/sitemap.xml', $xml);
echo date('Y-m-d H:i:s')." - อัพเดต sitemap.xml สำเร็จ ".count($posts)." URL\n";

ข้อจำกัดและ Best Practice บน Shared Hosting

Shared Hosting มีข้อจำกัดที่แตกต่างจาก VPS โดยสิ้นเชิง การเข้าใจข้อจำกัดเหล่านี้จะช่วยให้ตั้งค่า Cron ได้อย่างเหมาะสม

ประเด็น Shared Hosting แนวทางแก้ไข
ความถี่ต่ำสุด ทุก 1 นาที (DirectAdmin) เพียงพอสำหรับงานส่วนใหญ่
Execution Timeout มักถูกจำกัดที่ 60–300 วินาที ใส่ set_time_limit(0) ในสคริปต์
Memory Limit 128–512 MB ขึ้นอยู่กับแผน รัน php -d memory_limit=256M
Output ถ้าไม่ redirect อาจส่งเมลแจ้ง crontab owner เพิ่ม > /dev/null 2>&1 ท้าย command
Environment PATH Cron ไม่มี PATH เหมือน shell ปกติ ใช้ absolute path เสมอ

สำหรับงานที่ต้องทำงานนานมากกว่า 5 นาที ทำงานหนัก หรือต้องการ resource สูง ควรพิจารณาอัปเกรดไปใช้ VPS ซึ่งไม่มีข้อจำกัดเหล่านี้ และสามารถตั้ง Cron ผ่าน crontab ของ system ได้โดยตรง

การแก้ปัญหา Cron Job ที่ไม่ทำงาน

Cron Job ที่ตั้งค่าไว้แต่ไม่ทำงานเป็นปัญหาที่พบบ่อยมากบน Shared Hosting และมักเกิดจากสาเหตุที่แก้ไขได้ง่ายๆ ต่อไปนี้คือขั้นตอนการ debug แบบเป็นระบบ

ขั้นที่ 1 — ตรวจสอบ PHP Path

เชื่อมต่อผ่าน SSH แล้วรันคำสั่งนี้เพื่อหา path ที่ถูกต้อง

which php
which php83
/usr/local/php83/bin/php -v

คัดลอก path ที่ได้ไปใช้ใน Cron Command แทน /usr/bin/php ธรรมดา เพราะ Cron มีสภาพแวดล้อมที่ต่างจาก Shell ปกติ บาง Hosting ต้องระบุ path เต็มอย่าง /usr/local/php83/bin/php เท่านั้นจึงจะทำงาน

ขั้นที่ 2 — ทดสอบ Script บน Command Line ก่อน

ก่อนตั้ง Cron ให้รัน script ผ่าน SSH โดยตรงก่อนเสมอ เพื่อแน่ใจว่า script ทำงานได้โดยไม่มี error

/usr/bin/php /home/username/public_html/cron/script.php

ถ้ามี error ตอนนี้ Cron ก็จะเจอ error เดียวกัน แก้ให้ script รันได้ปกติก่อนแล้วค่อยตั้ง Cron

ขั้นที่ 3 — ตรวจสอบ Permission ของไฟล์

Script ที่ Cron รันต้องมี permission ที่ถูกต้อง ตรวจสอบและแก้ไขด้วยคำสั่ง

chmod 644 /home/username/public_html/cron/script.php
# โฟลเดอร์ที่ script เขียนไฟล์ต้องให้ write permission
chmod 755 /home/username/public_html/cron/

ขั้นที่ 4 — เพิ่ม Log เพื่อ Trace การทำงาน

ถ้า script รันได้จาก SSH แต่ยังไม่รันจาก Cron ให้เพิ่ม log บรรทัดแรกสุดของ script เพื่อดูว่า Cron เรียก script ถึงหรือไม่ และ environment ที่ script เห็นมีอะไรบ้าง

<?php
file_put_contents('/home/username/cron-debug.log',
    date('Y-m-d H:i:s')." | PHP: ".PHP_VERSION.
    " | USER: ".get_current_user().
    " | CWD: ".getcwd()."\n", FILE_APPEND);
// ... โค้ดหลัก

ตรวจสอบว่า Cron ทำงานถูกต้อง

ให้ Script เขียน Log ทุกครั้งที่รัน เพื่อตรวจสอบได้ภายหลัง:

<?php
file_put_contents('/home/username/cron.log',
    date('Y-m-d H:i:s') . " - Script ran\n", FILE_APPEND);
// ... your script code

Hosting รองรับ Cron Job เต็มรูปแบบ

AsiaGB Hosting รองรับ Cron Job ผ่าน DirectAdmin พร้อม PHP 7.4 / 8.2 / 8.3 เริ่มต้น 500 บาท/ปี Uptime 99%

ดูแพ็กเกจ Hosting