EST. 2026 — บันทึกงานเซิร์ฟเวอร์จริง SYSADMIN · SECURITY · AI · DIRECTADMIN เล่มที่ 01
THE SYSADMIN ALMANAC
หน้าหลัก / DirectAdmin / ตั้ง DirectAdmin Admin Backup cron จาก CLI ไม่ต้องกดผ่าน panel
DirectAdmin

ตั้ง DirectAdmin Admin Backup cron จาก CLI ไม่ต้องกดผ่าน panel

โดย Ton-Or ·24 ก.ค. 2026·อ่าน 8 นาที
ตั้ง DirectAdmin Admin Backup cron จาก CLI ไม่ต้องกดผ่าน panel

กด Submit ในหน้า CMD_ADMIN_BACKUP หนึ่งครั้ง เบื้องหลัง DirectAdmin แค่ต่อท้ายบรรทัดเดียวลงไฟล์ text ธรรมดา — พอรู้หน้าตาบรรทัดนั้น ผมก็ตั้ง backup cron ทีละสิบ user จาก shell ได้ ไม่ต้องนั่งคลิกทีละคน

พอดีต้องตั้ง Admin Backup ให้ลูกค้าหลายสิบ user บนเครื่อง DirectAdmin ตัวหนึ่ง ถ้าไปคลิกผ่านหน้า CMD_ADMIN_BACKUP ทีละคนคงหมดวัน ผมเลยไปนั่งดูว่าเวลากด Submit มันไปเขียนอะไรไว้ตรงไหน สรุปว่าปุ่ม Submit ในหน้านั้นทำงานง่ายกว่าที่คิดมาก — มันแค่ต่อท้าย (append) หนึ่งบรรทัดลงไฟล์ text ธรรมดา แล้วมี daemon ตัวหนึ่งวนอ่านไฟล์นี้เอาไปทำตามเวลา พอเข้าใจหน้าตาบรรทัดนั้น การตั้ง cron จาก shell ก็เป็นแค่การ echo ต่อท้ายไฟล์

ค่า user, IP, รหัส FTP ในบทความเป็นตัวอย่างทั้งหมด (host เป็น ftp.example.com) ของจริงห้ามเอาลงบทความ โครงสร้างบรรทัดกับกลไกคือของจริง

ไฟล์ที่ DA เก็บ cron ไว้จริง คือ backup_crons.list

Admin Backup cron ทุกอันไปกองรวมกันในไฟล์เดียว ไม่ได้ไปอยู่ใน crontab ของ root แบบที่หลายคนเดา:

ls -l /usr/local/directadmin/data/admin/backup_crons.list
# -rw------- 1 diradmin diradmin  842 ... backup_crons.list

จุดที่ต้องจำสามอย่างครับ — เจ้าของไฟล์ต้องเป็น diradmin:diradmin, สิทธิ์ต้องเป็น 600 (ในนี้มีรหัส FTP ห้ามให้ใครอ่าน) และตัวที่เอาไฟล์นี้ไปทำงานคือ daemon ชื่อ dataskq ซึ่งไม่ได้ทำงานทันที มันวนเช็คทุกๆ ประมาณ 60 วินาที ถ้าเราไปแก้ไฟล์แล้วเจ้าของหรือสิทธิ์เพี้ยน dataskq จะไม่แตะให้ ฉะนั้นแก้เสร็จต้องตั้ง owner/mode กลับทุกครั้ง

เส้นทางที่ cron หนึ่งอันเดินจริงbackup_crons.listเราเขียนบรรทัดนี้->dataskqวนเช็คทุก ~60 วิ->task.queueคิวงานจริง->FTP /Mondayไฟล์ backup ปลายทาง

หน้าตาแต่ละบรรทัด: id=พารามิเตอร์แบบ URL-encode

หนึ่งบรรทัดคือหนึ่ง cron รูปแบบคือ <id>=<พารามิเตอร์แบบ URL-encode> โดย <id> เป็นเลขจำนวนเต็มที่ไม่ซ้ำ (1, 2, 3, ...) เวลาจะเพิ่มอันใหม่ก็หาเลขสูงสุดที่มีแล้ว +1

# /usr/local/directadmin/data/admin/backup_crons.list
1=action=backup&hour=5&minute=0&dayofweek=%2A&where=local&...&select0=webuser1
2=action=backup&hour=3&minute=0&dayofweek=0&where=ftp&...&select0=webuser2&select1=webuser3

พารามิเตอร์ที่ขาดไม่ได้มีชุดนึง — action=backup เสมอ, type=admin (cron ระดับ admin), value=multiple (แม้เลือก user เดียวก็ต้อง multiple), owner=admin, แล้วก็ตารางเวลา hour minute dayofmonth dayofweek month และ where ว่าจะเก็บที่ ftp หรือ local ส่วน user ที่จะ backup ใส่เป็น select0, select1, select2 ไล่ index ไปเรื่อยๆ อย่าใช้ index ซ้ำเพราะมันจะทับกันเอง

ตรงนี้มีกับดักที่ผมงงอยู่นานครับ ในบรรทัดจะเห็น when=now ติดมาด้วยเสมอ — อย่าไปเข้าใจผิดว่ามันสั่งให้รันเดี๋ยวนี้ มันเป็นแค่ค่าที่ DA เขียนติดมา ตัวที่กำหนดเวลาจริงคือ hour/minute/dayofweek ต่างหาก

when=now

ของหลอกตา ไม่ใช่ตารางเวลา

ติดมากับทุกบรรทัดเสมอ ไม่ว่าจะตั้งให้รันตอนไหน เห็นแล้วข้ามไปได้เลย ไม่ต้องแก้

hour / minute / dayofweek

ตัวจริงที่บอกว่ารันเมื่อไหร่

นี่คือ artifact ที่มีผลจริง — dayofweek 0 คืออาทิตย์, %2A คือทุกวัน อยากได้ทุกวันตีสามก็ hour=3 minute=0 dayofweek=%2A

เรื่อง URL-encode ไม่ต้อง encode ทุกตัวอักษรแบบที่ panel ทำ (panel มัน encode หนักถึงขั้นแปลงตัวเลขเป็น %30) parser ของ DA รับทั้งสองแบบ ที่ต้อง encode จริงๆ มีแค่สามตัวที่จะไปชนไวยากรณ์ key=value&key=value คือ * เขียนเป็น %2A, / เป็น %2F, และ = เป็น %3D นอกนั้นเขียนดิบได้อ่านง่ายกว่า เดี๋ยว panel มัน re-encode ให้เองตอนเซฟรอบหน้า

FTP target: append_to_path กับรหัสผ่าน base64

ถ้าเก็บขึ้น FTP ต้องมีบล็อก ftp_* ครบ ไม่งั้น DA จะเงียบๆ ไม่ทำหรือ fallback ไป local สองจุดที่คนพลาดบ่อยคือรหัสผ่านกับ path ปลายทาง

รหัส FTP ในไฟล์เก็บเป็น base64 ไม่ใช่ plaintext และไม่ใช่ hash — มันถอดกลับได้ ใครอ่านไฟล์ได้ก็เห็นรหัส (นี่แหละเหตุผลว่าทำไม mode ต้อง 600) สร้าง token ด้วย printf อย่าใช้ echo เพราะ echo จะแถว newline ต่อท้ายทำให้ base64 เพี้ยน:

printf '%s' 'MockFtpPass_2026' | base64
# TW9ja0Z0cFBhc3NfMjAyNg==

ส่วน append_to_path เป็นของดีที่ควรใช้ ปกติถ้า backup ทุกวันลง path เดิม ไฟล์วันใหม่จะทับวันเก่า พอตั้ง append_to_path=dayofweek DA จะต่อชื่อวันเข้าไปให้เอง กลายเป็นว่าได้ backup หมุน 7 วันฟรีๆ โดยไม่ต้องเขียน logic เอง

append_to_path=dayofweek ทำงานยังไงftp_path /admin->+ ชื่อวันอัตโนมัติ->/admin/Monday ... /admin/Sundayวนเก็บ 7 วัน ไม่ทับกัน

ค่าอื่นที่ append_to_path รับได้คือ dayofmonth (ต่อ /01../31), date (/YYYY-MM-DD), datetime (/YYYY-MM-DD-HHMM) หรือ nothing ถ้าไม่อยากให้ต่ออะไร ส่วน ftp_path จำไว้ว่ามันคือ path เทียบกับ home ที่ chroot ของ user FTP ไม่ใช่ path จริงบนดิสก์

เพิ่ม cron ผ่าน shell ด้วยสคริปต์สั้นๆ

รวมทุกอย่างเป็นสคริปต์เดียว รับ hour, minute, dayofweek แล้วตามด้วย user ที่จะ backup กี่คนก็ได้ หัวใจอยู่ที่หาเลข id ถัดไป แล้วประกอบบรรทัดต่อท้ายไฟล์ ปิดท้ายด้วยการตั้ง owner/mode กลับให้ dataskq ยอมรับ:

#!/bin/bash
# da-add-backup-cron.sh <hour> <minute> <dayofweek> <user1> [user2] ...
set -euo pipefail
CRONS=/usr/local/directadmin/data/admin/backup_crons.list
HOUR="$1"; shift
MIN="$1";  shift
DOW="$1";  shift          # 0-7 หรือ %2A (ทุกวัน)
USERS=("$@")

# หาเลข id ถัดไป
NEXT=$(awk -F= 'NF>0{print $1}' "$CRONS" | sort -n | tail -1)
NEXT=$((NEXT + 1))

# ประกอบ selectN ให้แต่ละ user
SEL=""; i=0
for u in "${USERS[@]}"; do SEL+="&select${i}=${u}"; i=$((i+1)); done

# รหัส FTP ต้องเป็น base64 (ตัวอย่าง mock)
FTP_PW=$(printf '%s' 'MockFtpPass_2026' | base64)

LINE="${NEXT}=action=backup&hour=${HOUR}&minute=${MIN}&dayofmonth=%2A&dayofweek=${DOW}&month=%2A"
LINE+="&where=ftp&ftp_ip=ftp.example.com&ftp_port=21&ftp_username=backupuser&ftp_password=${FTP_PW}"
LINE+="&ftp_path=%2Fadmin&ftp_secure=no&append_to_path=dayofweek"
LINE+="&database_data_aware=yes&email_data_aware=yes&trash_aware=yes"
LINE+="&type=admin&value=multiple&owner=admin&when=now${SEL}"

cp "$CRONS" "${CRONS}.bak-$(date +%Y%m%d-%H%M)"   # สำรองก่อนแตะเสมอ
echo "$LINE" >> "$CRONS"
chown diradmin:diradmin "$CRONS"
chmod 600 "$CRONS"
echo "เพิ่ม cron id=${NEXT}: ${HOUR}:${MIN} dow=${DOW} users=${USERS[*]}"

เรียกใช้:

# ทุกอาทิตย์ตีสาม backup 2 user
./da-add-backup-cron.sh 3 0 0 webuser1 webuser2

# ทุกวันตีสอง user เดียว (dayofweek = %2A)
./da-add-backup-cron.sh 2 0 '%2A' webuser3

ถ้าจะตั้งให้ user หลายสิบคน ผมแนะนำให้ทยอยเวลาห่างกันสัก 5 นาทีต่อคน อย่าตั้งชนกันหมดตอนตีสองเป๊ะ เพราะ backup พร้อมกันทั้งเครื่อง I/O กับ RAM จะพุ่ง เว็บที่ตื่นอยู่ตอนนั้นจะอืดตาม วนลูป i*5 นาทีเอาก็พอ

ทดสอบเดี๋ยวนั้น ไม่ต้องรอ cron ถึงเวลา

ไม่อยากรอถึงตีสามเพื่อดูว่าตั้งถูกไหม มีทางลัดคือเขียนงานลง task.queue ตรงๆ ซึ่งเป็นคิวที่ dataskq หยิบไปทำในรอบถัดไป (~60 วินาที) โดยไม่เกี่ยวกับตารางเวลาเลย:

TASK="action=backup&type=admin&value=multiple&owner=admin&when=now"
TASK+="&where=ftp&ftp_ip=ftp.example.com&ftp_port=21&ftp_username=backupuser"
TASK+="&ftp_password=$(printf '%s' 'MockFtpPass_2026' | base64)"
TASK+="&ftp_path=/admin&ftp_secure=no&append_to_path=dayofweek"
TASK+="&database_data_aware=yes&email_data_aware=yes&trash_aware=yes&select0=webuser1"
echo "$TASK" >> /usr/local/directadmin/data/task.queue

วิธีนี้สะอาดกว่าการไปแก้ cron ให้รันนาทีถัดไปแล้วค่อยแก้กลับ เพราะไม่ต้องไปยุ่งกับบรรทัด cron จริงเลย รอสักครู่แล้วไปดู log ว่าเข้าจริงไหม

ตรวจว่าเข้าจริงไหม

หลังเพิ่มบรรทัดหรือยิง task.queue ผมเช็คสามจุดครับ — หนึ่ง เปิดหน้า CMD_ADMIN_BACKUP ในช่อง Current Backup Crons ต้องมี entry ใหม่โผล่ (ถ้าไม่โผล่แปลว่าไฟล์ owner/mode เพี้ยน dataskq อ่านไม่ได้) สอง ดู log ว่ามีการสั่ง backup จริง สาม ไปดูไฟล์ปลายทางบน FTP ในโฟลเดอร์ชื่อวัน:

tail -f /var/log/directadmin/admin_backups.log
journalctl -u directadmin --since "5 min ago" | grep action=backup

กับดักที่ผมเจอบ่อย

panel โชว์ cron แต่มันไม่รัน

เกือบทุกครั้งคือไฟล์ backup_crons.list เจ้าของไม่ใช่ diradmin:diradmin หรือ mode ไม่ใช่ 600 หลังแก้ไฟล์ chown/chmod กลับให้ครบ แล้วเช็ค systemctl is-active directadmin ว่า dataskq ยังเดินอยู่

รหัส FTP ในไฟล์คือ base64 ไม่ใช่การเข้ารหัส

base64 ถอดกลับได้ทันที ใครอ่านไฟล์นี้ได้เท่ากับได้รหัส FTP ไปเลย นี่คือเหตุผลที่ mode ต้อง 600 เป๊ะ และเวลาเขียนบทความ/แชร์ config ห้ามเอา token ตัวจริงออกมา ให้ mask หรือใช้ค่า mock เสมอ — ในบทความนี้ทั้ง host, user, รหัสเป็นของสมมติหมด

อีกสองอันที่เจอ — ถ้ารหัสใช้ไม่ได้ทั้งที่พิมพ์ถูก ให้เช็คว่าตอน base64 เผลอใช้ echo แทน printf '%s' หรือเปล่า เพราะ echo ต่อ newline ทำให้ token ผิดไปหนึ่งตัว และถ้า where=ftp แต่ลืมใส่บล็อก ftp_* ให้ครบ DA จะไม่ฟ้อง error ชัดๆ มันจะเงียบๆ ไปทำ local หรือไม่ทำเลย งงหาสาเหตุนาน

แก้หรือลบ entry

ลบทีละอันด้วย sed -i '/^2=/d' backup_crons.list (ลบ id=2) ส่วนการแก้ก็คือลบแล้ว append ใหม่ ทุกครั้งที่แตะไฟล์ cp สำรองไว้ก่อนเสมอ แล้ว chown/chmod กลับหลังแก้เสร็จ

โดยรวมพอเข้าใจว่าปุ่ม Submit ในหน้า panel ก็คือการ append หนึ่งบรรทัดลงไฟล์นี้ งานตั้ง backup เป็นสิบ user ที่เคยกินเวลาทั้งบ่ายก็เหลือแค่ลูป shell รอบเดียว ผมใช้โครงนี้ตั้ง cron ให้ทั้งเครื่องในรอบเดียว แล้วปล่อยให้ dataskq มันทำงานของมันไป ใครจะเอาไปปรับ tier เวลา/ปลายทางตามเครื่องตัวเองก็ได้ตามสะดวกครับ ตรงไหนผมอธิบายตกหล่นหรือพิมพ์ผิด เดี๋ยวกลับมาแก้เพิ่มให้ภายหลังครับ

อยากให้เราทำให้ไหม?

ให้เราช่วยติดตั้ง ตั้งค่า ย้ายระบบ หรือแก้ปัญหาเซิร์ฟเวอร์ เพื่อให้คุณมีเวลาไปทำงานที่สำคัญกว่า

คุยเรื่องงานกับเรา

เคยเจองานแบบนี้ไหม? ติดตรงไหน?

มาแลกวิธีกันต่อในบอร์ด LinuxThai

→ คุยต่อในบอร์ด

ความคิดเห็น 0

ยังไม่มีความคิดเห็น — เป็นคนแรกเลย