EST. 2026 — บันทึกงานเซิร์ฟเวอร์จริง SYSADMIN · SECURITY · AI · DIRECTADMIN เล่มที่ 01
THE SYSADMIN ALMANAC
หน้าหลัก / DirectAdmin / ย้าย DA ตอนที่ 3 — ลง DirectAdmin + CustomBuild บน Debian 12
DirectAdmin — ซีรีส์ ย้าย DA CentOS7 -> Debian12 [3]

ย้าย DA ตอนที่ 3 — ลง DirectAdmin + CustomBuild บน Debian 12

โดย Ton-Or ·20 พ.ค. 2026·อ่าน 11 นาที
ย้าย DA ตอนที่ 3 — ลง DirectAdmin + CustomBuild บน Debian 12

ตอนนี้เครื่อง staging พร้อมแล้ว ผมจะลง DirectAdmin ทั้งกองลงไปบน VM ที่ยังอยู่หลัง NAT ก่อน cutover เพื่อให้ทุกอย่างพร้อมก่อนสลับของจริง

ตอนที่ 1 กับ 2 ผมเตรียม VM กับ NAT ไว้แล้ว และถอด RAID เดิมออกมาสร้าง ZFS mirror บน Proxmox ใครยังไม่ได้อ่านสองตอนแรก แนะนำให้ย้อนไปดูก่อน เพราะตอนนี้ผมจะต่อยอดจากตรงนั้นเลย — เครื่อง staging ที่ยังนั่งอยู่หลัง NAT รอให้เราเข้าไปประกอบของ

ตอนที่ 3 นี้คือการลง DirectAdmin กับ stack ทั้งหมดลงบน staging VM ผมเข้าผ่าน SSH ที่ forward port ไว้ตั้งแต่ตอนที่ 1 (ผม map ไว้ที่ <port> ชี้เข้า VM) ทุกอย่างทำบนเครื่องที่ยังไม่มีใครเห็นจากภายนอก ตั้งให้เสร็จสมบูรณ์ก่อน แล้วค่อยไป cutover ในตอนที่ 4

ภาพรวมตอนนี้SSH ผ่าน NATport forward->setup.shลง DA->CustomBuildPHP/DB/WAF->copy custom dataจากเครื่องเก่า

ทำไมต้อง Debian 12 ไม่ใช่ Debian 13

เรื่องนี้ผมตอบตรงๆ ได้เลยว่าไม่มีทางเลือก ถ้าใครถือ DirectAdmin Legacy License อยู่ Debian 13 (trixie) ใช้ไม่ได้ครับ ผมลองลงดูเองทีนึง พอรัน setup.sh มันเด้งฟ้องออกมาตรงๆ ไม่อ้อมค้อม

legacy code-base does not support debian13 system,
downgrade your system or upgrade DirectAdmin license

ความหมายชัดเจน — โค้ดเบสฝั่ง legacy ยังไม่รองรับ Debian 13 ทางออกมีสองทาง คือ downgrade ระบบลงมา หรือไม่ก็อัปเกรด license ไปเป็น Modern subscription ใครจะจ่ายขึ้นไปใช้ subscription ก็ตามสะดวก แต่สำหรับงานนี้ผมเลือก Debian 12 (bookworm) เป็นคำตอบสุดท้าย เหตุผลตรงไปตรงมา — Debian 12 มี LTS support ยาวไปถึงปี 2028 พอที่จะใช้คุ้มรอบการ migrate รอบนี้ และเข้ากันได้กับ license ที่มีอยู่โดยไม่ต้องเสียเงินเพิ่ม

สรุปเรื่อง license

ถ้าถือ Legacy License ให้ยึด Debian 12 เป็นฐาน อย่าเพิ่งกระโดดไป Debian 13 จนกว่าฝั่ง DirectAdmin จะรองรับ legacy code-base อย่างเป็นทางการ ไม่งั้น setup.sh จะไม่ยอมรันให้ตั้งแต่ต้น

ลง DirectAdmin ด้วย setup.sh

ขั้นแรกคือดึง setup.sh มาแล้วให้สิทธิ์รัน จากนั้นรัน setup.sh พร้อมส่งค่าทั้งหมดผ่าน environment variable ผมชอบวิธีนี้เพราะมันทำให้การติดตั้งเป็น non-interactive ทั้งก้อน ไม่ต้องนั่งตอบคำถามทีละข้อ และจดบันทึกซ้ำได้

wget https://download.directadmin.com/setup.sh
chmod 755 setup.sh

DA_HOSTNAME=server.example.com \
DA_ETH_DEV=<eth-dev> \
[email protected] \
DA_ADMIN_USER=admin \
DA_ADMIN_PASSWORD='<strong-password>' \
DA_NS1=ns1.example.com \
DA_NS2=ns2.example.com \
DA_CHANNEL=stable \
DA_SKIP_CUSTOMBUILD=1 \
  bash setup.sh '<license-key>'

จุดที่ผมตั้งใจคือ DA_SKIP_CUSTOMBUILD=1 — ผมไม่ให้ setup.sh ไปสั่ง CustomBuild ตอนติดตั้งเลย เพราะผมอยากคุม stack เองทุกตัวในขั้นถัดไป ถ้าปล่อยให้มันลง default มาก่อน เดี๋ยวต้อง rebuild ทับซ้ำเสียเวลา

กับดักที่ผมโดนมาแล้วตอนรัน setup.sh

มีสามเรื่องที่อยากเตือนไว้ก่อน เพราะผมเสียเวลากับมันมาเอง

ตัวแปรชื่อ DA_ETH_DEV ไม่ใช่ DA_ETHERNET_DEV — เอกสารเก่าบางที่ยังเขียนเป็น ETHERNET_DEV ผมตามมันไปก็พลาด setup ไม่รับค่า กว่าจะจับได้ก็เสียเวลาไปครึ่งชั่วโมง ตรวจชื่อ interface จริงของเครื่องก่อน (บน Debian มักเป็นชื่อแนว enpXsY) แล้วใส่ให้ตรง

License key เป็น positional argument ไม่ใช่ environment variable — มันต้องไปอยู่ท้ายสุดหลัง bash setup.sh ไม่ใช่ใส่นำหน้าเป็น env เหมือนตัวอื่น ถ้าใส่ผิดตำแหน่ง DA จะ activate ไม่ผ่าน

ตอนเครื่องนี้ยังอยู่หลัง NAT ตัว DA จะตรวจเจอ private IP ของ VM (เช่น 192.168.1.x ในวงในของ Proxmox) แล้วเอาไปใส่ลง config ทันที ตรงนี้เป็นเรื่องปกติของการติดตั้งหลัง NAT แต่ต้องจำไว้ว่ามันจะต้องถูกแก้ตอน cutover ให้เป็น public IP จริง รายละเอียดทั้งหมดผมเก็บไว้พูดในตอนที่ 4

NAT IP ค้างใน config

เครื่องที่ติดตั้งหลัง NAT จะมี private IP ฝังอยู่ในไฟล์ตั้งค่าหลายจุด อย่าเพิ่งไปไล่แก้ตอนนี้ ปล่อยไว้ก่อน แล้วรวบไปจัดการพร้อมกันตอน cutover จะเป็นระเบียบกว่า

เรื่อง License ping-pong ที่ต้องเข้าใจให้ชัด

DirectAdmin legacy license มีพฤติกรรมที่ต้องรู้ไว้ก่อนจะลงเครื่องที่สอง — กติกาคือ "DA ตัวที่ start ล่าสุดเป็นฝ่ายชนะ" (last DA started wins) พอเครื่องใหม่ activate license ตัว DA panel ของเครื่องเก่าจะหลุดทันที

ฟังดูน่ากลัว แต่จริงๆ ผลกระทบจำกัดมาก เพราะมันกระทบแค่ตัว admin panel ของเครื่องเก่าเท่านั้น ส่วน service ที่ให้บริการลูกค้าจริง — mail, web, FTP — ทำงานต่อบนเครื่องเก่าได้ตามปกติ เพราะ service พวกนั้นแยกตัวออกจาก DA panel มันไม่ได้พึ่ง license เพื่อรันต่อ ลูกค้าจะไม่รู้สึกอะไรเลย เว็บยังขึ้น เมลยังเข้าออก สิ่งเดียวที่หายไปคือสิทธิ์เข้า admin panel ของเครื่องเก่าชั่วคราว

หลัง activate เครื่องใหม่

เครื่องเก่า — DA panel

  • หลุด login ทันที
  • เข้า admin panel ไม่ได้
ยังทำงานต่อ

เครื่องเก่า — services

  • web ยังเสิร์ฟปกติ
  • mail เข้าออกได้
  • FTP ใช้งานได้

รู้ข้อนี้ไว้จะวางแผน cutover ได้สบายใจขึ้น เพราะไม่ต้องกลัวว่าพอ activate เครื่องใหม่แล้วลูกค้าจะล่มทั้งกระดาน มันไม่ล่ม กระทบแค่มุมที่เราเข้าไปบริหารเท่านั้น

CustomBuild — ประกอบ PHP, MariaDB และ WAF

พอ DA ลงเสร็จ ก็มาถึงหัวใจของตอนนี้ คือการประกอบ stack ด้วย CustomBuild ผมตั้งทุกอย่างให้ครบในรอบเดียว แล้วค่อยสั่ง build ทีเดียวจบ

cd /usr/local/directadmin/custombuild

./build set php1_release 8.2
./build set php1_mode php-fpm
./build set php2_release 8.1
./build set php3_release 5.6
./build set php4_release 7.4
./build set php5_release 8.3
./build set php6_release 8.4
./build set php_opcache yes
./build set php_imagick yes
./build set php_igbinary yes
./build set php_redis yes

./build set webserver nginx_apache
./build set modsecurity modsecurity
./build set modsecurity_ruleset comodo

./build set rspamd yes
./build set fail2ban yes

./build set ftpd proftpd
./build set mysql 10.6
./build set mysql_inst mariadb

./build all d

ที่ผมตั้ง PHP ไว้หลายเวอร์ชัน (5.6 ไปจนถึง 8.4) เพราะบนเครื่อง shared host จริง ลูกค้าแต่ละรายใช้เวอร์ชันต่างกัน เว็บเก่าบางตัวยังต้อง 5.6 หรือ 7.4 อยู่ ส่วนงานใหม่ก็อยากได้ 8.x การมีให้เลือกหลาย slot คือสิ่งจำเป็นบนเครื่องแบบนี้ ส่วน php1 ผมตั้งเป็น 8.2 และเปิด php-fpm ไว้เป็นค่าเริ่มต้น

ฝั่งเว็บเซิร์ฟเวอร์ผมเลือก nginx_apache คือ nginx นั่งหน้าเป็น reverse proxy และ Apache อยู่หลัง ได้ทั้งความเร็วของ nginx กับความยืดหยุ่นเรื่อง .htaccess ของ Apache ในตัวเดียว เปิด ModSecurity พร้อม ruleset ของ Comodo เป็น WAF, เปิด rspamd กรองสแปม และ fail2ban กันการ brute-force ส่วน database ใช้ MariaDB 10.6 และ FTP ใช้ proftpd

คำสั่งสุดท้าย ./build all d คือสั่ง build ทั้งกองในโหมด background ใช้เวลาประมาณ 1 ถึง 2.5 ชั่วโมง ขึ้นอยู่กับสเปกเครื่อง เครื่องช้ากว่านี้ก็รอนานหน่อย ระหว่างนั้นไปทำอย่างอื่นได้

6 slotPHP 5.6-8.4
10.6MariaDB
nginx+apacheweb stack
1-2.5 ชม.เวลา build

Options ที่ CustomBuild รุ่นใหม่ไม่มีแล้ว — อย่าไปตั้งซ้ำ

เรื่องนี้พลาดง่ายมาก โดยเฉพาะคนที่ก๊อปสคริปต์เก่ามาใช้ บน CustomBuild รุ่น 1.700 ขึ้นไป มี option หลายตัวที่หายไปแล้วเพราะมันถูกจัดการให้อัตโนมัติหรือถูกควบรวมไปกับตัวอื่น ถ้าไปสั่ง ./build set กับ option ที่ไม่มีอยู่ มันจะไม่ทำงานอย่างที่คิด หรือบางทีก็เด้ง error เปล่าๆ

option ที่เลิกตั้งได้แล้ว มีตามนี้

  • default_php — ไม่ต้องตั้ง เพราะ php1 เป็น default โดยอัตโนมัติอยู่แล้ว
  • php[2-8]_mode — ไม่ต้องตั้งทีละ slot แค่ตั้ง php1_mode ตัวเดียว slot อื่นจะ inherit ตามไปเอง
  • ftp_with_tls — proftpd มี TLS มาให้ในตัวแล้ว ไม่ต้องเปิดแยก
  • http2 กับ http3_quic — nginx 1.30+ รองรับมาในตัว เปิดให้อัตโนมัติ
  • eximconf_release — ตัวนี้ระบบ auto-managed ให้แล้ว อย่าไปล็อกเวอร์ชันเอง
กฎเหล็ก: อย่า assume

ก่อนจะ ./build set อะไรก็ตาม ให้รัน ./build options ดูก่อนเสมอว่า option นั้นยังมีอยู่จริงไหมในเวอร์ชันที่ติดตั้ง อย่าเดาจากสคริปต์เก่าหรือ docs เก่าเด็ดขาด CustomBuild เปลี่ยนชุด option ไปตามเวอร์ชัน

PHP OPcache override — จุดที่ตั้งแล้วหายทุกรอบ

อันนี้เป็นบทเรียนที่ผมเสียเวลาตั้งใหม่อยู่หลายรอบกว่าจะรู้ทาง — CustomBuild มันจะ rewrite ไฟล์ 10-directadmin.ini ใหม่ทุกครั้งที่เราสั่ง ./build php_* อะไรก็ตามที่เราเขียนทับเข้าไปในไฟล์นั้นจะหายเกลี้ยง พอ rebuild ทีก็ต้องตั้ง OPcache ใหม่ทุกที น่าหงุดหงิดมาก

ทางแก้คือใช้ลำดับการโหลดไฟล์ของ PHP ให้เป็นประโยชน์ — PHP โหลดไฟล์ใน php.conf.d เรียงตามตัวอักษร ไฟล์ที่ขึ้นต้นด้วยเลขสูงกว่าจะโหลดทีหลัง และค่าที่โหลดทีหลังจะ override ค่าก่อนหน้า ดังนั้นผมแค่เอา custom config ไปไว้ในไฟล์ที่เรียงหลัง 10-* เช่นขึ้นต้นด้วย 99- มันก็จะทับค่าของ DirectAdmin เสมอ และที่สำคัญ CustomBuild ไม่แตะไฟล์ 99-*.ini ของเรา

PHP โหลดไฟล์ใน php.conf.d ตามลำดับตัวอักษร ค่าที่โหลดทีหลังชนะ10-directadmin.iniCustomBuild เขียนใหม่ทุก buildไฟล์ถัดไปตามตัวอักษร99-custom-opcache.iniCustomBuild ไม่แตะค่าที่เขียนทับเองหายชนะ default และรอดทุก rebuildเลข 99 นำหน้าทำให้เรียงหลังสุด แล้วทับค่าของ DirectAdmin ได้เสมอ
ไฟล์ 10-directadmin.ini ถูก CustomBuild สร้างใหม่ทุกครั้งที่ build php ค่าที่แก้เองในไฟล์นั้นหายไป แต่ไฟล์ที่ขึ้นต้นด้วย 99 โหลดทีหลังและ CustomBuild ไม่แตะ ค่าที่ตั้งไว้เลยรอด
cat > /usr/local/php82/lib/php.conf.d/99-custom-opcache.ini <<EOF
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.validate_timestamps=1
opcache.revalidate_freq=60
opcache.interned_strings_buffer=16
EOF

for ver in 56 74 81 82 83 84; do
    [ -d /usr/local/php${ver}/lib/php.conf.d ] && \
        cp /usr/local/php82/lib/php.conf.d/99-custom-opcache.ini \
           /usr/local/php${ver}/lib/php.conf.d/
done

สคริปต์ตัวล่างคือก๊อปไฟล์ 99-custom-opcache.ini ไปวางในทุก PHP slot ที่ติดตั้งไว้ จะได้ตั้งค่าเดียวกันครบทุกเวอร์ชันในทีเดียว เงื่อนไข [ -d ... ] คือเช็คก่อนว่า slot นั้นมีอยู่จริงค่อยก๊อป

ผลลัพธ์คือ OPcache setting ของเราจะ survive ทุก rebuild ไม่ต้องมานั่งตั้งใหม่อีก หลักการเดียวกันนี้เอาไปใช้ override ค่า php.ini อื่นๆ ที่ CustomBuild ชอบเขียนทับได้หมด

หลักการที่เอาไปใช้ต่อได้

อะไรก็ตามที่ CustomBuild เขียนทับทุก rebuild ให้ย้าย custom override ไปไว้ในไฟล์ที่ขึ้นต้นด้วยเลขสูง (เช่น 99-) แล้วมันจะรอดทุกรอบ เพราะ CustomBuild แตะแค่ไฟล์ที่ตัวมันสร้าง

Custom data ที่ต้อง copy มาจากเครื่องเก่า

เครื่อง staging ที่เพิ่งลง DA จะเป็นเครื่องเปล่า ยังไม่มีของที่สะสมมาจากเครื่องเก่า มีหลายอย่างที่ DA ไม่ได้ migrate ให้อัตโนมัติ ต้องลากมาเอง ก่อนจะ cutover ผมจัดการก๊อปพวกนี้มาก่อน

# DNSSEC keys
scp -r root@<old-server>:/var/named/*.{ksk,zsk}* root@<new-server>:/var/named/
scp -r root@<old-server>:/var/named/*.signed root@<new-server>:/var/named/

# DA custom hooks
scp -r root@<old-server>:/usr/local/directadmin/scripts/custom/* \
       root@<new-server>:/usr/local/directadmin/scripts/custom/

# DA custom templates
scp -r root@<old-server>:/usr/local/directadmin/data/templates/custom/* \
       root@<new-server>:/usr/local/directadmin/data/templates/custom/

# System configs
scp root@<old-server>:/etc/aliases root@<new-server>:/etc/aliases
scp root@<old-server>:/etc/hosts.deny root@<new-server>:/etc/hosts.deny

ไล่ทีละกลุ่ม — DNSSEC keys ใน /var/named/ ต้องเอามาทั้งคู่ทั้ง key (ksk/zsk) และ zone ที่ signed แล้ว ไม่งั้น DNSSEC ของโดเมนที่เซ็นไว้จะใช้ไม่ได้หลังย้าย, DA custom hooks คือสคริปต์ที่เราเขียนแทรกพฤติกรรมของ DA เอง, custom templates คือ template ที่เราดัดแปลงไว้ (เช่นหน้าตา config ที่ DA generate), ส่วน system config อย่าง /etc/aliases และ /etc/hosts.deny ก็เป็นของที่สะสมมานานต้องลากตามมาด้วย

named.conf options ต้องแก้มือ

ส่วน allow-transfer และ also-notify ใน named.conf ที่ชี้ไป slave NS ต้องเข้าไปแก้เอง DA ไม่ overwrite section นี้ให้ ถ้าลืม zone transfer ไป secondary NS จะพังเงียบๆ

สรุปตอนที่ 3

จบตอนนี้เครื่อง staging มี DA + stack ครบทั้งกอง PHP หลายเวอร์ชัน, MariaDB, nginx+apache, ModSecurity, rspamd, fail2ban พร้อม custom data ที่ลากมาจากเครื่องเก่าแล้ว ทุกอย่างยังอยู่หลัง NAT รอวันสลับของจริง

ของยากของจริงอยู่ตอนหน้า — ตอนที่ 4 จะเป็นเรื่อง cutover ของจริง กับ 17 ปัญหาที่ผมเจอหลังย้าย ทั้งเรื่อง NAT IP ที่ค้างใน config, license ที่ต้อง activate ฝั่งใหม่, และอีกหลายจุดที่ไม่มีในคู่มือ เรื่องเด็ดอยู่ตอนนั้น

ตรงไหนผมนึกออกเพิ่ม หรือเจอว่ามีจุดที่อธิบายไม่ครบ จะกลับมาแก้เพิ่มทีหลัง ใครจะปรับ option ให้ต่างจากผมก็ตามสะดวก หลักการมันเหมือนกันหมด ขอแค่ตั้งให้ครบก่อน cutover แล้วที่เหลือจะง่ายขึ้นเยอะ

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

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

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

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

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

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

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

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