SSD Nodes Learn RAM 8GB — $66/ปี
คู่มือ Matt Connorโดย Matt Connor · อัปเดตเมื่อ 2026-08-01

Docker Compose ให้เริ่มเองหลังรีบูตด้วย restart policy

ตั้งค่า Docker Compose ให้บริการกลับมาหลังรีบูตด้วย restart policy พร้อมอธิบายเหตุผลที่ on-failure ไม่ทำงานข้ามการรีบูต และกรณีที่ควรใช้ systemd

คำตอบสั้น ๆ

บริการ Docker Compose จะเริ่มทำงานเมื่อระบบบูต หากตรงตามเงื่อนไข 2 ข้อพร้อมกัน Docker daemon ต้องเปิดใช้งานเป็น system service และแต่ละบริการในไฟล์ต้องมี restart policy เป็น unless-stopped หรือ always เพิ่ม restart: unless-stopped ให้ทุกบริการ แล้วเรียกใช้ docker compose up -d 1 ครั้ง คอนเทนเนอร์จะกลับมาทำงานเองหลังรีบูต สำหรับกรณีทั่วไปไม่ต้องดำเนินการอื่น

คุณต้องใช้ systemd unit ก็ต่อเมื่อลำดับการเริ่มทำงานมีความสำคัญ เช่น stack ที่ต้องใช้ดิสก์ที่ mount แล้ว, VPN interface หรือ network share ที่ยังไม่พร้อมเมื่อ Docker daemon เริ่มทำงาน กรณีนี้เกิดขึ้นได้จริง และคู่มือส่วนที่สองจะอธิบายเรื่องนี้ หากคุณยังไม่คุ้นเคยกับการกำหนด service และ volume ให้เริ่มจาก พื้นฐาน Docker Compose บน VPS แล้วกลับมาอ่านต่อ

ตั้งค่านโยบายการเริ่มต้นใหม่ใน compose.yaml

นโยบายนี้กำหนดแยกเป็น 1 บรรทัดต่อบริการ ไม่มีสวิตช์ส่วนกลาง ดังนั้นบริการที่คุณลืมตั้งค่าจะหยุดทำงานหลังการรีบูต ขณะที่บริการอื่นใน stack เริ่มทำงานตามปกติ

services:
  app:
    image: nginx:1.27
    restart: unless-stopped
    ports:
      - "8080:80"
  db:
    image: postgres:16
    restart: unless-stopped
    environment:
      POSTGRES_PASSWORD: change-me
    volumes:
      - dbdata:/var/lib/postgresql/data

volumes:
  dbdata:

ใช้การตั้งค่านี้ แล้วอ่านค่านโยบายจาก container ที่กำลังทำงาน:

docker compose up -d
docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app)

คำสั่งนี้จะแสดง unless-stopped หากแสดง no แสดงว่าแก้ไขไฟล์แล้ว แต่ยังไม่ได้สร้าง container ใหม่

นี่เป็นสาเหตุของความล้มเหลวที่พบบ่อยที่สุด นโยบายการเริ่มต้นใหม่จะถูกจัดเก็บไว้ใน container ไม่ใช่ในไฟล์ YAML การแก้ไข compose.yaml จะไม่เปลี่ยนแปลง container ที่มีอยู่แล้ว docker compose restart ก็ไม่ช่วยเช่นกัน เพราะคำสั่งนี้หยุดและเริ่ม object ของ container เดิม โดยไม่เปลี่ยนการกำหนดค่า มีเพียง docker compose up -d ที่เปรียบเทียบไฟล์กับ container ที่กำลังทำงาน ตรวจพบว่านโยบายเปลี่ยนแปลง และสร้าง container ใหม่

สำหรับ container ที่คุณยังไม่ต้องการสร้างใหม่ ให้เปลี่ยนนโยบายโดยตรง:

docker update --restart unless-stopped my-container

ให้แก้ไขไฟล์ YAML ด้วยเช่นกัน docker update จะเปลี่ยนค่าใน container ที่กำลังทำงาน และการใช้ docker compose up -d ครั้งถัดไปจะอ่านค่าจากไฟล์แล้วเปลี่ยนกลับเป็นค่าเดิม

ค่าการรีสตาร์ตแต่ละค่าทำงานอย่างไร

Docker กำหนดค่าไว้ 4 ค่า โดยความแตกต่างระหว่างค่าเหล่านี้จะแสดงเมื่อเครื่องรีบูตหรือเมื่อ daemon ถูกรีสตาร์ต

  • no เป็นค่าเริ่มต้น ระบบจะไม่รีสตาร์ต container โดยอัตโนมัติไม่ว่าในกรณีใด
  • always จะรีสตาร์ต container ทุกครั้งที่ container หยุดทำงาน หากคุณสั่งหยุดด้วยตนเอง container จะเริ่มทำงานอีกครั้งเมื่อ Docker daemon เริ่มทำงานครั้งถัดไป ลักษณะนี้มักทำให้เกิดความประหลาดใจ เช่น container ที่คุณตั้งใจหยุดไว้เมื่อสัปดาห์ก่อนกลับมาทำงานอีกครั้งหลังเครื่องรีบูต
  • unless-stopped ทำงานเหมือน always แต่ container ที่หยุดด้วยตนเองจะยังคงหยุดอยู่หลังจาก daemon รีสตาร์ต ค่านี้เหมาะสำหรับ service ที่คุณหยุดเป็นครั้งคราวเพื่อบำรุงรักษา
  • on-failure จะรีสตาร์ต container เฉพาะเมื่อ container จบการทำงานด้วย exit code ที่ไม่ใช่ศูนย์ คุณสามารถจำกัดจำนวนครั้งที่พยายามได้ตามตัวอย่างใน restart: on-failure:3

สำหรับ stack ที่ควรทำงานอยู่เสมอเมื่อ server ทำงานอยู่ unless-stopped เป็นค่าเริ่มต้นที่เหมาะสม เลือก always เฉพาะเมื่อคุณต้องการให้ container กลับมาทำงานและไม่ปล่อยให้หยุดอยู่นาน

เหตุใดการรีสตาร์ท: on-failure จึงไม่ทำงานต่อหลังการรีบูต

หลายคนเลือก on-failure เพราะดูเหมือนเป็นตัวเลือกที่รัดกุม แต่กลับพบว่าคอนเทนเนอร์ทุกตัวหยุดทำงานหลังการรีบูตครั้งแรก เหตุผลอยู่ที่คำจำกัดความของนโยบายนี้ on-failure ตอบสนองต่อเหตุการณ์เดียวเท่านั้น คือกระบวนการของคอนเทนเนอร์สิ้นสุดลงพร้อมรหัสข้อผิดพลาด

การรีบูตไม่ใช่ข้อผิดพลาด เมื่อโฮสต์ปิดระบบ systemd จะหยุด docker.service และ daemon จะหยุดคอนเทนเนอร์แต่ละตัวโดยเจตนา คอนเทนเนอร์ไม่ได้ล้มเหลว ดังนั้นนโยบายจึงไม่มีเหตุการณ์ให้ตอบสนอง เมื่อระบบเริ่มทำงานอีกครั้ง daemon จะตรวจสอบคอนเทนเนอร์ที่ต้องเริ่มทำงานต่อ และคอนเทนเนอร์ on-failure ที่ถูกหยุดอย่างเรียบร้อยจะไม่อยู่ในกลุ่มนั้น คอนเทนเนอร์จะยังคงอยู่ในสถานะ exited

คุณตรวจสอบได้โดยตรง กำหนด restart: on-failure ให้กับ service เรียกใช้ docker compose up -d แล้วรีบูต จากนั้นเรียกใช้:

docker compose ps -a

service จะแสดงด้วยสถานะ Exited และสถานะโดยละเอียดลักษณะเดียวกับ Exited (0) 2 minutes ago ไม่มีสิ่งใดเสียหาย และไม่มีการบันทึกเป็นข้อผิดพลาด จึงทำให้วิเคราะห์ปัญหาได้ยาก นโยบายทำงานตรงตามที่ระบุไว้

on-failure ยังคงมีประโยชน์ เหมาะกับคอนเทนเนอร์ที่ทำงานเป็น job และอาจขัดข้อง โดยคุณต้องการให้ลองทำงานซ้ำในจำนวนครั้งที่จำกัด และไม่ต้องการให้เกิดลูปการรีสตาร์ท นโยบายนี้ไม่เหมาะสำหรับทำให้ service ที่ทำงานต่อเนื่องยังทำงานอยู่หลังการรีบูต

นโยบายการเริ่มต้นใหม่จะทำงานได้ก็ต่อเมื่อบริการ Docker เริ่มทำงานขณะบูตระบบ

นโยบายการเริ่มต้นใหม่ถูกบังคับใช้โดย Docker daemon หาก daemon ไม่เริ่มทำงาน ก็จะไม่มีสิ่งใดบังคับใช้นโยบาย ตรวจสอบดังนี้:

systemctl is-enabled docker
systemctl is-enabled containerd

ทั้งสองคำสั่งควรแสดง enabled แพ็กเกจจากที่เก็บอย่างเป็นทางการของ Docker จะเปิดใช้งานบริการเหล่านี้ขณะติดตั้ง ดังนั้นบนเซิร์ฟเวอร์ใหม่ การตรวจสอบนี้มักผ่าน หากคำสั่งใดแสดง disabled ให้แก้ไขดังนี้:

sudo systemctl enable --now docker containerd

กรณีนี้มีจุดที่ควรทำความเข้าใจ Ubuntu ยังมี docker.socket ซึ่งจะเริ่ม daemon ตามคำขอเมื่อมีการเรียกใช้ Docker API เป็นครั้งแรก ผู้ใช้เห็นว่า docker.socket เปิดใช้งานอยู่ จึงเข้าใจว่า daemon ได้รับการตั้งค่าให้เริ่มทำงานแล้ว และปิดใช้งาน docker.service เพื่อประหยัดหน่วยความจำ เมื่อบูตระบบ ไม่มีสิ่งใดเรียกใช้ API ดังนั้นจึงไม่มีการเข้าถึง socket, daemon ไม่เริ่มทำงาน และไม่มี container ใดเริ่มทำงานจนกว่าคุณจะป้อนคำสั่ง docker ครั้งแรก การเปิดใช้งาน socket ไม่ใช่สิ่งทดแทนการเปิดใช้งาน docker.service

เมื่อ systemd unit เป็นคำตอบที่เหมาะสมกว่า

นโยบายการเริ่มใหม่ไม่มีแนวคิดเรื่องลำดับการทำงานเมื่อเทียบกับส่วนอื่นของระบบ daemon จะเริ่มทำงานและเริ่ม container ของคุณทันทีที่ทำได้ หาก stack ของคุณใช้ bind-mount ไดเรกทอรีจาก volume แยกต่างหาก, NFS (ระบบไฟล์เครือข่าย) share หรือดิสก์ที่เข้ารหัส container อาจเริ่มทำงานก่อนที่ path ดังกล่าวจะมีอยู่ Docker จะสร้างไดเรกทอรีว่างที่จุด mount และเริ่ม container โดยใช้ไดเรกทอรีนั้นโดยไม่มีปัญหา จากนั้นฐานข้อมูลของคุณจะเริ่มทำงานโดยไม่มีข้อมูล

ให้เขียน systemd unit เมื่อมีเงื่อนไขใดเงื่อนไขหนึ่งต่อไปนี้ stack ต้องใช้ mount, อินเทอร์เฟซ VPN หรือ unit อื่นที่ต้องพร้อมใช้งานก่อน คุณต้องการให้ systemctl stop myapp และ systemctl start myapp ทำงานเช่นเดียวกับ service อื่นทั้งหมดในเครื่อง หรือคุณต้องการให้หยุด stack อย่างเรียบร้อยระหว่างการปิดระบบ แทนที่จะถูกยุติพร้อมกับ daemon หาก systemd unit เป็นเรื่องใหม่สำหรับคุณ การเขียน systemd service และ timer อธิบายรูปแบบไฟล์โดยละเอียดมากขึ้น

การเขียนยูนิต systemd

วาง stack ไว้ใน path แบบตายตัวนอก home directory /srv/myapp เป็นตัวเลือกที่เหมาะสม เพราะยูนิตที่ทำงานก่อนที่ผู้ใช้จะ login ไม่ควรอ่าน /home

สร้าง /etc/systemd/system/myapp.service:

[Unit]
Description=myapp docker compose stack
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target
RequiresMountsFor=/srv/myapp/data

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/srv/myapp
ExecStart=/usr/bin/docker compose up -d --remove-orphans
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0

[Install]
WantedBy=multi-user.target

Enable และ start ยูนิต:

sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service
systemctl status myapp.service

ยูนิตที่ทำงานปกติจะแสดง Active: active (exited) ซึ่งอาจดูผิดปกติเมื่อเห็นครั้งแรก แต่เป็นสถานะที่ถูกต้อง: Type=oneshot ที่มี RemainAfterExit=yes หมายความว่ายูนิตได้เรียกใช้คำสั่งแล้ว คำสั่งทำงานเสร็จแล้ว และ systemd ยังคงทำเครื่องหมายให้ยูนิตอยู่ในสถานะ active เพื่อให้ ExecStop ทำงานเมื่อระบบ shutdown

แต่ละบรรทัดมีเหตุผลของตนเอง Requires=docker.service ทำให้ยูนิตหยุดทำงานทันที แทนที่จะให้ docker compose ทำงานกับ socket ที่หยุดทำงานแล้ว After= กำหนดลำดับการทำงาน เพราะ Requires= เพียงอย่างเดียวไม่กำหนดลำดับ RequiresMountsFor= ทำให้ systemd โหลด mount unit สำหรับ path ดังกล่าวและรอให้ทำงานเสร็จ ซึ่งเป็นเหตุผลหลักที่ควรใช้ยูนิตแทน restart policy TimeoutStartSec=0 ป้องกันไม่ให้ systemd ยุติ start job ขณะที่ยัง pull image ขนาดใหญ่อยู่

ข้อควรทราบเกี่ยวกับการใช้กลไกทั้งสองร่วมกัน เอกสารของ Docker แนะนำไม่ให้ใช้ restart policy ร่วมกับ host process manager คำเตือนนี้หมายถึง process manager ที่กำกับดูแล container process โดยตรงและ restart process ดังกล่าว ขณะที่ daemon กำลังทำเช่นเดียวกัน ยูนิต Type=oneshot ไม่ได้กำกับดูแล process ใด ดังนั้นการคง restart: unless-stopped ไว้ใน compose file ควบคู่กับยูนิตนี้จึงทำได้ และเป็นสิ่งที่ควรทำ systemd จัดการลำดับการทำงานขณะ boot ส่วน daemon จัดการกรณีที่ container crash ในช่วงเช้ามืด

ตรวจสอบด้วยการรีบูตจริง

ไม่มีสิ่งใดทดแทนการทดสอบจริงได้ systemctl restart docker ไม่ได้ทดสอบลำดับการ mount และ docker compose down ตามด้วย docker compose up -d ก็ไม่ได้ทดสอบสิ่งใดเกี่ยวกับการบูตเลย

sudo reboot

รอ เชื่อมต่อใหม่ แล้วตรวจสอบตามลำดับนี้:

uptime
systemctl is-active docker
docker compose ps

uptime ยืนยันว่าคุณกำลังตรวจสอบเครื่องที่รีบูตจริง docker compose ps ซึ่งเรียกใช้จากไดเรกทอรี stack ควรแสดงบริการทุกตัวเป็น running โดยมี uptime ใกล้เคียงกับเครื่อง บริการที่แสดงเป็น Exited คือบริการที่ควรตรวจสอบ

หากมีสิ่งใดไม่เริ่มทำงาน บันทึก daemon จะครอบคลุมช่วงเวลาการบูต:

journalctl -u docker.service -b --no-pager | tail -50

สำหรับ stack ที่จัดการด้วย unit journalctl -u myapp.service -b --no-pager จะแสดงเอาต์พุต docker compose ที่แน่นอนจากการบูต รวมถึงกรณีดึง image ไม่สำเร็จหรือไม่มีไฟล์ .env

สิ่งที่ทำให้การเริ่มต้นอัตโนมัติทำงานผิดพลาดโดยไม่แสดงอาการ

คอนเทนเนอร์ที่สร้างด้วย docker compose run จะไม่ได้รับนโยบายการเริ่มต้นใหม่จากไฟล์ Compose จะถือว่าคอนเทนเนอร์เหล่านี้เป็นคอนเทนเนอร์แบบใช้ครั้งเดียว หากบริการดูเหมือนไม่ปฏิบัติตามนโยบาย ให้ตรวจสอบว่าเริ่มต้นด้วย run แทนที่จะเป็น up หรือไม่

พาธแบบ relative ใน volume หรือในรายการ env_file จะอ้างอิงกับไดเรกทอรีของไฟล์ compose การทำงานนี้ใช้ได้จาก shell และใช้ได้จาก unit ที่กำหนด WorkingDirectory แต่จะล้มเหลวจาก unit ที่ไม่ได้กำหนดค่า เนื่องจากไดเรกทอรีทำงานจะเป็น /

Rootless Docker เป็นกรณีแยกต่างหาก daemon จะทำงานเป็น user service และ user service จะหยุดทำงานเมื่อ session สุดท้ายของผู้ใช้นั้นสิ้นสุดลง ให้เปิดใช้งานสำหรับผู้ใช้และอนุญาตให้ทำงานต่อไปโดยไม่มีผู้ใช้เข้าสู่ระบบ:

systemctl --user enable docker
sudo loginctl enable-linger $USER

หากไม่มี enable-linger rootless daemon จะปิดตัวลงเมื่อคุณออกจากระบบ และคอนเทนเนอร์จะหยุดทำงานตามไปด้วย ซึ่งดูเหมือนว่านโยบายการเริ่มต้นใหม่ทำงานผิดพลาด

มีอีกประเด็นหนึ่ง การอัปเดตความปลอดภัยอัตโนมัติอาจ reboot server ในเวลาที่กำหนดไว้ การทำเช่นนี้จะเป็นประโยชน์ก็ต่อเมื่อ stack ของคุณเริ่มทำงานกลับมาได้เอง การตั้งค่านี้บนเครื่องใหม่ควรทำพร้อมกับงานช่วงชั่วโมงแรกที่เหลือใน สิบนาทีแรกบน VPS ใหม่

FAQ

restart: always และ restart: unless-stopped แตกต่างกันอย่างไร

ทั้งสองแบบจะเริ่มคอนเทนเนอร์ใหม่เมื่อคอนเทนเนอร์หยุดทำงานเอง ความแตกต่างจะเกิดขึ้นเมื่อคุณหยุดคอนเทนเนอร์ด้วยตนเอง เมื่อใช้ always คอนเทนเนอร์จะเริ่มทำงานอีกครั้งเมื่อ Docker daemon เริ่มทำงานครั้งถัดไป ดังนั้นการรีบูตจะยกเลิกการหยุดด้วยตนเองของคุณ เมื่อใช้ unless-stopped daemon จะจดจำว่าคอนเทนเนอร์ถูกหยุดโดยเจตนา และจะไม่เริ่มคอนเทนเนอร์นั้น ใช้ unless-stopped เว้นแต่คุณต้องการให้คอนเทนเนอร์หยุดทำงานต่อไป

ฉันเพิ่ม restart: unless-stopped แล้ว แต่คอนเทนเนอร์ยังไม่เริ่มทำงานหลังรีบูต เพราะเหตุใด

นโยบายนี้อยู่ที่คอนเทนเนอร์ ไม่ได้อยู่ในไฟล์ และการแก้ไข YAML จะไม่อัปเดตคอนเทนเนอร์ที่มีอยู่แล้ว เรียกใช้ docker compose up -d เพื่อให้ Compose สร้างคอนเทนเนอร์ใหม่ จากนั้นตรวจสอบด้วย docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app) หากคำสั่งแสดง no แสดงว่าคอนเทนเนอร์ถูกสร้างก่อนที่คุณจะแก้ไขไฟล์ สาเหตุที่พบบ่อยอีกประการหนึ่งคือ docker.service ยังไม่ได้เปิดใช้งาน ซึ่งคุณตรวจสอบได้ด้วย systemctl is-enabled docker

หากใช้ restart policies อยู่แล้ว ยังจำเป็นต้องมี systemd unit หรือไม่

โดยทั่วไปไม่จำเป็น restart policy เพียงพอสำหรับ stack ที่ต้องการเพียงเครือข่าย ซึ่งเป็นกรณีของ stack ส่วนใหญ่ ให้เพิ่ม unit เมื่อคอนเทนเนอร์ต้องพึ่งพาสิ่งที่ยังไม่พร้อมเมื่อ Docker daemon เริ่มทำงาน เช่น ดิสก์ภายนอก encrypted volume, NFS share หรือ VPN interface unit จะกำหนดลำดับการเริ่มทำงานผ่าน After= และ RequiresMountsFor= ซึ่ง restart policy ไม่สามารถระบุได้

จะหยุด stack อย่างถาวรโดยไม่ให้กลับมาทำงานเมื่อรีบูตครั้งถัดไปได้อย่างไร

เมื่อใช้ unless-stopped การใช้ docker compose stop ก็เพียงพอ เพราะคอนเทนเนอร์ที่ถูกหยุดด้วยตนเองจะไม่ถูกเริ่มต่อเมื่อ daemon เริ่มทำงานอีกครั้ง เมื่อใช้ always การหยุดเพียงอย่างเดียวไม่เพียงพอ และคอนเทนเนอร์จะกลับมาทำงานหลังรีบูต ให้เรียกใช้ docker compose down ซึ่งจะลบคอนเทนเนอร์ หรือเปลี่ยนนโยบายก่อนด้วย docker update --restart no my-container หาก systemd unit เป็นผู้จัดการ stack ให้เรียกใช้ sudo systemctl disable myapp.service ด้วย มิฉะนั้น unit จะเริ่ม stack อีกครั้ง