SSD Nodes Learn Hosting plans →
מדריכים Matt Connorמאת Matt Connor · עודכן 2026-08-31

Docker Compose: הגדרת הפעלה אוטומטית של שירותים ב-boot

למדו כיצד להגדיר restart policy ב-docker-compose.yaml כדי להבטיח שהמכולות יעלו לאחר אתחול. הסבר על ההבדל בין always ל-unless-stopped ומתי נדרש שירות systemd מותאם.

התשובה הקצרה

שירותי Docker Compose עולים בעת האתחול כאשר מתקיימים שני תנאים בו-זמנית. ה-daemon של Docker חייב להיות מופעל כשירות מערכת, ולכל שירות בקובץ חייבת להיות מדיניות restart של unless-stopped או always. הוסיפו restart: unless-stopped לכל שירות, הריצו docker compose up -d פעם אחת, והמכולות יחזרו לפעול מעצמן לאחר reboot. שום דבר נוסף אינו נדרש עבור המקרה הנפוץ.

יש צורך ב-systemd unit רק כאשר סדר העלייה קריטי: stack התלוי בכונן ממופה (mounted disk), בממשק VPN, או בשיתוף רשת שאינו זמין ברגע שבו ה-daemon של Docker עולה. זהו מקרה אמיתי, והחלק השני של מדריך זה עוסק בו. אם אתם עדיין לומדים את יסודות הגדרות השירותים וה-volumes, התחילו ב-יסודות Docker Compose ב-VPS וחזרו לכאן לאחר מכן.

הגדרת מדיניות הפעלה מחדש ב-compose.yaml

המדיניות מוגדרת בשורה אחת לכל שירות. לא קיים מתג גלובלי, לכן שירות שנשכח יישאר כבוי לאחר אתחול השרת, בעוד שאר ה-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:

החילו את השינוי ולאחר מכן בדקו את המדיניות של המכולה הפעילה:

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

הפקודה תדפיס unless-stopped. אם מודפס no, הקובץ נערך אך המכולה לא נוצרה מחדש.

זוהי נקודת הכשל הנפוצה ביותר. מדיניות ההפעלה מחדש נשמרת במכולה, לא בקובץ ה-YAML. עריכת compose.yaml אינה משנה דבר במכולה קיימת. גם docker compose restart לא תועיל, כיוון שהיא עוצרת ומפעילה את אותו אובייקט מכולה מבלי לגעת בתצורה שלו. רק docker compose up -d משווה בין הקובץ לבין המכולות הרצות, מזהה שהמדיניות השתנתה, ויוצרת אותן מחדש.

עבור מכולה שאינכם רוצים ליצור מחדש כרגע, שנו את המדיניות בשידור חי:

docker update --restart unless-stopped my-container

עדיין יש לערוך גם את קובץ ה-YAML. docker update משנה את המכולה הפעילה, והפקודה docker compose up -d הבאה תקרא את הקובץ ותחזיר את הערך הישן.

מה עושה כל ערך restart בפועל

Docker מגדיר ארבעה ערכים, וההבדל ביניהם בא לידי ביטוי רק כאשר המכונה מבצעת reboot או כאשר ה-daemon מופעל מחדש.

  • no הוא ערך ברירת המחדל. המכולה לעולם לא תופעל מחדש באופן אוטומטי, בשום נסיבות.
  • always מפעיל את המכולה מחדש בכל פעם שהיא נעצרת. אם עצרתם אותה ידנית, היא תחזור לפעול בכל מקרה בפעם הבאה שה-daemon של Docker יעלה. זה מפתיע לעיתים קרובות: מכולה שעצרתם במכוון בשבוע שעבר חוזרת לפעול לאחר reboot.
  • unless-stopped מתנהג כמו always, מלבד העובדה שמכולה שנעצרה ידנית תישאר כבויה גם לאחר הפעלה מחדש של ה-daemon. זהו הערך הרצוי עבור שירות שאתם משביתים מדי פעם לצורכי תחזוקה.
  • on-failure מפעיל את המכולה מחדש רק כאשר היא מסתיימת עם exit code שאינו אפס. ניתן להגביל את מספר הניסיונות, כפי שמופיע ב-restart: on-failure:3.

עבור stack שאמור פשוט להיות פעיל בכל פעם שהשרת פעיל, unless-stopped הוא ברירת המחדל הנכונה. בחרו ב-always רק כאשר אתם רוצים מכולה שתמשיך לפעול גם אם ניסו להשאיר אותה כבויה.

מדוע restart: on-failure לא שורד אתחול (reboot)

משתמשים רבים בוחרים ב-on-failure כי הוא נשמע כאפשרות בטוחה, אך מגלים שכל המכולות נעצרות לאחר האתחול הראשון. הסיבה לכך טמונה בהגדרה. on-failure מגיב לדבר אחד בלבד: יציאה של תהליך המכולה עם קוד שגיאה.

אתחול אינו נחשב לשגיאה. כאשר המארח (host) נכבה, systemd עוצר את docker.service, וה-daemon עוצר כל מכולה באופן יזום. המכולה לא קרסה, ולכן למדיניות אין על מה להגיב. בעת העלייה מחדש, ה-daemon בודק אילו מכולות עליו להפעיל, ומכולה מסוג on-failure שנעצרה בצורה תקינה אינה נכללת בהן. היא נשארת במצב exited.

ניתן לראות זאת ישירות. הגדירו restart: on-failure בשירות, הריצו docker compose up -d, בצעו reboot, ולאחר מכן הריצו:

docker compose ps -a

השירות מופיע במצב Exited עם סטטוס כמו Exited (0) 2 minutes ago. שום דבר אינו תקול ושום דבר לא נרשם כשגיאה, וזה מה שהופך את הבעיה לקשה לאבחון. המדיניות פעלה בדיוק לפי הגדרתה.

on-failure עדיין שימושי. הוא מתאים למכולה שמריצה משימה ועלולה לקרוס, כאשר נדרש מספר מוגבל של ניסיונות חוזרים ללא לולאת הפעלה אינסופית. זהו הכלי הלא נכון לשמירה על שירות שרץ לאורך זמן לאחר אתחולים.

מדיניות Restart פועלת רק אם שירות Docker עולה בזמן ה-boot

מדיניות ה-Restart נאכפת על ידי ה-daemon של Docker. אם ה-daemon אינו עולה, שום דבר לא ייאכף. בדקו זאת:

systemctl is-enabled docker
systemctl is-enabled containerd

שתי הפקודות צריכות להחזיר enabled. החבילות מהמאגר הרשמי של Docker מפעילות אותן בזמן ההתקנה, לכן בשרת חדש זה בדרך כלל תקין. אם אחת מהן מחזירה disabled, תקנו זאת:

sudo systemctl enable --now docker containerd

יש כאן מלכודת שחשוב להבין. Ubuntu מספקת גם את docker.socket, שמפעיל את ה-daemon לפי דרישה ברגע שגורם כלשהו פונה ל-API של Docker. משתמשים רואים ש-docker.socket מופעל, מניחים שה-daemon מכוסה, ומנטרלים את docker.service כדי לחסוך בזיכרון. בזמן ה-boot, אף אחד לא פונה ל-API, לכן ה-socket לא נגיש, ה-daemon לא עולה, ואף container לא יופעל עד שתקלידו את פקודת ה-docker הראשונה שלכם. הפעלת socket אינה תחליף להפעלה של docker.service.

מתי יחידת systemd היא הפתרון העדיף

למדיניות הפעלה מחדש (restart policies) אין מנגנון לניהול סדר פעולות מול שאר המערכת. ה-daemon עולה ומפעיל את המכולות שלך ברגע שהוא יכול. אם ה-stack שלך מבצע bind-mount לתיקייה מתוך כונן נפרד, שיתוף NFS (רשת קבצים), או כונן מוצפן, המכולות עלולות לעלות לפני שהנתיב קיים. Docker ייצור בשמחה תיקייה ריקה בנקודת ה-mount ויפעיל את המכולה מולה, והתוצאה תהיה מסד נתונים שעולה ללא נתונים.

יש לכתוב יחידת systemd בכל אחד מהמקרים הבאים: ה-stack זקוק ל-mount, לממשק VPN, או ליחידה אחרת שתהיה מוכנה קודם לכן. אתה מעוניין ש-systemctl stop myapp ו-systemctl start myapp יפעלו כפי שהם פועלים עבור כל שירות אחר בשרת. לחלופין, אם ברצונך שה-stack ייסגר בצורה מסודרת בזמן כיבוי המערכת, במקום שייקטל יחד עם ה-daemon. אם יחידות systemd הן נושא חדש עבורך, המדריך כתיבת שירות וטיימר ב-systemd מפרט את מבנה הקבצים לעומק.

כתיבת יחידת systemd

מקמו את ה-stack בנתיב קבוע מחוץ לתיקיית הבית. /srv/myapp היא בחירה טובה, כיוון שליחידה שרצה לפני שמישהו מתחבר למערכת אין סיבה לקרוא מתוך /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

הפעילו והריצו אותה:

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

יחידה תקינה מציגה Active: active (exited). זה נראה שגוי בפעם הראשונה שרואים זאת. זה תקין: Type=oneshot עם RemainAfterExit=yes מציין שהיחידה הריצה את הפקודה שלה, הפקודה הסתיימה, ו-systemd שומרת על היחידה מסומנת כפעילה כדי ש-ExecStop ירוץ בעת כיבוי.

לכל שורה יש תפקיד. Requires=docker.service גורם ליחידה להיכשל במהירות במקום להריץ את docker compose מול socket מת. After= קובע את סדר הפעולות, כיוון ש-Requires= לבדו אינו עושה זאת. RequiresMountsFor= גורם ל-systemd למשוך את יחידת ה-mount עבור נתיב זה ולהמתין לה, וזו הסיבה העיקרית להשתמש ביחידה במקום במדיניות restart. TimeoutStartSec=0 מונע מ-systemd להרוג את משימת ההפעלה בזמן ש-image גדול עדיין נמצא בתהליך משיכה.

הערה על שילוב שני המנגנונים. התיעוד של Docker ממליץ לא לערבב מדיניות restart עם מנהל תהליכים של המערכת המארחת. האזהרה הזו מתייחסת למנהל תהליכים שמפקח על תהליך ה-container עצמו ומפעיל אותו מחדש בזמן שה-daemon מנסה לעשות את אותו הדבר. יחידת Type=oneshot אינה מפקחת על דבר, לכן השארת restart: unless-stopped בקובץ ה-compose לצד יחידה זו היא תקינה, וזהו המצב הרצוי. ה-systemd מטפל בסדר הפעולות בעת עליית המערכת, וה-daemon מטפל ב-container שקורס בשלוש לפנות בוקר.

היחידה נראית אחרת כאשר הדבר שאתם שומרים עליו פעיל הוא תהליך רגיל שרץ לאורך זמן ולא stack, כיוון שאז אין daemon מתחתיו וה-Restart= של systemd עצמו חייב לבצע את הפיקוח; הרצת dsh ללא ממשק מאחורי systemd היא דוגמה מעשית לצורה זו, כולל שימוש במשתמש ייעודי וב-journal.

אימות באמצעות אתחול מלא

אין תחליף לבדיקה אמיתית. 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 חסר. האתחול שאתם מתזמנים הוא זה שאתם מנטרים, לכן תנו ל-unit לדווח לכם על אלו שלא: שורת OnFailure= המופנית אל שרת ntfy בניהול עצמי הופכת stack שנכשל בעלייה להתראה בדחיפה, במקום תקלה שתגלו רק כעבור ימים.

גורמים להפסקת עלייה אוטומטית שקטה

מכולות שנוצרו באמצעות docker compose run לעולם לא יקבלו את מדיניות ה-restart המוגדרת בקובץ. Compose מתייחס אליהן כמכולות חד-פעמיות. אם נראה ששירות מתעלם מהמדיניות שלו, בדקו אם הוא הופעל באמצעות run במקום up.

נתיב יחסי ב-volume או בערך env_file מחושב ביחס לתיקייה שבה נמצא קובץ ה-compose. זה עובד מה-shell שלכם, וזה עובד מיחידת systemd שמגדירה WorkingDirectory. זה נכשל מיחידה ללא הגדרה כזו, כיוון שתיקיית העבודה היא אז /.

Docker במצב rootless הוא מקרה נפרד. ה-daemon רץ כשירות משתמש, ושירות משתמש נעצר כאשר הסשן האחרון של אותו משתמש מסתיים. הפעילו אותו עבור המשתמש ואפשרו לו להמשיך לרוץ גם כשאין אף משתמש מחובר:

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

ללא enable-linger, ה-daemon במצב rootless נכבה בעת התנתקות (logout) והמכולות נסגרות יחד איתו, מה שנראה בדיוק כמו מדיניות restart תקולה.

דבר אחרון. עדכוני אבטחה אוטומטיים יכולים לבצע reboot לשרת בשעה קבועה, וזה דבר חיובי רק אם ה-stack שלכם חוזר לעבוד מעצמו. הגדרת נושא זה במכונה חדשה היא חלק מהעבודה שיש לבצע בשעה הראשונה, כפי שמתואר ב-עשר הדקות הראשונות ב-VPS חדש.

FAQ

מה ההבדל בין restart: always לבין restart: unless-stopped?

שתי ההגדרות מפעילות מחדש את המכולה כאשר היא נעצרת מעצמה. ההבדל ביניהן בא לידי ביטוי לאחר עצירה ידנית של המכולה. עם always, המכולה תופעל מחדש בפעם הבאה שבה ה-daemon של Docker יעלה, כך שאתחול של השרת מבטל את העצירה הידנית שביצעת. עם 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.

האם אני זקוק ל-systemd unit אם אני כבר משתמש במדיניות הפעלה מחדש?

בדרך כלל לא. מדיניות הפעלה מחדש מספיקה עבור רוב ה-stacks, שזקוקים רק לרשת. הוסיפו unit כאשר המכולות תלויות ברכיב שאינו זמין בעת עליית ה-daemon של Docker, כגון כונן חיצוני, כרך מוצפן, שיתוף NFS או ממשק VPN. ה-unit מאפשר לכם לקבוע סדר עלייה באמצעות After= ו-RequiresMountsFor=, יכולת שאינה קיימת במדיניות הפעלה מחדש.

כיצד עוצרים stack לצמיתות מבלי שיחזור לאחר אתחול?

עם unless-stopped, הפקודה docker compose stop מספיקה, כיוון שמכולה שנעצרה ידנית אינה מופעלת מחדש כאשר ה-daemon עולה. עם always, עצירה אינה מספיקה והמכולה תחזור לאחר אתחול. עליכם להריץ את docker compose down, שמסיר את המכולות, או לשנות תחילה את המדיניות עם docker update --restart no my-container. אם ה-stack מנוהל על ידי systemd unit, הריצו גם את sudo systemctl disable myapp.service, אחרת ה-unit יפעיל אותו מחדש.